Chapter 21 - Standard C++ Language Additions

Chapter 21 - Standard C++ Language
Additions
Outline
21.1
21.2
21.3
21.4
21.5
21.6
21.7
21.8
21.9
21.10
21.11
21.12
Introduction
bool Data Type
static_cast Operator
const_cast Operator
reinterpret_cast Operator
namespaces
Run-Time Type Information (RTTI)
Operator Keywords
explicit Constructors
mutable Class Members
Pointers to Class Members (.* and ->*)
Multiple Inheritance and virtual Base Classes
 2000 Deitel & Associates, Inc. All rights reserved.
21.1 Introduction
• We shall cover standard C++ features
– data type bool
– cast operators
– namespaces
– run-time type information (RTTI)
– operator keywords
 2000 Deitel & Associates, Inc. All rights reserved.
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
 2000 Deitel & Associates, Inc. All rights reserved.
1
// Fig. 21.1: fig21_01.cpp
2
// Demonstrating data type bool.
3
#include <iostream>
Outline
1. Initialize bool
variable an int.
4
5
using std::cout;
6
using std::endl;
7
using std::cin;
8
using std::boolalpha;
2. Input a variable.
3. Print the bool value
of the int.
9
10 int main()
3.1 Print the value of
bool variable.
11 {
12
bool boolean = false;
13
int x = 0;
14
15
16
17
cout << "boolean is " << boolean
<< "\nEnter an integer: ";
boolean is 0
Enter an integer: 22
cin >> x;
18
19
cout << "integer " << x << " is"
20
<< ( x ? " nonzero " : " zero " )
21
<< "and interpreted as ";
22
 2000 Deitel & Associates, Inc. All rights reserved.
23
24
25
26
27
28
29
30
31
32
33
34 }
if ( x )
cout << "true\n"; integer 22 is nonzero and interpreted as true
else
cout << "false\n";
boolean
cout <<
cout <<
<<
= true;
"boolean is " << boolean;
"\nboolean output with boolalpha manipulator is "
boolalpha << boolean << endl;
return 0;
Outline
3.2 Print the string
value of the bool
variable.
boolean is 1
boolean output with boolalpha manipulator is
Notice how the
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
 2000 Deitel & Associates, Inc. All rights reserved.
output varies.
Program Output
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);
 2000 Deitel & Associates, Inc. All rights reserved.
1 // Fig. 21.2: fig21_02.cpp
2 // Demonstrating the static_cast operator.
3 #include <iostream>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Outline
1. Define class
BaseClass
using std::cout;
using std::endl;
class BaseClass {
public:
void f( void ) const { cout << "BASE\n"; }
1.1 Define member
function
};
1.2 Define class
DerivedClass
class DerivedClass : public BaseClass {
public:
void f( void ) const { cout << "DERIVED\n"; }
};
18 void test( BaseClass * );
19
20 int main()
convert
21 {
22
// use static_cast for a conversion
23
double d = 8.22;
1.3 Define member
function
double to int
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
 2000 delete
Deitel & Associates,
Inc. All rights reserved.
30
basePtr;
1.4 Global function
prototype
(calls function f)
2. Function calls
d is 8.22
x is 8
3. Output results
31
32
Outline
return 0;
33 }
3.1 Function definition
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 }
d is 8.22
x is 8
DERIVED
Program Output
converts a base pointer to a derived pointer,
and calls the derived function f
 2000 Deitel & Associates, Inc. All rights reserved.
1 // Fig. 21.2: fig21_02.cpp
2 // Demonstrating the static_cast operator.
3 #include <iostream>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using std::cout;
using std::endl;
In const function print the
this pointer is originally const
ConstCastTest *. It is cast
into type ConstCastTest *,
and can then be modified.
class BaseClass {
public:
void f( void ) const { cout << "BASE\n"; }
};
class DerivedClass : public BaseClass {
public:
void f( void ) const { cout << "DERIVED\n"; }
};
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
 2000 delete
Deitel & Associates,
Inc. All rights reserved.
30
basePtr;
Outline
1. Class definition
1.1 Initialize objects
2. Print
31
32
Outline
return 0;
33 }
34
35 void test( BaseClass * basePtr )
36 {
37
3. Function definition
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 }
d is 8.22
x is 8
DERIVED
 2000 Deitel & Associates, Inc. All rights reserved.
Program Output
21.4 const_cast Operator
• const_cast - cast away const or volatile
– cannot be used directly to cast away const-ness
• use pointers
 2000 Deitel & Associates, Inc. All rights reserved.
1
// Fig. 21.3: fig21_03.cpp
2
// Demonstrating the const_cast operator.
Outline
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
 2000 //
Deitel
& Associates,
Inc. All
rights reserved.
1. Define class
ConstCastTest
1.1 Define member
functions
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// undo const-ness to allow modification
const_cast< ConstCastTest * >( this )->number--;
Outline
cout << number << endl;
}
2. Create and initialize
object
Casts the this pointer into type ConstCastTest *.
This casts away
int main()
the "const-ness" and allows number to be modified.
{
ConstCastTest x;
3. Modify and print
x.setNumber( 8 ); // set private data number to 8
object with a const
function.
cout << "Initial value of number: " << x.getNumber();
x.printNumber();
return 0;
}
Initial value of number: 8
Number after modification: 7
 2000 Deitel & Associates, Inc. All rights reserved.
Program Output
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.).
 2000 Deitel & Associates, Inc. All rights reserved.
1
// Fig. 21.4: fig21_04.cpp
2
// Demonstrating the reinterpret_cast operator.
3
#include <iostream>
1. Initialize variables
and pointers
4
5
using std::cout;
6
using std::endl;
7
8
int main()
9
{
10
Outline
ptr (type int *) cast to a pointer of type (char *).
int x = 120, *ptr = &x;
2. Cast a pointer to
pointer of a different
type
3. Output data
11
12
cout << *reinterpret_cast<char *>( ptr ) << endl;
13
14
return 0;
15 }
Program Output
x
120 is the ASCII character code for 'x'
 2000 Deitel & Associates, Inc. All rights reserved.
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 the namespace do not need a prefix
– not guaranteed to be unique
– can be nested
 2000 Deitel & Associates, Inc. All rights reserved.
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
 2000 Deitel & Associates, Inc. All rights reserved.
1
// Fig. 21.5: fig21_05.cpp
2
// Demonstrating namespaces.
3
#include <iostream>
4
using namespace std;
// use std namespace
int myInt = 98;
// global variable
Outline
5
6
1.1 Declare global
variable
7
8
1. Use std namespace
namespace Example {
9
const double PI = 3.14159;
10
const double E = 2.71828;
11
int myInt = 8;
12
void printValues();
1.2 Define namespace
Example
13
14
namespace Inner {
15
16
// nested namespace
enum Years { FISCAL1 = 1990, FISCAL2, FISCAL3 };
}
17 }
18
19 namespace {
20
// unnamed namespace
Unnamed namespace members do not
need qualifiers
22
23 int main()
24 {
25
// output value d of unnamed namespace
26
cout << "d = " << d;
d = 88.22
(global) myInt = 98
// output global variable
 2000 cout
Deitel &
Inc. All rights
reserved.
29
<<Associates,
"\n(global)
myInt
= " << myInt;
28
1.4 Define unnamed
namespace
double d = 88.22;
21 }
27
1.3 Define namespace
inner
2. Print variables
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;
Example::printValues();
38
39
2. Print variables
3. Function definition
PI = 3.14159
36
37
Outline
E = 2.71828
// invoke printValues function
myInt
= 8
In printValues:
FISCAL3
myInt = =
8 1992
return 0;
PI = 3.14159
E = 2.71828
40 }
d = 88.22
41
(global) myInt = 98
42 void Example::printValues()
FISCAL3 = 1992
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 }
 2000 Deitel & Associates, Inc. All rights reserved.
Function printValues is a member
of Example and does not need a
namespace qualifier.
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
 2000 Deitel & Associates, Inc. All rights reserved.
Outline
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
 2000 Deitel & Associates, Inc. All rights reserved.
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
virtual double area() const { return 0.0; }
13 };
14
15 class Circle : public Shape {
16 public:
17
Circle( int r = 1 ) { radius = r; }
18
19
virtual double 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
virtual double area() const
32
{

2000
Deitel
& Associates,
Inc. *Allradius
rights reserved.
33
return
2 * PI
* height +
Outline
1. Define base and
derived classes
34
2 * Circle::area();
35
}
36 private:
37
int height;
38 };
39
1.1 Function prototype
40 void outputShapeArea( const Shape * );
// prototype
41
42 int main()
1.2 Declare objects
43 {
44
Circle circle;
45
Cylinder cylinder;
2. Function calls
46
Shape *ptr = 0;
47
3. Define function.
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 {
Notice how shapePtr is cast to
56
const Circle *circlePtr;
various types. If it is not of the
57
const Cylinder *cylinderPtr;
right type, the cast returns 0.
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
 2000 Deitel
Associates,
Inc. All rights
66
//& cast
shapePtr
to a reserved.
Circle *
Outline
67
circlePtr = dynamic_cast< const Circle * >( shapePtr );
68
69
if ( circlePtr != 0 )
70
cout << "Circle's area: " << circlePtr->area();
71
else
72
73
// if true, invoke area()
Notice how shapePtr
is cast to
Outline
various types. If it is not of the
right type,3.the
cast returns
0.
Define
function.
cout << "Neither a Circle nor a Cylinder.";
}
74
75
cout << endl;
76 }
Circle's area: 3.14159
Cylinder's area: 12.5664
Neither a Circle nor a Cylinder.
 2000 Deitel & Associates, Inc. All rights reserved.
Program Output
21.8 Operator Keywords
• can use keywords in place of operators (such as !,
&, ^, etc.).
– use header <iso646.h> (may vary with compiler)
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
 2000 Deitel & Associates, Inc. All rights reserved.
1
// Fig. 21.9: fig21_09.cpp
2
// Demonstrating operator keywords.
3
#include <iostream>
4
1. Load header
5
using std::cout;
6
using std::endl;
7
using std::boolalpha;
8
9
Outline
1.1 Initialize variables
Operator keywords can be used
instead of the symbols
#include <iso646.h>
10
11 int main()
2. Use operator
keywords
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;
 2000
29
} Deitel & Associates, Inc. All rights reserved.
3. Print results
Outline
a and b:
a or
not
a not_eq
a bitand
a bit_or
a xor
compl
a and_eq
a or_eq
a xor_eq
true
b: true
a: false
b: false
b: 22
b: 22
b: 0
a: -23
b: 22
b: 30
b: 30
 2000 Deitel & Associates, Inc. All rights reserved.
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 explicit prevents implicit conversion
– use before constructor prototype in class definition
 2000 Deitel & Associates, Inc. All rights reserved.
1
// Fig. 21.9: fig21_09.cpp
2
// Demonstrating operator keywords.
3
#include <iostream>
Outline
4
5
using std::cout;
6
using std::endl;
7
using std::boolalpha;
8
9
15 is not an Array object, but it is
implicitly converted to an Array object
using the conversion constructor. This new
object is printed using outputArray.
#include <iso646.h>
11 int main()
12 {
int a = 8, b = 22;
14
15
cout << boolalpha
"
If the keyword explicit
comes before the constructor,
this program will issue a
compiler error - outputArray
cannot take an int.
16
<<
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;
a and b: " << ( a and b )
27
28
1.1 Define constructor
1.2 Define destructor
10
13
1. Define Array class
return 0;
 2000
29
} Deitel & Associates, Inc. All rights reserved.
2. Create object
2.1 Print Array object
2.2 Print int
3. Function definition
a and b:
a or
not
a not_eq
a bitand
a bit_or
a xor
compl
a and_eq
a or_eq
a xor_eq
true
b: true
a: false
b: false
b: 22
b: 22
b: 0
a: -23
b: 22
b: 30
b: 30
 2000 Deitel & Associates, Inc. All rights reserved.
Outline
Program Output
1
// Fig 21.10: array2.h
2
// Simple class Array (for integers)
Outline
3 #ifndef ARRAY2_H
4
#define ARRAY2_H
5
6
1. Class definition
#include <iostream>
7
8
1.1 Function prototype
using std::ostream;
9
10 class Array {
11
friend ostream &operator<<( ostream &, const Array & );
1.2 Member variables
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
21 // Fig 21.10: array2.cpp
22 // Member function definitions for class Array
23 #include <iostream>
24
25 using std::cout;
26 using std::ostream;
27
28 #include <cassert>
29 #include "array2.h"
 2000 Deitel & Associates, Inc. All rights reserved.
30
---------------------1. Load header file
31 // Default constructor for class Array (default size 10)
Outline
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 = new int[ size ]; // create space for array
39
assert( ptr != 0 );
// terminate if memory not allocated
40
41
42
for ( int i = 0; i < size; i++ )
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
54
for ( i = 0; i < a.size; i++ )
output << a.ptr[ i ] << ' ' ;
55
56
return output;
// enables cout << x << y;
 2000
57
} Deitel & Associates, Inc. All rights reserved.
1.1 Function
definitions
58 // Fig 21.10: fig21_10.cpp
Outline
59 // Driver for simple class Array
60 #include <iostream>
61
1. Load header
62 using std::cout;
63
1.1 Initialize object
64 #include "array2.h"
65
66 void outputArray( const Array & );
2. Print object
67
3. Function definition
68 int main()
69 {
70
Array integers1( 7 );
71
72
outputArray( integers1 );
73
74
outputArray( 15 );
outputArray needs a parameter
of type const Array &, so 15
is converted into an Array by the
15 to an Array and output
conversion constructor.
// output Array integers1
// convert
75
76
return 0;
77 }
78
79 void outputArray( const Array &arrayToOutput )
80 {
81
82
cout << "The array received contains:\n"
<< arrayToOutput << "\n\n";
83
 2000
} Deitel & Associates, Inc. All rights reserved.
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
 2000 Deitel & Associates, Inc. All rights reserved.
Outline
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
 2000 Deitel & Associates, Inc. All rights reserved.
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)
 2000 Deitel & Associates, Inc. All rights reserved.
1
// Fig. 21.13 fig21_13.cpp
2
// Demonstrating operators .* and ->*
Outline
3 #include <iostream>
1. Class definition
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
} Deitel & Associates, Inc. All rights reserved.
 2000
1.1 Function
prototypes
1.2 Initialize object
2. Function calls
26
Outline
27 void arrowStar( Test *tPtr )
28 {
29
void ( Test::*memPtr )() = &Test::function;
30
( tPtr->*memPtr )();
3. Function definitions
// invoke function indirectly
31 }
arrowStar declares and initializes
memPtr to point to a function in Test that
takes no parameters and returns no value.
32
33 void dotStar( Test *tPtr )
34 {
35
int Test::*vPtr = &Test::value;
36
cout << ( *tPtr ).*vPtr << endl;
&Test::function to get the offset into
//the
access
class value
for member function function.
37 }
dotStar declares
initializes
Without and
Test::,
memPtr is a standard
vPtr to point
to value.
pointer.
function
8
The .* operator is then used to access
the member to which vPtr points.
 2000 Deitel & Associates, Inc. All rights reserved.
Program Output
21.12 Multiple Inheritance and virtual
Base Classes
• Ambiguities can result with multiple inheritance
ios
ostream
istream
iostream
• 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)
 2000 Deitel & Associates, Inc. All rights reserved.
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.
Base Class
virtual
inheritance
virtual
inheritance
First Derived Class
Second Derived Class
Multiply-Derived Class
 2000 Deitel & Associates, Inc. All rights reserved.
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
virtual void 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 };
 2000 Deitel & Associates, Inc. All rights reserved.
31
Outline
1. Define base class
1.1 Define nonvirtual derived
classes
1.2 Define multiply
derived class
32 int main()
33 {
34
Multiple both;
//
35
DerivedOne one;
//
36
DerivedTwo two;
//
37
38
Base *array[ 3 ];
39
array[ 0 ] = &both;
40
array[ 1 ] = &one;
41
array[ 2 ] = &two;
The address of both is implicitly
instantiate
Multiple
object
converted
to a base
class pointer. This is
instantiate
DerivedOne
ambiguous because object
class Multiple has
instantiate
DerivedTwo
duplicate
subobjectsobject
inherited from
Base.
42
// polymorphically invoke print
44
for ( int k = 0; k < 3; k++ )
45
array[ k ] -> print();
46
47
return 0;
48 }
2. Initialize array
elements
If DerivedOne and DerivedTwo
had used virtual inheritance, no
errors would result and the objects will
be printed.
Compiling...
Fig21_15.cpp
fig21_15.cpp(39) : error C2594: '=' : ambiguous conversions from
'class Multiple *' to 'class Base *'
 2000 Deitel & Associates, Inc. All rights reserved.
1.3 Create class
objects
1.4 Create an array of
base class pointers.
// ERROR--ambiguous
43
Outline
Program Output