550 likes | 562 Views
Chapter 22 - Other Topics. Outline 22.1 Introduction 22.2 const_cast Operator 22.3 reinterpret_cast Operator 22.4 namespaces 22.5 Operator Keywords 22.6 explicit Constructors 22.7 mutable Class Members 22.8 Pointers to Class Members (.* and ->*)
E N D
Chapter 22 - Other Topics Outline 22.1 Introduction 22.2 const_cast Operator 22.3 reinterpret_cast Operator 22.4 namespaces 22.5 Operator Keywords 22.6 explicit Constructors 22.7 mutable Class Members 22.8 Pointers to Class Members (.* and ->*) 22.9 Multiple Inheritance 22.10 Multiple Inheritance and virtual Base Classes
22.1 Introduction • Consider additional C++ features • Cast operators • Namespaces • Operator keywords • Multiple inheritence
22.2 const_cast Operator • const_cast operator • Used to cast away const or volatile • Get rid of a variable's "const-ness" • const_cast < new data type >
1 // Fig. 22.1: fig22_01.cpp 2 // Demonstrating operator const_cast. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 // class ConstCastTest definition 9 class ConstCastTest { 10 public: 11 void setNumber( int ); 12 int getNumber() const; 13 void printNumber() const; 14 private: 15 int number; 16 }; // end class ConstCastTest 17 18 // set number 19 void ConstCastTest::setNumber( int num ) { number = num; } 20 21 // return number 22 int ConstCastTest::getNumber() const { return number; } 23 Function is const, and cannot modify data. fig22_01.cpp(1 of 2)
24 // output number 25 void ConstCastTest::printNumber() const 26 { 27 cout << "\nNumber after modification: "; 28 29 // cast away const-ness to allow modification 30 const_cast< ConstCastTest * >( this )->number--; 31 32 cout << number << endl; 33 34 } // end printNumber 35 36 int main() 37 { 38 ConstCastTest test; // create ConstCastTest instance 39 40 test.setNumber( 8 ); // set private data number to 8 41 42 cout << "Initial value of number: " << test.getNumber(); 43 44 test.printNumber(); 45 return0; 46 47 } // end main Cast away the const-ness the this pointer. This allows the data to be modified. fig22_01.cpp(2 of 2)fig22_01.cppoutput (1 of 1) Initial value of number: 8 Number after modification: 7
22.3 reinterpret_cast Operator • reinterpret_cast • Used for nonstandard casts (i.e., one pointer to another) • int * to char * • Cannot be used for standard casts (i.e, double to int)
1 // Fig. 22.2: fig22_02.cpp 2 // Demonstrating operator reinterpret_cast. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 int main() 9 { 10 int x = 120; 11 int *ptr = &x; 12 13 // use reinterpret_cast to cast from int * to char * 14 cout << *reinterpret_cast< char * >( ptr ) << endl; 15 16 return0; 17 18 } // end main Create an int *. Cast it to a char * for printing. 120 is the ASCII value of 'x'. fig22_02.cpp(1 of 1)fig22_02.cppoutput (1 of 1) x
22.4 namespaces • Program has identifiers in different scopes • Sometimes scopes overlap, lead to problems • Namespace defines scope • Place identifiers and variables within namespace • Access with namespace_name::member • Note guaranteed to be unique namespace Name { contents } • Unnamed namespaces are global • Need no qualification • Namespaces can be nested
22.4 namespaces • using statement • using namespace namespace_name; • Members of that namespace can be used without preceding namespace_name:: • Can also be used with individual member • Examples • using namespace std • Discouraged by some programmers, because includes entire contents of std • using namespace std::cout • Can write cout instead of std::cout
1 // Fig. 22.3: fig22_03.cpp 2 // Demonstrating namespaces. 3 #include <iostream> 4 5 using namespace std; // use std namespace 6 7 int integer1 = 98; // global variable 8 9 // create namespace Example 10 namespace Example { 11 12 // declare two constants and one variable 13 const doublePI = 3.14159; 14 const doubleE = 2.71828; 15 int integer1 = 8; 16 17 void printValues(); // prototype 18 19 // nested namespace 20 namespace Inner { 21 22 // define enumeration 23 enum Years { FISCAL1 = 1990, FISCAL2, FISCAL3 }; 24 25 } // end Inner 26 27 } // end Example Note the using statement. This includes all of std, allowing us to use cout and endl. Create a new namespace, Example. Note that it has a variable integer1, different from the global integer1. Note the nested namespace Inner. fig22_03.cpp(1 of 3)
28 29 // create unnamed namespace 30 namespace { 31 double doubleInUnnamed = 88.22; // declare variable 32 33 } // end unnamed namespace 34 35 int main() 36 { 37 // output value doubleInUnnamed of unnamed namespace 38 cout << "doubleInUnnamed = " << doubleInUnnamed; 39 40 // output global variable 41 cout << "\n(global) integer1 = " << integer1; 42 43 // output values of Example namespace 44 cout << "\nPI = " << Example::PI << "\nE = " 45 << Example::E << "\ninteger1 = " 46 << Example::integer1 << "\nFISCAL3 = " 47 << Example::Inner::FISCAL3 << endl; 48 49 Example::printValues(); // invoke printValues function 50 51 return0; 52 53 } // end main Create an unnamed namespace. Its variables are global. fig22_03.cpp(2 of 3)
54 55 // display variable and constant values 56 void Example::printValues() 57 { 58 cout << "\nIn printValues:\ninteger1 = " 59 << integer1 << "\nPI = " << PI << "\nE = " 60 << E << "\ndoubleInUnnamed = " << doubleInUnnamed 61 << "\n(global) integer1 = " << ::integer1 62 << "\nFISCAL3 = " << Inner::FISCAL3 << endl; 63 64 } // end printValues fig22_03.cpp(3 of 3)
doubleInUnnamed = 88.22 (global) integer1 = 98 PI = 3.14159 E = 2.71828 integer1 = 8 FISCAL3 = 1992 In printValues: integer1 = 8 PI = 3.14159 E = 2.71828 doubleInUnnamed = 88.22 (global) integer1 = 98 FISCAL3 = 1992 fig22_03.cppoutput (1 of 1)
22.5 Operator Keywords • Operator keywords • Can be used instead of operators • Useful for keyboards without ^ | & etc.
1 // Fig. 22.5: fig22_05.cpp 2 // Demonstrating operator keywords. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 using std::boolalpha; 8 9 #include <iso646.h> 10 11 int main() 12 { 13 int a = 2; 14 int b = 3; 15 16 cout << boolalpha 17 << " a and b: " << ( a and b ) 18 << "\n a or b: " << ( a or b ) 19 << "\n not a: " << ( not a ) 20 << "\na not_eq b: " << ( a not_eq b ) 21 << "\na bitand b: " << ( a bitand b ) 22 << "\na bit_or b: " << ( a bitor b ) 23 << "\n a xor b: " << ( a xor b ) 24 << "\n compl a: " << ( compl a ) 25 << "\na and_eq b: " << ( a and_eq b ) 26 << "\n a or_eq b: " << ( a or_eq b ) 27 << "\na xor_eq b: " << ( a xor_eq b ) << endl; Note use of operator keywords. fig22_05.cpp(1 of 2)
28 29 return0; 30 31 } // end main fig22_05.cpp(2 of 2)fig22_05.cppoutput (1 of 1) a and b: true a or b: true not a: false a not_eq b: false a bitand b: 3 a bit_or b: 3 a xor b: 0 compl a: -4 a and_eq b: 3 a or_eq b: 3 a xor_eq b: 1
22.6 explicit Constructors • Implicit conversions • In Chapter 8, compiler may perform implicitly • If constructor exists • Suppose we have constructor • myClass( int x ) • Define function • myFunction( myClass y ) • Now, call myFunction( 3 ) • Compiler implicitly converts 3 to myClass, using the constructor • Then calls myFunction with the new object
22.6 explicit Constructors • May not have desired behavior • Declare constructor explicit • Cannot be used in implicit conversions • Example • First show Array class with implicit conversion • Then show Array class with explicit constructor
1 // Fig 22.6: array.h 2 // Simple class Array (for integers). 3 #ifndef ARRAY_H 4 #define ARRAY_H 5 6 #include <iostream> 7 8 using std::ostream; 9 10 // class Array definition 11 class Array { 12 friend ostream &operator<<( ostream &, const Array & ); 13 public: 14 Array( int = 10 ); // default/conversion constructor 15 ~Array(); // destructor 16 private: 17 int size; // size of the array 18 int *ptr; // pointer to first element of array 19 20 }; // end class Array 21 22 #endif// ARRAY_H Without the explicit keyword, this constructor can be used for implicit conversions. array.h (1 of 1)
1 // Fig 22.7: array.cpp 2 // Member function definitions for class Array. 3 #include <iostream> 4 5 using std::cout; 6 using std::ostream; 7 8 #include <new> 9 10 #include "array.h" 11 12 // default constructor for class Array (default size 10) 13 Array::Array( int arraySize ) 14 { 15 size = ( arraySize < 0 ? 10 : arraySize ); 16 cout << "Array constructor called for " 17 << size << " elements\n"; 18 19 // create space for array 20 ptr = newint[ size ]; 21 22 // initialize array elements to zeroes 23 for ( int i = 0; i < size; i++ ) 24 ptr[ i ] = 0; 25 26 } // end constructor array.cpp (1 of 2)
27 28 // destructor for class Array 29 Array::~Array() { delete [] ptr; } 30 31 // overloaded stream insertion operator for class Array 32 ostream &operator<<( ostream &output, const Array &arrayRef ) 33 { 34 for ( int i = 0; i < arrayRef.size; i++ ) 35 output << arrayRef.ptr[ i ] << ' ' ; 36 37 return output; // enables cout << x << y; 38 39 } // end operator<< array.cpp (2 of 2)
1 // Fig 22.8: fig22_08.cpp 2 // Driver for simple class Array. 3 #include <iostream> 4 5 using std::cout; 6 7 #include"array.h" 8 9 void outputArray( const Array & ); 10 11 int main() 12 { 13 Array integers1( 7 ); 14 15 outputArray( integers1 ); // output Array integers1 16 17 outputArray( 15 ); // convert 15 to an Array and output 18 19 return0; 20 21 } // end main Call outputArray and pass an int. This works because the int is implicitly converted to an Array by the constructor. fig22_08.cpp(1 of 2)
22 23 // print array contents 24 void outputArray( const Array &arrayToOutput ) 25 { 26 cout << "The array received contains:\n" 27 << arrayToOutput << "\n\n"; 28 29 } // end outputArray fig22_08.cpp(2 of 2)fig22_08.cppoutput (1 of 1) Array constructor called for 7 elements The array received contains: 0 0 0 0 0 0 0 Array constructor called for 15 elements The array received contains: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 // Fig. 22.9: array.h 2 // Simple class Array (for integers). 3 #ifndef ARRAY_H 4 #define ARRAY_H 5 6 #include <iostream> 7 8 using std::ostream; 9 10 // class Array definition 11 class Array { 12 friend ostream &operator<<( ostream &, const Array & ); 13 public: 14 explicit Array( int = 10 ); // default constructor 15 ~Array(); // destructor 16 private: 17 int size; // size of the array 18 int *ptr; // pointer to first element of array 19 20 }; // end class Array 21 22 #endif// ARRAY_H This time, declare constructor explicit. array.h (1 of 1)
1 // Fig. 22.10: array.cpp 2 // Member function definitions for class Array. 3 #include <iostream> 4 5 using std::cout; 6 using std::ostream; 7 8 #include <new> 9 10 #include"array.h" 11 12 // default constructor for class Array (default size 10) 13 Array::Array( int arraySize ) 14 { 15 size = ( arraySize < 0 ? 10 : arraySize ); 16 cout << "Array constructor called for " 17 << size << " elements\n"; 18 19 // create space for array 20 ptr = newint[ size ]; 21 22 // initialize array elements to zeroes 23 for ( int i = 0; i < size; i++ ) 24 ptr[ i ] = 0; 25 26 } // end constructor array.cpp (1 of 2)
27 28 // destructor for class Array 29 Array::~Array() { delete [] ptr; } 30 31 // overloaded insertion operator for class Array 32 ostream &operator<<( ostream &output, const Array &arrayRef ) 33 { 34 for ( int i = 0; i < arrayRef.size; i++ ) 35 output << arrayRef.ptr[ i ] << ' ' ; 36 37 return output; // enables cout << x << y; 38 39 } // end operator<< array.cpp (2 of 2)
1 // Fig. 22.11: fig22_11.cpp 2 // Driver for simple class Array. 3 #include <iostream> 4 5 using std::cout; 6 7 #include"array.h" 8 9 void outputArray( const Array & ); 10 11 int main() 12 { 13 Array integers1( 7 ); 14 15 outputArray( integers1 ); // output Array integers1 16 17 // ERROR: construction not allowed 18 outputArray( 15 ); // convert 15 to an Array and output 19 20 outputArray( Array( 15 ) ); // must use constructor 21 22 return0; 23 24 } // end main 25 This call will cause an error when compiled. fig22_11.cpp(1 of 2)
26 // display array contents 27 void outputArray( const Array &arrayToOutput ) 28 { 29 cout << "The array received contains:\n" 30 << arrayToOutput << "\n\n"; 31 32 } // end outputArray fig22_11.cpp(2 of 2)fig22_11.cppoutput (1 of 1) c:\cpp4e\ch22\FIG22_09_10_11\Fig22_11.cpp(18) : error C2664: 'outputArray' : cannot convert parameter 1 from 'const int' to 'const class Array &' Reason: cannot convert from 'const int' to 'const class Array' No constructor could take the source type, or constructor overload resolution was ambiguous Error executing cl.exe. test.exe - 1 error(s), 0 warning(s)
22.7 mutable Class Members • mutable data member • Always modifiable, even in a const function or object • Avoid need for const_cast • const_cast vs. mutable • For const object with no mutable data members • const_cast used every time • Reduces chance of accidental change
1 // Fig. 21.12: fig21_12.cpp 2 // Demonstrating storage class specifier mutable. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 // class TestMutable definition 9 class TestMutable { 10 public: 11 TestMutable( int v = 0 ) { value = v; } 12 void modifyValue() const { value++; } 13 int getValue() const { return value; } 14 private: 15 mutableint value; // mutable member 16 17 }; // end class TestMutable 18 19 int main() 20 { 21 const TestMutable test( 99 ); 22 23 cout << "Initial value: " << test.getValue(); 24 25 test.modifyValue(); // modifies mutable member 26 cout << "\nModified value: " << test.getValue() << endl; Declare a mutable int. It can be modified by const functions. fig21_12.cpp(1 of 2)
27 28 return0; 29 30 } // end main fig21_12.cpp(2 of 2)fig21_12.cppoutput (1 of 1) Initial value: 99 Modified value: 100
22.8 Pointers to Class Members (.* and ->*) • Use to access class members • Not the same as previously discussed pointers • Class function • void *memPtr () • Regular function pointer, to function that returns void and takes no arguments • void ( Test::*memPtr )() • Pointer to function in class Test • Function returns void, takes no arguments • To call function • Need pointer to Test object (tPtr) • (tPtr->*memPtr)()
22.8 Pointers to Class Members (.* and ->*) • Class data member • int *vPtr • Regular pointer to an int • int Test::*vPtr • Pointer to an int member of class Test • To access • Need pointer to object (tPtr) • (*tPtr).*vPtr
1 // Fig. 22.13 : fig22_13.cpp 2 // Demonstrating operators .* and ->*. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 // class Test definition 9 class Test { 10 public: 11 void function() { cout << "function\n"; } 12 int value; // public data member 13 }; // end class Test 14 15 void arrowStar( Test * ); 16 void dotStar( Test * ); 17 18 int main() 19 { 20 Test test; 21 22 test.value = 8; // assign value 8 23 arrowStar( &test ); // pass address to arrowStar 24 dotStar( &test ); // pass address to dotStar fig22_13.cpp(1 of 2)
25 26 return0; 27 28 } // end main 29 30 // access member function of Test object using ->* 31 void arrowStar( Test *testPtr ) 32 { 33 // declare function pointer 34 void ( Test::*memPtr )() = &Test::function; 35 36 // invoke function indirectly 37 ( testPtr->*memPtr )(); 38 39 } // end arrowStar 40 41 // access members of Test object data member using .* 42 void dotStar( Test *testPtr2 ) 43 { 44 int Test::*vPtr = &Test::value; // declare pointer 45 46 cout << ( *testPtr2 ).*vPtr << endl; // access value 47 48 } // end dotStar Assign function pointer to the address of function in Test. Note that neither side refers to a specific object. Next, call function directly. Create pointer to data member value. Then, access the data. fig22_13.cpp(2 of 2)
function 8 fig22_13.cppoutput (1 of 1)
22.9 Multiple Inheritance • Multiple inheritence • Derived class has several base classes • Powerful, but can cause ambiguity problems • If both base classes have functions of the same name • Solution: specify exact function using :: • myObject.BaseClass1::function() • Format • Use comma-separated list class Derived : public Base1, public Base2{ contents}
1 // Fig. 22.14: base1.h 2 // Definition of class Base1 3 #ifndef BASE1_H 4 #define BASE1_H 5 6 // class Base1 definition 7 class Base1 { 8 public: 9 Base1( int parameterValue ) { value = parameterValue; } 10 int getData() const { return value; } 11 12 protected: // accessible to derived classes 13 int value; // inherited by derived class 14 15 }; // end class Base1 16 17 #endif// BASE1_H There are two base classes in this example, each has its own getData function. This base class contains an int. base1.h (1 of 1)
1 // Fig. 22.15: base2.h 2 // Definition of class Base2 3 #ifndef BASE2_H 4 #define BASE2_H 5 6 // class Base2 definition 7 class Base2 { 8 public: 9 Base2( char characterData ) { letter = characterData; } 10 char getData() const { return letter; } 11 12 protected: // accessible to derived classes 13 char letter; // inherited by derived class 14 15 }; // end class Base2 16 17 #endif// BASE2_H base2.h (1 of 1)
1 // Fig. 22.16: derived.h 2 // Definition of class Derived which inherits 3 // multiple base classes (Base1 and Base2). 4 #ifndef DERIVED_H 5 #define DERIVED_H 6 7 #include <iostream> 8 9 using std::ostream; 10 11 #include"base1.h" 12 #include"base2.h" 13 14 // class Derived definition 15 class Derived : public Base1, public Base2 { 16 friend ostream &operator<<( ostream &, const Derived & ); 17 18 public: 19 Derived( int, char, double ); 20 double getReal() const; 21 22 private: 23 double real; // derived class's private data 24 25 }; // end class Derived 26 27 #endif// DERIVED_H Use comma-separated list. derived.h (1 of 1)
1 // Fig. 22.17: derived.cpp 2 // Member function definitions for class Derived 3 #include"derived.h" 4 5 // constructor for Derived calls constructors for 6 // class Base1 and class Base2. 7 // use member initializers to call base-class constructors 8 Derived::Derived( int integer, char character, double double1 ) 9 : Base1( integer ), Base2( character ), real( double1 ) { } 10 11 // return real 12 double Derived::getReal() const { return real; } 13 14 // display all data members of Derived 15 ostream &operator<<( ostream &output, const Derived &derived ) 16 { 17 output << " Integer: " << derived.value 18 << "\n Character: " << derived.letter 19 << "\nReal number: " << derived.real; 20 21 return output; // enables cascaded calls 22 23 } // end operator<< Note use of base-class constructors in derived class constructor. derived.cpp (1 of 1)
1 // Fig. 22.18: fig22_18.cpp 2 // Driver for multiple inheritance example. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 #include"base1.h" 9 #include"base2.h" 10 #include"derived.h" 11 12 int main() 13 { 14 Base1 base1( 10 ), *base1Ptr = 0; // create Base1 object 15 Base2 base2( 'Z' ), *base2Ptr = 0; // create Base2 object 16 Derived derived( 7, 'A', 3.5 ); // create Derived object 17 18 // print data members of base-class objects 19 cout << "Object base1 contains integer " 20 << base1.getData() 21 << "\nObject base2 contains character " 22 << base2.getData() 23 << "\nObject derived contains:\n" << derived << "\n\n"; 24 fig22_18.cpp(1 of 2)
25 // print data members of derived-class object 26 // scope resolution operator resolves getData ambiguity 27 cout << "Data members of Derived can be" 28 << " accessed individually:" 29 << "\n Integer: " << derived.Base1::getData() 30 << "\n Character: " << derived.Base2::getData() 31 << "\nReal number: " << derived.getReal() << "\n\n"; 32 33 cout << "Derived can be treated as an " 34 << "object of either base class:\n"; 35 36 // treat Derived as a Base1 object 37 base1Ptr = &derived; 38 cout << "base1Ptr->getData() yields " 39 << base1Ptr->getData() << '\n'; 40 41 // treat Derived as a Base2 object 42 base2Ptr = &derived; 43 cout << "base2Ptr->getData() yields " 44 << base2Ptr->getData() << endl; 45 46 return0; 47 48 } // end main Note calls to specific base class functions. Can treat derived-class pointer as either base-class pointer. fig22_18.cpp(2 of 2)
Object base1 contains integer 10 Object base2 contains character Z Object derived contains: Integer: 7 Character: A Real number: 3.5 Data members of Derived can be accessed individually: Integer: 7 Character: A Real number: 3.5 Derived can be treated as an object of either base class: base1Ptr->getData() yields 7 base2Ptr->getData() yields A fig22_18.cppoutput (1 of 1)
ios ostream istream iostream 22.10 Multiple Inheritance and virtual Base Classes • Ambiguities from multiple inheritance • iostream could have duplicate subobjects • Data from ios inherited into ostream and istream • Upcasting iostream pointer to ios object is a problem • Two ios subobjects could exist, which is used? • Ambiguous, results in syntax error • iostream does not actually have this problem
Base Class virtual inheritance virtual inheritance First Derived Class Second Derived Class Multiply-Derived Class 22.10 Multiple Inheritance and virtual Base Classes • Solution: use virtual base class inheritance • Only one subobject inherited into multiply derived class
1 // Fig. 22.20: fig22_20.cpp 2 // Attempting to polymorphically call a function that is 3 // multiply inherited from two base classes. 4 #include <iostream> 5 6 using std::cout; 7 using std::endl; 8 9 // class Base definition 10 class Base { 11 public: 12 virtualvoid print() const = 0; // pure virtual 13 14 }; // end class Base 15 16 // class DerivedOne definition 17 class DerivedOne : public Base { 18 public: 19 20 // override print function 21 void print() const { cout << "DerivedOne\n"; } 22 23 }; // end class DerivedOne 24 This example will demonstrate the ambiguity of multiple inheritance. fig22_20.cpp(1 of 3)
25 // class DerivedTwo definition 26 class DerivedTwo : public Base { 27 public: 28 29 // override print function 30 void print() const { cout << "DerivedTwo\n"; } 31 32 }; // end class DerivedTwo 33 34 // class Multiple definition 35 class Multiple : public DerivedOne, public DerivedTwo { 36 public: 37 38 // qualify which version of function print 39 void print() const { DerivedTwo::print(); } 40 41 }; // end class Multiple 42 fig22_20.cpp(2 of 3)
43 int main() 44 { 45 Multiple both; // instantiate Multiple object 46 DerivedOne one; // instantiate DerivedOne object 47 DerivedTwo two; // instantiate DerivedTwo object 48 49 // create array of base-class pointers 50 Base *array[ 3 ]; 51 52 array[ 0 ] = &both; // ERROR--ambiguous 53 array[ 1 ] = &one; 54 array[ 2 ] = &two; 55 56 // polymorphically invoke print 57 for ( int i = 0; i < 3; i++ ) 58 array[ i ] -> print(); 59 60 return0; 61 62 } // end main Which base subobject will be used? fig22_20.cpp(3 of 3)