Main Page

Previous Page
Next Page

[Page 1223 (continued)]


  • C++ provides the const_cast operator for casting away const or volatile qualification.

  • A program declares a variable with the volatile qualifier when that program expects the variable to be modified by other programs. Declaring a variable volatile indicates that the compiler should not optimize the use of that variable because doing so could affect the ability of those other programs to access and modify the volatile variable.

  • In general, it is dangerous to use the const_cast operator, because it allows a program to modify a variable that was declared const, and thus was not supposed to be modifiable.

  • There are cases in which it is desirable, or even necessary, to cast away const-ness. For example, older C and C++ libraries might provide functions with non-const parameters and that do not modify their parameters. If you wish to pass const data to such a function, you would need to cast away the data's const-ness; otherwise, the compiler would report error messages.

  • If you pass non-const data to a function that treats the data as if it were constant, then returns that data as a constant, you might need to cast away the const-ness of the returned data to access and modify that data.

  • A program includes many identifiers defined in different scopes. Sometimes a variable of one scope will "overlap" with a variable of the same name in a different scope, possibly creating a naming conflict. The C++ standard attempts to solve this problem with namespaces.

  • Each namespace defines a scope in which identifiers are placed. To use a namespace member, either the member's name must be qualified with the namespace name and the binary scope resolution operator (::) or a using directive or declaration must appear before the name is used in the program.

  • Typically, using statements are placed at the beginning of the file in which members of the namespace are used.

  • Not all namespaces are guaranteed to be unique. Two third-party vendors might inadvertently use the same identifiers for their namespace names.

  • A using namespace directive specifies that the members of a namespace will be used frequently throughout a program. This allows the programmer to access all the members of the namespace.

  • [Page 1224]
  • A using namespace directive can be used for predefined namespaces (e.g., std) or programmer-defined namespaces.

  • A namespace can contain constants, data, classes, nested namespaces, functions, etc. Definitions of namespaces must occupy the global scope or be nested within other namespaces.

  • An unnamed namespace has an implicit using directive, so its members appear to occupy the global namespace, are accessible directly and do not have to be qualified with a namespace name. Global variables are also part of the global namespace.

  • When accessing members of a nested namespace, the members must be qualified with the namespace name (unless the member is being used inside the nested namespace).

  • Namespaces can be aliased.

  • The C++ standard provides operator keywords that can be used in place of several C++ operators. Operator keywords are useful for programmers who have keyboards that do not support certain characters such as !, &, ^, ~, |, etc.

  • If a data member should always be modifiable, C++ provides the storage-class specifier mutable as an alternative to const_cast. A mutable data member is always modifiable, even in a const member function or const object. This reduces the need to cast away "const-ness."

  • Both mutable and const_cast allow a data member to be modified; they are used in different contexts. For a const object with no mutable data members, operator const_cast must be used every time a member is to be modified. This greatly reduces the chance of a member being accidentally modified because the member is not permanently modifiable.

  • Operations involving const_cast are typically hidden in a member function's implementation. The user of a class might not be aware that a member is being modified.

  • C++ provides the .* and ->* operators for accessing class members via pointers. This is rarely used capability that is used primarily by advanced C++ programmers.

  • Declaring a pointer to a function requires that you enclose the pointer name preceded by an * in parentheses. A pointer to a function must specify, as part of its type, both the return type of the function it points to and the parameter list of that function.

  • In C++, a class may be derived from more than one base classa technique known as multiple inheritance, in which a derived class inherits the members of two or more base classes.

  • A common problem with multiple inheritance is that each of the base classes might contain data members or member functions that have the same name. This can lead to ambiguity problems when you attempt to compile.

  • The is-a relationships of single inheritance also apply in multiple-inheritance relationships.

  • Multiple inheritance is used, for example, in the C++ Standard Library to form class basic_iostream. Class basic_ios is the base class for both basic_istream and basic_ostream, each of which is formed with single inheritance. Class basic_iostream inherits from both basic_istream and basic_ostream. This enables objects of class basic_iostream to provide the functionality of both basic_istreams and basic_ostreams. In multiple-inheritance hierarchies, the situation described here is referred to as diamond inheritance.

  • Because classes basic_istream and basic_ostream each inherit from basic_ios, a potential problem exists for basic_iostream. If not implemented correctly, class basic_iostream could contain two copies of the members of class basic_iosone inherited via class basic_istream and one inherited via class basic_ostream). Such a situation would be ambiguous and would result in a compilation error, because the compiler would not know which version of the members from class basic_ios to use.

  • The ambiguity in diamond inheritance occurs when a derived-class object inherits two or more base-class subobjects. The problem of duplicate subobjects is resolved with virtual inheritance. When a base class is inherited as virtual, only one subobject will appear in the derived classa process called virtual base-class inheritance.

    [Page 1225]
  • Implementing hierarchies with virtual base classes is simpler if default constructors are used for the base classes. If a virtual base class provides a constructor that requires arguments, the implementation of the derived classes becomes more complicated, because the most derived class must explicitly invoke the virtual base class's constructor to initialize the members inherited from the virtual base class.

Previous Page
Next Page