022 Other Topics

Post on 18-Feb-2016

216 views 1 download

description

programming

transcript

2003 Prentice Hall, Inc. All rights reserved.

1

Chapter 22 - Other Topics

Outline22.1 Introduction22.2 const_cast Operator22.3 reinterpret_cast Operator22.4 namespaces22.5 Operator Keywords22.6 explicit Constructors22.7 mutable Class Members22.8 Pointers to Class Members (.* and ->*)22.9 Multiple Inheritance22.10 Multiple Inheritance and virtual Base Classes

2003 Prentice Hall, Inc. All rights reserved.

2

22.1 Introduction

• Consider additional C++ features– Cast operators– Namespaces– Operator keywords– Multiple inheritence

2003 Prentice Hall, Inc. All rights reserved.

3

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 >

2003 Prentice Hall, Inc.All rights reserved.

Outline4

fig22_01.cpp(1 of 2)

1 // Fig. 22.1: fig22_01.cpp2 // Demonstrating operator const_cast. 3 #include <iostream>4 5 using std::cout;6 using std::endl;7 8 // class ConstCastTest definition9 class ConstCastTest {10 public:11 void setNumber( int );12 int getNumber() const;13 void printNumber() const;14 private:15 int number;16 }; // end class ConstCastTest17 18 // set number19 void ConstCastTest::setNumber( int num ) { number = num; }20 21 // return number22 int ConstCastTest::getNumber() const { return number; }23

Function is const, and cannot modify data.

2003 Prentice Hall, Inc.All rights reserved.

Outline5

fig22_01.cpp(2 of 2)

fig22_01.cppoutput (1 of 1)

24 // output number25 void ConstCastTest::printNumber() const26 {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 printNumber35 36 int main()37 {38 ConstCastTest test; // create ConstCastTest instance39 40 test.setNumber( 8 ); // set private data number to 841 42 cout << "Initial value of number: " << test.getNumber();43 44 test.printNumber();45 return 0;46 47 } // end main

Initial value of number: 8Number after modification: 7

Cast away the const-ness the this pointer. This allows the data to be modified.

2003 Prentice Hall, Inc. All rights reserved.

6

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)

2003 Prentice Hall, Inc.All rights reserved.

Outline7

fig22_02.cpp(1 of 1)

fig22_02.cppoutput (1 of 1)

1 // Fig. 22.2: fig22_02.cpp2 // 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 return 0;17 18 } // end main

x

Create an int *. Cast it to a char * for printing. 120 is the ASCII value of 'x'.

2003 Prentice Hall, Inc. All rights reserved.

8

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

2003 Prentice Hall, Inc. All rights reserved.

9

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

2003 Prentice Hall, Inc.All rights reserved.

Outline10

fig22_03.cpp(1 of 3)

1 // Fig. 22.3: fig22_03.cpp2 // Demonstrating namespaces.3 #include <iostream>4 5 using namespace std; // use std namespace 6 7 int integer1 = 98; // global variable8 9 // create namespace Example 10 namespace Example { 11 12 // declare two constants and one variable 13 const double PI = 3.14159; 14 const double E = 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.

Note the nested namespace Inner.

Create a new namespace, Example. Note that it has a variable integer1, different from the global integer1.

2003 Prentice Hall, Inc.All rights reserved.

Outline11

fig22_03.cpp(2 of 3)

28 29 // create unnamed namespace 30 namespace { 31 double doubleInUnnamed = 88.22; // declare variable32 33 } // end unnamed namespace 34 35 int main()36 {37 // output value doubleInUnnamed of unnamed namespace38 cout << "doubleInUnnamed = " << doubleInUnnamed;39 40 // output global variable41 cout << "\n(global) integer1 = " << integer1;42 43 // output values of Example namespace44 cout << "\nPI = " << Example::PI << "\nE = "45 << Example::E << "\ninteger1 = " 46 << Example::integer1 << "\nFISCAL3 = "47 << Example::Inner::FISCAL3 << endl;48 49 Example::printValues(); // invoke printValues function50 51 return 0;52 53 } // end main

Create an unnamed namespace. Its variables are global.

2003 Prentice Hall, Inc.All rights reserved.

Outline12

fig22_03.cpp(3 of 3)

54 55 // display variable and constant values56 void Example::printValues() 57 {58 cout << "\nIn printValues:\ninteger1 = "59 << integer1 << "\nPI = " << PI << "\nE = "60 << E << "\ndoubleInUnnamed = " << doubleInUnnamed 61 << "\n(global) integer1 = " << ::integer162 << "\nFISCAL3 = " << Inner::FISCAL3 << endl;63 64 } // end printValues

2003 Prentice Hall, Inc.All rights reserved.

Outline13

fig22_03.cppoutput (1 of 1)

doubleInUnnamed = 88.22(global) integer1 = 98PI = 3.14159E = 2.71828integer1 = 8FISCAL3 = 1992 In printValues:integer1 = 8PI = 3.14159E = 2.71828doubleInUnnamed = 88.22(global) integer1 = 98FISCAL3 = 1992

2003 Prentice Hall, Inc. All rights reserved.

14

22.5 Operator Keywords

• Operator keywords– Can be used instead of operators– Useful for keyboards without ^ | & etc.

2003 Prentice Hall, Inc. All rights reserved.

15

22.5 Operator Keywords

Operator Operator keyword Description Logical operator keywords

&& and logical AND || or logical OR ! not logical NOT Inequality operator keyword

!= not_eq Inequality Bitwise operator keywords

& bitand Bitwise AND | bitor Bitwise inclusive OR ^ xor Bitwise exclusive OR ~ compl Bitwise complement Bitwise assignment operator keywords

&= and_eq Bitwise AND assignment |= or_eq Bitwise inclusive OR

assignment ^= xor_eq Bitwise exclusive OR

assignment

2003 Prentice Hall, Inc.All rights reserved.

Outline16

fig22_05.cpp(1 of 2)

1 // Fig. 22.5: fig22_05.cpp2 // 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.

2003 Prentice Hall, Inc.All rights reserved.

Outline17

fig22_05.cpp(2 of 2)

fig22_05.cppoutput (1 of 1)

28 29 return 0;30 31 } // end main

a and b: true a or b: true not a: falsea not_eq b: falsea bitand b: 3a bit_or b: 3 a xor b: 0 compl a: -4a and_eq b: 3 a or_eq b: 3a xor_eq b: 1

2003 Prentice Hall, Inc. All rights reserved.

18

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

2003 Prentice Hall, Inc. All rights reserved.

19

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

2003 Prentice Hall, Inc.All rights reserved.

Outline20

array.h (1 of 1)

1 // Fig 22.6: array.h2 // Simple class Array (for integers).3 #ifndef ARRAY_H4 #define ARRAY_H5 6 #include <iostream>7 8 using std::ostream;9 10 // class Array definition11 class Array {12 friend ostream &operator<<( ostream &, const Array & );13 public:14 Array( int = 10 ); // default/conversion constructor15 ~Array(); // destructor16 private:17 int size; // size of the array18 int *ptr; // pointer to first element of array19 20 }; // end class Array21 22 #endif // ARRAY_H

Without the explicit keyword, this constructor can be used for implicit conversions.

2003 Prentice Hall, Inc.All rights reserved.

Outline21

array.cpp (1 of 2)

1 // Fig 22.7: array.cpp2 // 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 array20 ptr = new int[ size ]; 21 22 // initialize array elements to zeroes23 for ( int i = 0; i < size; i++ )24 ptr[ i ] = 0; 25 26 } // end constructor

2003 Prentice Hall, Inc.All rights reserved.

Outline22

array.cpp (2 of 2)

27 28 // destructor for class Array29 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<<

2003 Prentice Hall, Inc.All rights reserved.

Outline23

fig22_08.cpp(1 of 2)

1 // Fig 22.8: fig22_08.cpp2 // 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 integers116 17 outputArray( 15 ); // convert 15 to an Array and output 18 19 return 0;20 21 } // end main

Call outputArray and pass an int. This works because the int is implicitly converted to an Array by the constructor.

2003 Prentice Hall, Inc.All rights reserved.

Outline24

fig22_08.cpp(2 of 2)

fig22_08.cppoutput (1 of 1)

22 23 // print array contents24 void outputArray( const Array &arrayToOutput )25 {26 cout << "The array received contains:\n" 27 << arrayToOutput << "\n\n";28 29 } // end outputArray

Array constructor called for 7 elementsThe array received contains:0 0 0 0 0 0 0 Array constructor called for 15 elementsThe array received contains:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

2003 Prentice Hall, Inc.All rights reserved.

Outline25

array.h (1 of 1)

1 // Fig. 22.9: array.h2 // Simple class Array (for integers).3 #ifndef ARRAY_H4 #define ARRAY_H5 6 #include <iostream>7 8 using std::ostream;9 10 // class Array definition11 class Array {12 friend ostream &operator<<( ostream &, const Array & );13 public:14 explicit Array( int = 10 ); // default constructor15 ~Array(); // destructor16 private:17 int size; // size of the array18 int *ptr; // pointer to first element of array19 20 }; // end class Array21 22 #endif // ARRAY_H

This time, declare constructor explicit.

2003 Prentice Hall, Inc.All rights reserved.

Outline26

array.cpp (1 of 2)

1 // Fig. 22.10: array.cpp2 // 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 array20 ptr = new int[ size ];21 22 // initialize array elements to zeroes23 for ( int i = 0; i < size; i++ )24 ptr[ i ] = 0;25 26 } // end constructor

2003 Prentice Hall, Inc.All rights reserved.

Outline27

array.cpp (2 of 2)

27 28 // destructor for class Array29 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<<

2003 Prentice Hall, Inc.All rights reserved.

Outline28

fig22_11.cpp(1 of 2)

1 // Fig. 22.11: fig22_11.cpp2 // 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 integers116 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 return 0;23 24 } // end main25

This call will cause an error when compiled.

2003 Prentice Hall, Inc.All rights reserved.

Outline29

fig22_11.cpp(2 of 2)

fig22_11.cppoutput (1 of 1)

26 // display array contents27 void outputArray( const Array &arrayToOutput )28 {29 cout << "The array received contains:\n" 30 << arrayToOutput << "\n\n";31 32 } // end outputArray

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 ambiguousError executing cl.exe. test.exe - 1 error(s), 0 warning(s)

2003 Prentice Hall, Inc. All rights reserved.

30

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

2003 Prentice Hall, Inc.All rights reserved.

Outline31

fig21_12.cpp(1 of 2)

1 // Fig. 21.12: fig21_12.cpp2 // Demonstrating storage class specifier mutable.3 #include <iostream>4 5 using std::cout;6 using std::endl;7 8 // class TestMutable definition9 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 mutable int value; // mutable member 16 17 }; // end class TestMutable18 19 int main()20 {21 const TestMutable test( 99 );22 23 cout << "Initial value: " << test.getValue();24 25 test.modifyValue(); // modifies mutable member26 cout << "\nModified value: " << test.getValue() << endl;

Declare a mutable int. It can be modified by const functions.

2003 Prentice Hall, Inc.All rights reserved.

Outline32

fig21_12.cpp(2 of 2)

fig21_12.cppoutput (1 of 1)

27 28 return 0;29 30 } // end main

Initial value: 99Modified value: 100

2003 Prentice Hall, Inc. All rights reserved.

33

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)()

2003 Prentice Hall, Inc. All rights reserved.

34

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

2003 Prentice Hall, Inc.All rights reserved.

Outline35

fig22_13.cpp(1 of 2)

1 // Fig. 22.13 : fig22_13.cpp2 // Demonstrating operators .* and ->*.3 #include <iostream>4 5 using std::cout;6 using std::endl;7 8 // class Test definition9 class Test {10 public:11 void function() { cout << "function\n"; }12 int value; // public data member13 }; // end class Test14 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 arrowStar24 dotStar( &test ); // pass address to dotStar

2003 Prentice Hall, Inc.All rights reserved.

Outline36

fig22_13.cpp(2 of 2)

25 26 return 0;27 28 } // end main29 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 indirectly37 ( testPtr->*memPtr )(); 38 39 } // end arrowStar40 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 value47 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.

2003 Prentice Hall, Inc.All rights reserved.

Outline37

fig22_13.cppoutput (1 of 1)

function8

2003 Prentice Hall, Inc. All rights reserved.

38

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}

2003 Prentice Hall, Inc.All rights reserved.

Outline39

base1.h (1 of 1)

1 // Fig. 22.14: base1.h2 // Definition of class Base13 #ifndef BASE1_H4 #define BASE1_H5 6 // class Base1 definition7 class Base1 {8 public:9 Base1( int parameterValue ) { value = parameterValue; }10 int getData() const { return value; }11 12 protected: // accessible to derived classes13 int value; // inherited by derived class14 15 }; // end class Base116 17 #endif // BASE1_H

There are two base classes in this example, each has its own getData function.

This base class contains an int.

2003 Prentice Hall, Inc.All rights reserved.

Outline40

base2.h (1 of 1)

1 // Fig. 22.15: base2.h2 // Definition of class Base23 #ifndef BASE2_H4 #define BASE2_H5 6 // class Base2 definition7 class Base2 {8 public:9 Base2( char characterData ) { letter = characterData; }10 char getData() const { return letter; }11 12 protected: // accessible to derived classes13 char letter; // inherited by derived class14 15 }; // end class Base216 17 #endif // BASE2_H

2003 Prentice Hall, Inc.All rights reserved.

Outline41

derived.h (1 of 1)

1 // Fig. 22.16: derived.h2 // Definition of class Derived which inherits3 // multiple base classes (Base1 and Base2).4 #ifndef DERIVED_H5 #define DERIVED_H6 7 #include <iostream>8 9 using std::ostream;10 11 #include "base1.h"12 #include "base2.h"13 14 // class Derived definition15 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 data24 25 }; // end class Derived26 27 #endif // DERIVED_H

Use comma-separated list.

2003 Prentice Hall, Inc.All rights reserved.

Outline42

derived.cpp (1 of 1)

1 // Fig. 22.17: derived.cpp2 // Member function definitions for class Derived3 #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 real12 double Derived::getReal() const { return real; }13 14 // display all data members of Derived15 ostream &operator<<( ostream &output, const Derived &derived )16 {17 output << " Integer: " << derived.value 18 << "\n Character: " << derived.letter19 << "\nReal number: " << derived.real;20 21 return output; // enables cascaded calls22 23 } // end operator<<

Note use of base-class constructors in derived class constructor.

2003 Prentice Hall, Inc.All rights reserved.

Outline43

fig22_18.cpp(1 of 2)

1 // Fig. 22.18: fig22_18.cpp2 // 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 object15 Base2 base2( 'Z' ), *base2Ptr = 0; // create Base2 object16 Derived derived( 7, 'A', 3.5 ); // create Derived object17 18 // print data members of base-class objects19 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

2003 Prentice Hall, Inc.All rights reserved.

Outline44

fig22_18.cpp(2 of 2)

25 // print data members of derived-class object26 // scope resolution operator resolves getData ambiguity27 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 object37 base1Ptr = &derived; 38 cout << "base1Ptr->getData() yields "39 << base1Ptr->getData() << '\n';40 41 // treat Derived as a Base2 object42 base2Ptr = &derived; 43 cout << "base2Ptr->getData() yields "44 << base2Ptr->getData() << endl;45 46 return 0;47 48 } // end main

Note calls to specific base class functions.

Can treat derived-class pointer as either base-class pointer.

2003 Prentice Hall, Inc.All rights reserved.

Outline45

fig22_18.cppoutput (1 of 1)

Object base1 contains integer 10Object base2 contains character ZObject derived contains: Integer: 7 Character: AReal number: 3.5 Data members of Derived can be accessed individually: Integer: 7 Character: AReal number: 3.5 Derived can be treated as an object of either base class:base1Ptr->getData() yields 7base2Ptr->getData() yields A

2003 Prentice Hall, Inc. All rights reserved.

46

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

ios

ostreamistream

iostream

2003 Prentice Hall, Inc. All rights reserved.

47

22.10 Multiple Inheritance and virtual Base Classes

• Solution: use virtual base class inheritance– Only one subobject inherited into multiply derived class

Second Derived Class

Base Class

First Derived Class

Multiply-Derived Class

virtual inheritance

virtual inheritance

2003 Prentice Hall, Inc.All rights reserved.

Outline48

fig22_20.cpp(1 of 3)

1 // Fig. 22.20: fig22_20.cpp2 // Attempting to polymorphically call a function that is3 // multiply inherited from two base classes.4 #include <iostream>5 6 using std::cout;7 using std::endl;8 9 // class Base definition10 class Base {11 public: 12 virtual void print() const = 0; // pure virtual13 14 }; // end class Base15 16 // class DerivedOne definition17 class DerivedOne : public Base {18 public:19 20 // override print function 21 void print() const { cout << "DerivedOne\n"; } 22 23 }; // end class DerivedOne24

This example will demonstrate the ambiguity of multiple inheritance.

2003 Prentice Hall, Inc.All rights reserved.

Outline49

fig22_20.cpp(2 of 3)

25 // class DerivedTwo definition26 class DerivedTwo : public Base {27 public:28 29 // override print function 30 void print() const { cout << "DerivedTwo\n"; }31 32 }; // end class DerivedTwo33 34 // class Multiple definition35 class Multiple : public DerivedOne, public DerivedTwo {36 public:37 38 // qualify which version of function print39 void print() const { DerivedTwo::print(); }40 41 }; // end class Multiple42

2003 Prentice Hall, Inc.All rights reserved.

Outline50

fig22_20.cpp(3 of 3)

43 int main()44 {45 Multiple both; // instantiate Multiple object46 DerivedOne one; // instantiate DerivedOne object47 DerivedTwo two; // instantiate DerivedTwo object48 49 // create array of base-class pointers50 Base *array[ 3 ];51 52 array[ 0 ] = &both; // ERROR--ambiguous 53 array[ 1 ] = &one;54 array[ 2 ] = &two;55 56 // polymorphically invoke print57 for ( int i = 0; i < 3; i++ )58 array[ i ] -> print();59 60 return 0;61 62 } // end main

Which base subobject will be used?

2003 Prentice Hall, Inc.All rights reserved.

Outline51

fig22_20.cppoutput (1 of 1)

c:\cpp4e\ch22\fig22_20_21\fig22_20.cpp(52) : error C2594: '=' : ambiguous conversions from 'class Multiple *' to 'class Base *'Error executing cl.exe. test.exe - 1 error(s), 0 warning(s)

2003 Prentice Hall, Inc.All rights reserved.

Outline52

fig22_21.cpp(1 of 3)

1 // Fig. 22.21: fig22_21.cpp2 // Using virtual base classes.3 #include <iostream>4 5 using std::cout;6 using std::endl;7 8 // class Base definition9 class Base {10 public:11 12 // implicit default constructor13 14 virtual void print() const = 0; // pure virtual15 16 }; // end Base class17 18 // class DerivedOne definition19 class DerivedOne : virtual public Base {20 public:21 22 // implicit default constructor calls23 // Base default constructor 24 25 // override print function 26 void print() const { cout << "DerivedOne\n"; }27 28 }; // end DerivedOne class

Use virtual inheritance to solve the ambiguity problem.

The compiler generates default constructors, which greatly simplifies the hierarchy.

2003 Prentice Hall, Inc.All rights reserved.

Outline53

fig22_21.cpp(2 of 3)

29 30 // class DerivedTwo definition31 class DerivedTwo : virtual public Base {32 public:33 34 // implicit default constructor calls35 // Base default constructor 36 37 // override print function 38 void print() const { cout << "DerivedTwo\n"; }39 40 }; // end DerivedTwo class41 42 // class Multiple definition43 class Multiple : public DerivedOne, public DerivedTwo {44 public:45 46 // implicit default constructor calls47 // DerivedOne and DerivedTwo default constructors48 49 // qualify which version of function print 50 void print() const { DerivedTwo::print(); } 51 52 }; // end Multiple class

Use virtual inheritance, as before.

2003 Prentice Hall, Inc.All rights reserved.

Outline54

fig22_21.cpp(3 of 3)

53 54 int main()55 {56 Multiple both; // instantiate Multiple object57 DerivedOne one; // instantiate DerivedOne object58 DerivedTwo two; // instantiate DerivedTwo object59 60 // declare array of base-class pointers and initialize61 // each element to a derived-class type62 Base *array[ 3 ];63 64 array[ 0 ] = &both; 65 array[ 1 ] = &one;66 array[ 2 ] = &two;67 68 // polymorphically invoke function print69 for ( int i = 0; i < 3; i++ )70 array[ i ]->print();71 72 return 0;73 74 } // end main

2003 Prentice Hall, Inc.All rights reserved.

Outline55

fig22_21.cppoutput (1 of 1)

DerivedTwoDerivedOneDerivedTwo