420 likes | 586 Views
Chapter 21 - Standard C++ Language Additions. Outline 21.1 Introduction 21.2 bool Data Type 21.3 static_cast Operator 21.4 const_cast Operator 21.5 reinterpret_cast Operator 21.6 namespaces 21.7 Run-Time Type Information (RTTI) 21.8 Operator Keywords
E N D
Chapter 21 - Standard C++ Language Additions Outline 21.1 Introduction 21.2 bool Data Type 21.3 static_cast Operator 21.4 const_cast Operator 21.5 reinterpret_cast Operator 21.6 namespaces 21.7 Run-Time Type Information (RTTI) 21.8 Operator Keywords 21.9 explicit Constructors 21.10 mutable Class Members 21.11 Pointers to Class Members (.* and ->*) 21.12 Multiple Inheritance and virtual Base Classes
21.1 Introduction • We shall cover standard C++ features • data type bool • cast operators • namespaces • run-time type information (RTTI) • operator keywords
21.2 bool Data Type • bool - can be false or true • preferable to 0 (false) and non-zero (true) • outputting bool variables • numerical default (0 or 1) • stream manipulator boolalpha • outputs string "true" or "false" Examples: cout << boolVariable cout << boolalpha << boolVariable
1 // Fig. 21.1: fig21_01.cpp 2 // Demonstrating data type bool. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 using std::cin; 8 using std::boolalpha; 9 10 int main() 11 { 12 bool boolean = false; 13 int x = 0; 14 15 cout << "boolean is " << boolean 16 << "\nEnter an integer: "; 17 cin >> x; 18 19 cout << "integer " << x << " is" 20 << ( x ? " nonzero " : " zero " ) 21 << "and interpreted as "; 22 1. Initialize bool variable an int. 2. Input a variable. 3. Print the bool value of the int. 3.1 Print the value of bool variable. boolean is 0 Enter an integer: 22
23 if ( x ) 24 cout << "true\n"; Notice how the output varies. 25 else 26 cout << "false\n"; 27 28 boolean = true; 29 cout << "boolean is " << boolean; 30 cout << "\nboolean output with boolalpha manipulator is " 31 << boolalpha << boolean << endl; 32 33 return 0; 34 } integer 22 is nonzero and interpreted as true 3.2 Print the string value of the bool variable. Program Output boolean is 1 boolean output with boolalpha manipulator is true boolean is 0 Enter an integer: 22 integer 22 is nonzero and interpreted as true boolean is 1 boolean output with boolalpha manipulator is true
21.3 static_cast Operator • C++ has 4 separate, specific casts • static_cast - conversion between types • type checking at compile time • standard conversions: void* to char*, int to float, etc. • base class pointers to derived class pointers • Format: static_cast<type to convert to>(object to convert) int z = 3; float x = static_cast<int>(z);
1 // Fig. 21.2: fig21_02.cpp 2 // Demonstrating the static_cast operator. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 class BaseClass { convert double to int 9 public: 10 void f( void ) const { cout << "BASE\n"; } 11 }; 12 13 class DerivedClass : public BaseClass { 14 public: 15 void f( void ) const { cout << "DERIVED\n"; } 16 }; 17 18 void test( BaseClass * ); 19 20 int main() 21 { 22 // use static_cast for a conversion 23 double d = 8.22; 24 int x = static_cast< int >( d ); 25 26 cout << "d is " << d << "\nx is " << x << endl; 27 28 BaseClass * basePtr = new DerivedClass; 29 test( basePtr ); // call test 30 delete basePtr; 1. Define class BaseClass 1.1 Define member function 1.2 Define class DerivedClass 1.3 Define member function 1.4 Global function prototype (calls function f) 2. Function calls 3. Output results d is 8.22 x is 8
31 32 return 0; 33 } 34 35 void test( BaseClass * basePtr ) 36 { 37 DerivedClass *derivedPtr; 38 converts a base pointer to a derived pointer, and calls the derived function f 39 // cast base class pointer into derived class pointer 40 derivedPtr = static_cast< DerivedClass * >( basePtr ); 41 derivedPtr->f(); // invoke DerivedClass function f 42 } 3.1 Function definition Program Output d is 8.22 x is 8 DERIVED
1 // Fig. 21.2: fig21_02.cpp 2 // Demonstrating the static_cast operator. In constfunction print the this pointer is originally const ConstCastTest *.It is cast into type ConstCastTest *, and can then be modified. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 class BaseClass { 9 public: 10 void f( void ) const { cout << "BASE\n"; } 11 }; 12 13 class DerivedClass : public BaseClass { 14 public: 15 void f( void ) const { cout << "DERIVED\n"; } 16 }; 17 18 void test( BaseClass * ); 19 20 int main() 21 { 22 // use static_cast for a conversion 23 double d = 8.22; 24 int x = static_cast< int >( d ); 25 26 cout << "d is " << d << "\nx is " << x << endl; 27 28 BaseClass * basePtr = new DerivedClass; 29 test( basePtr ); // call test 30 delete basePtr; 1. Class definition 1.1 Initialize objects 2. Print
31 32 return 0; 33 } 34 35 void test( BaseClass * basePtr ) 36 { 37 DerivedClass *derivedPtr; 38 39 // cast base class pointer into derived class pointer 40 derivedPtr = static_cast< DerivedClass * >( basePtr ); 41 derivedPtr->f(); // invoke DerivedClass function f 42 } 3. Function definition Program Output d is 8.22 x is 8 DERIVED
21.4 const_cast Operator • const_cast - cast away const or volatile • cannot be used directly to cast away const-ness • use pointers
1 // Fig. 21.3: fig21_03.cpp 2 // Demonstrating the const_cast operator. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 class ConstCastTest { 9 public: 10 void setNumber( int ); 11 int getNumber() const; 12 void printNumber() const; 13 private: 14 int number; 15 }; 16 17 void ConstCastTest::setNumber( int num ) { number = num; } 18 19 int ConstCastTest::getNumber() const { return number; } 20 21 void ConstCastTest::printNumber() const 22 { 23 cout << "\nNumber after modification: "; 24 25 // the expression number-- would generate compile error 1. Define class ConstCastTest 1.1 Define member functions
26 // undo const-ness to allow modification 27 const_cast< ConstCastTest * >( this )->number--; Casts the this pointer into type ConstCastTest *. This casts away the "const-ness" and allows number to be modified. 28 29 cout << number << endl; 30 } 31 32 int main() 33 { 34 ConstCastTest x; 35 x.setNumber( 8 ); // set private data number to 8 36 37 cout << "Initial value of number: " << x.getNumber(); 38 39 x.printNumber(); 40 return 0; 41 } 2. Create and initialize object 3. Modify and print object with a const function. Program Output Initial value of number: 8 Number after modification: 7
21.5 reinterpret_cast Operator • reinterpret_cast - for nonstandard casts • one pointer type to another pointer type, void* to int, etc. • cannot be used for standard casts (int to double, etc.).
1 // Fig. 21.4: fig21_04.cpp 2 // Demonstrating the reinterpret_cast operator. 3 #include <iostream> 4 ptr (type int *) cast to a pointer of type (char *). 5 using std::cout; 6 using std::endl; 7 8 int main() 9 { 10 int x = 120, *ptr = &x; 120 is the ASCII character code for 'x' 11 12 cout << *reinterpret_cast<char *>( ptr ) << endl; 13 14 return 0; 15 } 1. Initialize variables and pointers 2. Cast a pointer to pointer of a different type 3. Output data Program Output x
21.6 namespaces • variables with same name and different scopes can overlap • need to distinguish them • a namespace defines a scope for local and global identifiers. • body delimited by braces {} • use (::) to access namespace members: namespace_name::member • or, a using statement must occur before name is used using namespace namespace_name; -members of thenamespace do not need a prefix • not guaranteed to be unique • can be nested
21.6 namespaces (II) • Unnamed namespaces • occupy global namespace • directly accessible • do not need namespace name • global variables are in global namespace • accessible in all scopes
1 // Fig. 21.5: fig21_05.cpp 2 // Demonstrating namespaces. 3 #include <iostream> 4 usingnamespace std; // use std namespace 5 6 int myInt = 98; // global variable 7 8 namespace Example { Unnamed namespace members do not need qualifiers 9 constdouble PI = 3.14159; 10 constdouble E = 2.71828; 11 int myInt = 8; 12 void printValues(); 13 14 namespace Inner { // nested namespace 15 enum Years { FISCAL1 = 1990, FISCAL2, FISCAL3 }; 16 } 17 } 18 19 namespace { // unnamed namespace 20 double d = 88.22; 21 } 22 23 int main() 24 { 25 // output value d of unnamed namespace 26 cout << "d = " << d; 27 28 // output global variable 29 cout << "\n(global) myInt = " << myInt; 1. Use std namespace 1.1 Declare global variable 1.2 Define namespace Example 1.3 Define namespace inner 1.4 Define unnamed namespace 2. Print variables d = 88.22 (global) myInt = 98
31 // output values of Example namespace 32 cout << "\nPI = " << Example::PI << "\nE = " 33 << Example::E << "\nmyInt = " 34 << Example::myInt << "\nFISCAL3 = " 35 << Example::Inner::FISCAL3 << endl; 36 37 Example::printValues(); // invoke printValues function 38 39 return 0; Function printValues is a member of Example and does not need a namespace qualifier. 40 } 41 42 void Example::printValues() 43 { 44 cout << "\nIn printValues:\n" << "myInt = " 45 << myInt << "\nPI = " << PI << "\nE = " 46 << E << "\nd = " << d << "\n(global) myInt = " 47 << ::myInt << "\nFISCAL3 = " 48 << Inner::FISCAL3 << endl; 49 } 2. Print variables 3. Function definition PI = 3.14159 E = 2.71828 myInt = 8 FISCAL3 = 1992 In printValues: myInt = 8 PI = 3.14159 E = 2.71828 d = 88.22 (global) myInt = 98 FISCAL3 = 1992
d = 88.22 (global) myInt = 98 PI = 3.14159 E = 2.71828 myInt = 8 FISCAL3 = 1992 In printValues: myInt = 8 PI = 3.14159 E = 2.71828 d = 88.22 (global) myInt = 98 FISCAL3 = 1992 Program Output
21.7 Run-Time Type Information (RTTI) • determines an object's type at run time • typeid (in <typeinfo>) typeid(object).name() - returns the name of the object as a C-style string • dynamic_cast - for polymorphic programming • often used to downcast base-class pointer to derived-class pointer • used with virtual functions
1 // Fig. 21.7: fig21_07.cpp 2 // Demonstrating dynamic_cast. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 const double PI = 3.14159; 9 10 class Shape { 11 public: 12 virtualdouble area() const { return 0.0; } 13 }; 14 15 class Circle : public Shape { 16 public: 17 Circle( int r = 1 ) { radius = r; } 18 19 virtualdouble area() const 20 { 21 return PI * radius * radius; 22 }; 23 protected: 24 int radius; 25 }; 26 27 class Cylinder : public Circle { 28 public: 29 Cylinder( int h = 1 ) { height = h; } 30 31 virtualdouble area() const 32 { 33 return 2 * PI * radius * height + 1. Define base and derived classes
34 2 * Circle::area(); 35 } 36 private: Notice how shapePtris cast to various types. If it is not of the right type, the cast returns 0. 37 int height; 38 }; 39 40 void outputShapeArea( const Shape * ); // prototype 41 42 int main() 43 { 44 Circle circle; 45 Cylinder cylinder; 46 Shape *ptr = 0; 47 48 outputShapeArea( &circle ); // output circle's area 49 outputShapeArea( &cylinder ); // output cylinder's area 50 outputShapeArea( ptr ); // attempt to output area 51 return 0; 52 } 53 54 void outputShapeArea( const Shape *shapePtr ) 55 { 56 const Circle *circlePtr; 57 const Cylinder *cylinderPtr; 58 59 // cast Shape * to a Cylinder * 60 cylinderPtr = dynamic_cast< const Cylinder * >( shapePtr ); 61 62 if ( cylinderPtr != 0 ) // if true, invoke area() 63 cout << "Cylinder's area: " << shapePtr->area(); 64 else { // shapePtr does not refer to a cylinder 65 66 // cast shapePtr to a Circle * 1.1 Function prototype 1.2 Declare objects 2. Function calls 3. Define function.
67 circlePtr = dynamic_cast< const Circle * >( shapePtr ); 68 69 if ( circlePtr != 0 ) // if true, invoke area() 70 cout << "Circle's area: " << circlePtr->area(); 71 else 72 cout << "Neither a Circle nor a Cylinder."; 73 } 74 Notice how shapePtris cast to various types. If it is not of the right type, the cast returns 0. 75 cout << endl; 76 } 3. Define function. Program Output Circle's area: 3.14159 Cylinder's area: 12.5664 Neither a Circle nor a Cylinder.
21.8 Operator Keywords • can use keywords in place of operators (such as !, &, ^, etc.). • use header <iso646.h> (may vary with compiler)
1 // Fig. 21.9: fig21_09.cpp 2 // Demonstrating operator keywords. 3 #include <iostream> Operator keywords can be used instead of the symbols 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 = 8, b = 22; 14 15 cout << boolalpha 16 << " a and b: " << ( a and b ) 17 << "\n a or b: " << ( a or b ) 18 << "\n not a: " << ( not a ) 19 << "\na not_eq b: " << ( a not_eq b ) 20 << "\na bitand b: " << ( a bitand b ) 21 << "\na bit_or b: " << ( a bitor b ) 22 << "\n a xor b: " << ( a xor b ) 23 << "\n compl a: " << ( compl a ) 24 << "\na and_eq b: " << ( a and_eq b ) 25 << "\n a or_eq b: " << ( a or_eq b ) 26 << "\na xor_eq b: " << ( a xor_eq b ) << endl; 27 28 return 0; 29 } 1. Load header 1.1 Initialize variables 2. Use operator keywords 3. Print results
a and b: true a or b: true not a: false a not_eq b: false a bitand b: 22 a bit_or b: 22 a xor b: 0 compl a: -23 a and_eq b: 22 a or_eq b: 30 a xor_eq b: 30 Program Output
21.9 explicit Constructors • constructors with one argument can be used for implicit conversion • type received by constructor turned into an object • automatic conversion sometimes undesirable • keyword explicitprevents implicit conversion • use before constructor prototype in class definition
1 // Fig. 21.9: fig21_09.cpp 2 // Demonstrating operator keywords. 15is not anArrayobject, but it is implicitly converted to anArray object using the conversion constructor. This new object is printed using outputArray. 3 #include <iostream> 4 If the keyword explicitcomes before the constructor, this program will issue a compiler error - outputArraycannot take an int. 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 = 8, b = 22; 14 15 cout << boolalpha 16 << " a and b: " << ( a and b ) 17 << "\n a or b: " << ( a or b ) 18 << "\n not a: " << ( not a ) 19 << "\na not_eq b: " << ( a not_eq b ) 20 << "\na bitand b: " << ( a bitand b ) 21 << "\na bit_or b: " << ( a bitor b ) 22 << "\n a xor b: " << ( a xor b ) 23 << "\n compl a: " << ( compl a ) 24 << "\na and_eq b: " << ( a and_eq b ) 25 << "\n a or_eq b: " << ( a or_eq b ) 26 << "\na xor_eq b: " << ( a xor_eq b ) << endl; 27 28 return 0; 29 } 1. Define Array class 1.1 Define constructor 1.2 Define destructor 2. Create object 2.1 Print Array object 2.2 Print int 3. Function definition
a and b: true a or b: true not a: false a not_eq b: false a bitand b: 22 a bit_or b: 22 a xor b: 0 compl a: -23 a and_eq b: 22 a or_eq b: 30 a xor_eq b: 30 Program Output
1 // Fig 21.10: array2.h 21 // Fig 21.10: array2.cpp 2 // Simple class Array (for integers) 22 // Member function definitions for class Array 23 #include <iostream> 3 #ifndef ARRAY2_H 4 #define ARRAY2_H 24 5 25 using std::cout; 6 #include <iostream> 26 using std::ostream; 27 7 8 using std::ostream; 28 #include <cassert> 29 #include "array2.h" 9 30 10 class Array { 11 friend ostream &operator<<( ostream &, const Array & ); 12 public: 13 Array( int = 10 ); // default/conversion constructor 14 ~Array(); // destructor 15 private: 16 int size; // size of the array 17 int *ptr; // pointer to first element of array 18 }; 19 20 #endif 1. Class definition 1.1 Function prototype 1.2 Member variables ---------------------- 1. Load header file
31 // Default constructor for class Array (default size 10) 32 Array::Array( int arraySize ) 33 { 34 size = ( arraySize > 0 ? arraySize : 10 ); 35 cout << "Array constructor called for " 36 << size << " elements\n"; 37 38 ptr = newint[ size ]; // create space for array 39 assert( ptr != 0 ); // terminate if memory not allocated 40 41 for ( int i = 0; i < size; i++ ) 42 ptr[ i ] = 0; // initialize array 43 } 44 45 // Destructor for class Array 46 Array::~Array() { delete [] ptr; } 47 48 // Overloaded output operator for class Array 49 ostream &operator<<( ostream &output, const Array &a ) 50 { 51 int i; 52 53 for ( i = 0; i < a.size; i++ ) 54 output << a.ptr[ i ] << ' ' ; 55 56 return output; // enables cout << x << y; 57 } 1.1 Function definitions
58 // Fig 21.10: fig21_10.cpp 59 // Driver for simple class Array 60 #include <iostream> 61 62 using std::cout; 63 outputArray needs a parameter of type const Array &, so 15 is converted into an Array by the conversion constructor. 64 #include "array2.h" 65 66 void outputArray( const Array & ); 67 68 int main() 69 { 70 Array integers1( 7 ); 71 72 outputArray( integers1 ); // output Array integers1 73 74 outputArray( 15 ); // convert 15 to an Array and output 75 76 return 0; 77 } 78 79 void outputArray( const Array &arrayToOutput ) 80 { 81 cout << "The array received contains:\n" 82 << arrayToOutput << "\n\n"; 83 } 1. Load header 1.1 Initialize object 2. Print object 3. Function definition
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 Program Output
21.10 mutable Class Members • mutable data member • always modifiable, even in a const function or object • permanently allows a const data member to be modified • const_cast: • used every time a const data member must be modified • reduces risk of accidentally modifying a const variable
21.11 Pointers to Class Members (.* and ->*) • pointers to class members are different from normal pointers • use .* and ->* instead of . and -> when accessing class members (functions and data)
1 // Fig. 21.13 fig21_13.cpp 2 // Demonstrating operators .* and ->* 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 class Test { 9 public: 10 void function() { cout << "function\n"; } 11 int value; 12 }; 13 14 void arrowStar( Test * ); 15 void dotStar( Test * ); 16 17 int main() 18 { 19 Test t; 20 21 t.value = 8; 22 arrowStar( &t ); 23 dotStar( &t ); 24 return 0; 25 } 1. Class definition 1.1 Function prototypes 1.2 Initialize object 2. Function calls
26 27 void arrowStar( Test *tPtr ) 28 { arrowStardeclares and initializes memPtr to point to a function in Test that takes no parameters and returns no value. &Test::function to get the offset into the class for member function function. Without Test::, memPtr is a standard pointer. 29 void ( Test::*memPtr )() = &Test::function; 30 ( tPtr->*memPtr )(); // invoke function indirectly 31 } dotStardeclares and initializes vPtr to point to value. The .* operator is then used to access the member to which vPtr points. 32 33 void dotStar( Test *tPtr ) 34 { 35 int Test::*vPtr = &Test::value; 36 cout << ( *tPtr ).*vPtr << endl; // access value 37 } 3. Function definitions Program Output function 8
21.12 Multiple Inheritance and virtual Base Classes • Ambiguities can result with multiple inheritance • iostream could have duplicate subobjects (data from ios inherited into ostream and istream). • Upcasting an iostream pointer to an ios object creates a problem. Two ios subobjects could exist: which one is used? • Ambiguous, results in syntax error (of course, iostream does not actually have this problem) ios ostream istream iostream
Base Class virtual inheritance virtual inheritance First Derived Class Second Derived Class Multiply-Derived Class 21.12 Multiple Inheritance and virtual Base Classes (II) • Use virtual base class inheritance • only one subobject of the base is inherited into the multiply derived class.
1 // Fig. 21.15: fig21_15.cpp 2 // Attempting to polymorphically call a function 3 // multiply inherited from two base classes. 4 #include <iostream> 5 6 using std::cout; 7 using std::endl; 8 9 class Base { 10 public: 11 virtualvoid print() const = 0; // pure virtual 12 }; 13 14 class DerivedOne : public Base { 15 public: 16 // override print function 17 void print() const { cout << "DerivedOne\n"; } 18 }; 19 20 class DerivedTwo : public Base { 21 public: 22 // override print function 23 void print() const { cout << "DerivedTwo\n"; } 24 }; 25 26 class Multiple : public DerivedOne, public DerivedTwo { 27 public: 28 // qualify which version of function print 29 void print() const { DerivedTwo::print(); } 30 }; 31 1. Define base class 1.1 Define non-virtual derived classes 1.2 Define multiply derived class
32 int main() The address of both is implicitly converted to a base class pointer. This is ambiguous because class Multiple has duplicate subobjects inherited from Base. 33 { 34 Multiple both; // instantiate Multiple object 35 DerivedOne one; // instantiate DerivedOne object 36 DerivedTwo two; // instantiate DerivedTwo object If DerivedOneandDerivedTwo had used virtualinheritance, no errors would result and the objects will be printed. 37 38 Base *array[ 3 ]; 39 array[ 0 ] = &both; // ERROR--ambiguous 40 array[ 1 ] = &one; 41 array[ 2 ] = &two; 42 43 // polymorphically invoke print 44 for ( int k = 0; k < 3; k++ ) 45 array[ k ] -> print(); 46 47 return 0; 48 } 1.3 Create class objects 1.4 Create an array of base class pointers. 2. Initialize array elements Program Output Compiling... Fig21_15.cpp fig21_15.cpp(39) : error C2594: '=' : ambiguous conversions from 'class Multiple *' to 'class Base *'