Você está na página 1de 48

Programming Language Concepts Using C and C++/Object Orientation a...

1 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

Programming Language Concepts Using C and C++/Object


Orientation and Inheritance in C++
Logic of inheritance does not change much from one programming language to another. For instance, if base and derived classes
share the same public interface, the derived class is said to be a subtype of its base class and instances of it can be treated as
instances of the base class. Or, thanks to dynamic dispatch, inheritance can be used to provide polymorphism.
However, newcomers to C++ from Java, or any other object-oriented programming language for that matter, are in for a few
surprises. What we will do in this chapter is to take a look at inheritance in C++ and underline the differences with other
programming languages.

Contents
1 Inheritance Peculiarities in C++
1.1 Inheritance Kinds
1.2 No Member Initialization
1.3 Default Dispatch Type is Static Dispatch
1.4 Hide-by-name Overloading
1.5 Multiple Inheritance, No Root Class
1.6 Test Program
2 Inheritance a la Java
2.1 Root Class and the Interface Concept
2.2 Module
2.2.1 Interface (Rational)
2.2.2 Implementation (Rational)
2.2.3 Interface (Whole)
2.2.4 Implementation (Whole)
2.3 Exception Classes
2.4 Test Program
3 Private Inheritance
3.1 Interface (List)
3.2 Implementation (List)
3.3 Interface (Stack)
3.4 Implementation (Stack)
4 Virtual Inheritance
5 Implementing Polymorphism
5.1 vtables
5.2 Adjustor Thunks
5.3 Complications due to Multiple Inheritance
6 Constructor and Destructor Call Order
6.1 An Object with Primitive Type Fields
6.2 An Object Composed of Sub-object(s)
6.3 Creating an Array of Objects
6.4 Inheritance
6.5 Member Initialization List
6.6 Multiple Inheritance
6.7 Restating the Formula: Inheritance is Compiler-Managed Composition
6.8 Virtual Inheritance
7 Notes

Inheritance Peculiarities in C++


Inheritance Kinds
First peculiarity of C++ is the assortment of inheritance kinds it offers to programmers: public, protected, and private. This meets
the newcomer with the first inheritance example she tries. Take a look at the following code snippet.
class D : B { ... }

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

2 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

This innocent-looking code claims to derive D from B. However, it will not let you treat an object of D as an object of B. Thats
right: D is not seen as a subtype of B. It looks like either C++ or the programmer got it wrong. Not exactly! Similar to the default
section being private, inheritance, unless otherwise stated, is taken to be of the so-called private kind. Deferring the answer of
what we mean by private inheritance to another section, we modify the above code to meet our expectations as given below.
class D : public B { ... }
Done with this peculiarity, lets move on to see some code examples. We will be using the following class definitions throughout
the examples.
class B {
public:
B(void) { }
void f1s(void) { cout << "In B::f1s(void)" << endl; }
virtual void f1d(void) { cout << "In B::f1d(void)" << endl; }
virtual void f2(int i) { cout << "In B::f2(int)" << endl; }
...
}; // end of class B
class D : public B {
public:
D(void) { }
void f1s(void) { cout << "In D::f1s(void)" << endl; }
virtual void f1d(void) { cout << "In D::f1d(void)" << endl; }
virtual void f2(short s) { cout << "In D::f2(short)" << endl; }
...
int _m_i;
short _m_s;
}; // end of class D

No Member Initialization
...
D* d = new D();
cout << d->_m_i << " " << d->_m_s << endl;
...
As a Java programmer you might think the above code fragment should produce two 0's in succession. However, it will output
two random values. Unlike Java and C# where, unless overridden, data members are provided with default initial values, C++
compiler does not initialize the data members. If they need to be initialized to 0 or to any other value, this must be done explicitly
by the programmer. It should also be noted that one cannot declare a data member with an initial value. That is, changing int
_m_i; to int _m_i = 0; in D will give rise to a syntactic error.
D() : _m_i(0), _m_s(0) { } or D() { _m_i = 0; _m_s = 0; }
The C++ compiler has full confidence in the programmer. After all, a C++ programmer does not make a mistake. An error prone
statement, which may be seen as the begetter of a hideous mistake in Java, is assumed to be the conscious decision of an
all-knowing programmer. Its not a bug its a feature!

Default Dispatch Type is Static Dispatch


...
B* b; // the static type of b is B*
if (bool_expr) b = new D(); // if this branch is taken bs dynamic type will be D*
else b = new B(); // if control falls through to this limp, dynamic type of b will
be B*
b->f1s();
...
As a competent Java programmer you would expect this to producedepending on the value bool_expr evaluates to"In
B::f1s(void)" or "In D::f1s(void)". However, in C++ it always outputs "In B::f1s(void)"!!! Unlike Java,
C++, unless otherwise told, uses static dispatch in binding function calls. This means the address of the function invoked by the
call to f1s will be resolved statically. That is, the compiler will use the static type of the identifier. In other words, functions
invoked as a result of the calls made can be figured out by checking the program text.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

3 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

...
B* b;
if (bool_expr) b = new D();
else b = new B();
b->f1d();
...
A virtual function is dispatched dynamically. So, unlike the previous one, this example will compile and yield an output
depending on the value of bool_expr. If it evaluates to true it will output "In D::f1d(void)", otherwise it will output "In
B::f1d(void)".

Hide-by-name Overloading
...
D* d = new D();
int i = 3;
d->f2(i); // will be dispatched to D::f2(short)
...
With Java semantics, above code outputs "In B::f2(int)". After all, d is also an object of type B and can use the public interface
of B just like a genuine B object. So, both D::f2(short) and B::f2(int) are exposed to clients of d. Not in C++! Unlike
Java, where base and derived class member functions make up a set of overloaded functions, C++ restricts this set to a single
scope. Since derived and base classes are different scopes, any derived class function with a name coinciding with a base class
function will shadow all the functions in the base class. Technically, we say C++ hides by name while Java is said to hide by
signature.
But doesnt it go against the logic of inheritance? You claim d to be an object of B (through the public inheritance relationship)
and dont let its clients use some function appearing in the public interface of B? Thats right and C++ provides means to meet
your expectations.
Example: Delegation
class B {
public:
...
void f2d(int i) { cout << "In B::f2d(int)" << endl; }
...
}; // end of class B
class D : public B {
public:
...
void f2d(short s) { cout << "In D::f2d(short)" << endl; }

Regardless of whether the call is to some virtual function or not, explicit use of the class name in the function call causes it to be
dispatched statically. In the following function, for instance, [although this is of type D* and f2d(int) is virtual]
function call in the second statement will be dispatched to B::f2d(int).
Note this type of static dispatch can be used to invoke any function in the receiver objects class or any function in any one of
the ancestor classes.
void f2d(int i) { cout << "Delegating..."; B::f2d(i); }
...
}; // end of class D
...
D* d = new D();
int i = 3;
d->f2d(i); // will be delegated to B::f2d(int) through D::f2d(int)
short s = 5;
d->f2d(s); // will be dispatched to D::f2d(short)
...
Example: using declaration

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

4 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

class B {
public:
...
void f2u(string s) { cout << "In B::f2u(string)" << endl; }
void f2u(void) { cout << "In B::f2u(void)" << endl; }
}; // end of class B
class D : public B {
public:
...
void f2u(float f) { cout << "In D::f2u(float)" << endl; }
using B::f2u;
...
}; // end of class D
...
D* d = new D();
float f = 3.0;
d->f2u(f); // will be dispatched to D::f2u(float)
string s = string("abc");
d->f2u(s); // will be dispatched to B::f2u(string)
d->f2u(); // will be dispatched to B::f2u(void)

Multiple Inheritance, No Root Class


Unlike Java, where a class can derive from one and only one class, C++ supports derivation from multiple classes. Considered
with the fact that interface notion is not supported, this feature is heavily used to implement interfaces.
class D : public B1, public B2 { ... }
One other point to take note of in C++ is its lack of a root class. That is, there is no classsuch as the Object class in
Javathat serves as a common denominator among different classes. Consequently, one talks about a directed acyclic graph of
classes instead of a tree of classes.

Test Program
Peculiarities.cxx

1.
#include <iostream>
2.
#include <string>
3.
using namespace std;
4.

5.
namespace CSE224 {
6.
namespace DS {
7.
class B {
8.
public:
9.
B(void) { }
10.
void f1s(void) { cout << "In B::f1s(void)" << endl; }
11.
virtual void f1d(void) { cout << "In B::f1d(void)" << endl; }
12.
virtual void f2(int i) { cout << "In B::f2(int)" << endl; }

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

5 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

13.
virtual void f2d(int i) { cout << "In B::f2d(int)" << endl; }
14.
virtual void f2u(string s) { cout << "In B::f2u(string)" << endl; }
15.
virtual void f2u(void) { cout << "In B::f2u(void)" << endl; }
16.
}; // end of class B
17.

18.
class D : public B {
19.
public:
20.
D(void) { }
21.
void f1s(void) { cout << In D::f1s(void) << endl; }
22.
virtual void f1d(void) { cout << In D::f1d(void) << endl; }
23.
virtual void f2(short s) { cout << In D::f2(short) << endl; }
24.
virtual void f2d(short s) { cout << In D::f2d(short) << endl; }
25.
virtual void f2d(int i) { cout << Delegating...; this->B::f2d(i); }
26.
virtual void f2u(float f) { cout << In D::f2u(float) << endl; }
27.
using B::f2u;
28.

29.
int _m_i;
30.
short _m_s;
31.
}; // end of class D
32.
} // end of namespace DS
33.
} // end of namespace CSE224
34.

35.
using namespace CSE224::DS;
36.

37.
void default_is_static_dispatch(void) {
38.
cout << "TESTING DEFAULT DISPATCH TYPE" << endl;
39.
cout << "b: Static type: B*, Dynamic type: D*" << endl;
40.
B* b = new D();
41.
cout << "Sending (non-virtual) f1s(void) to b..."; b->f1s();
42.
cout << "Sending (virtual) f1d(void) to b..."; b->f1d();
43.
} // end of void default_is_static_dispatch(void)
44.

45.
void call_delegation(void) {

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

6 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

46.
cout << "Testing delegation..." << endl;
47.
D* d = new D();
48.
int i = 3;
49.
cout << "Sending (virtual) f2d(int) to d...";
50.
d->f2d(i);
51.
short s = 5;
52.
cout << "Sending (virtual) f2d(short) to d...";
53.
d->f2d(s);
54.
} // end of void call_delegation(void)
55.

56.
void using_declaration(void) {
57.
cout << "Testing the using declaration..." << endl;
58.
D* d = new D();
59.
float f = 3.0;
60.
cout << "Sending (virtual) f2u(float) to d...";
61.
d->f2u(f);
62.
string s = string(abc);
63.
cout << "Sending (virtual) f2u(string) to d...";
64.
d->f2u(s);
65.
cout << "Sending (virtual) f2u(void) to d...";
66.
d->f2u();
67.
} // end of void using_declaration(void)
68.

69.
void CPP_hides_by_name(void) {
70.
cout << "TESTING HIDE-BY NAME" << endl;
71.
D* d = new D();
72.
int i = 3;
73.
cout << "Sending (virtual) f2(int) to d...";
74.
d->f2(i);
75.
call_delegation();
76.
using_declaration();
77.
} // end of void CPP_hides_by_name(void)
78.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

7 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

79.
void no_member_initialization(void) {
80.
cout << "TESTING MEMBER INITIALIZATION" << endl;
81.
D* d = new D();
82.
cout << "_m_i: " << d->_m_i << " _m_s: " << d->_m_s << endl;
83.
} // end of void no_member_initialization(void)
84.

85.
int main(void) {
86.
no_member_initialization();
87.
cout << endl;
88.
default_is_static_dispatch();
89.
cout << endl;
90.
CPP_hides_by_name();
91.

92.
return 0;
93.
} // end of int main(void)

gxx o Test.exe Peculiarities.cxx


Test
TESTING MEMBER INITIALIZATION
_m_i: -1 _m_s: 9544
TESTING DEFAULT DISPATCH TYPE
b: Static type: B*, Dynamic type: D*
Sending (non-virtual) f1s(void) to b...In B::f1s(void)
Sending (virtual) f1d(void) to b...In D::f1d(void)
TESTING
Sending
Testing
Sending
Sending
Testing
Sending
Sending
Sending

HIDE-BY NAME
(virtual) f2(int) to d...In D::f2(short)
delegation...
(virtual) f2d(int) to d...Delegating...In B::f2d(int)
(virtual) f2d(short) to d...In D::f2d(short)
the using declaration...
(virtual) f2u(float) to d...In D::f2u(float)
(virtual) f2u(string) to d...In B::f2u(string)
(virtual) f2u(void) to d...In B::f2u(void)

Inheritance a la Java
In this part of the handout, we provide an insight into how C++ and Java can be related as far as inheritance is concerned. This is
accomplished by simulating the concepts found in Java using those found in C++. Such an approach should not be taken as an
advertisement campaign of Java; needless to say Java is not without competition. It should rather be taken as an incomplete
attempt at providing clues to the inner workings of the mentioned concepts.

Root Class and the Interface Concept

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

8 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

In Java, expressing common attributes among unrelated objects is made possible by means of the root class and the interface
concept. The former defines a common denominator among all classes, while the latter is used to classify a group of classes.[1]
For instance, because it is listed in Object all objects can be tested for equality with an object of a compatible type; or objects
of classes claiming to be Comparable can be compared with a compatible object.
These two notions are not supported in C++ as a linguistic abstraction. Instead, programmers are expected to resort to using
conventions or simulate it through other constructs. For instance, testing for equality is accomplished by overriding the default
implementation of the == operator; interface concept, which is not directly supported, can be simulated by means of abstract
classes with pure virtual functions.
Object

1.
#ifndef OBJECT_HXX
2.
#define OBJECT_HXX
3.

4.
namespace System {
5.
class Object {

Our intention in having the header file Object is to define a root class that can be used as a polymorphic type in generic
functions, such as compareTo defined in IComparable; we do not mean to provide any shared functionality as is done in
Object class of Java. This, however, cannot be accomplished simply by defining an empty class. In order for a type to be
polymorphic in C++ it must have at least one virtual function. We therefore include a dummy virtual function in our class
definition.
But then, why did we make its access modifier protected? First of all, it cannot be public because we dont want any
functionality to be exposed through this class. What about declaring no_op to be private? After all, declaring it as
protected means deriving classes can now send the no_op message. Answer lies in the nature of polymorphism: In order for
polymorphism to be possible, one should be able to override the definition of a dynamically-dispatched function found in the
base class. This implies such functions should be open at least to the derived classes. As a matter of fact C++ compilers will not
even let you declare virtual functions in a <sourcelang="cpp" enclose="none">private</source> section.

6.
protected:
7.
virtual void no_op(void) { return; }
8.
}; // end of class Object
9.
} // end of namespace System
10.

11.
#endif

Definition: A pure virtual function is a virtual function that is not given a function body in the declaring class. A derived class
claiming to be concrete must therefore provide an implementation for such a function.
In terms of C++-supported concepts, an interface is a "fieldless" abstract class whose all functions are pure virtual.
IComparable

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

9 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

1.
#ifndef ICOMPARABLE_HXX
2.
#define ICOMPARABLE_HXX
3.

4.
#include "Object"
5.

6.
namespace System {
7.
class IComparable {
8.
public:
9.
virtual int compareTo(const Object&) const = 0;
10.
}; // end of class IComparable
11.
} // end of namespace System
12.

13.
#endif

Module
Interface (Rational)
Rational

1.
#ifndef RATIONAL_HXX
2.
#define RATIONAL_HXX
3.

4.
#include <iostream>
5.
using namespace std;
6.

7.
#include "IComparable"
8.
#include "Object"
9.
using namespace System;
10.

11.
#include "math/exceptions/NoInverse"
12.
#include "math/exceptions/ZeroDenominator"
13.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

10 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

#include "math/exceptions/ZeroDivisor"
14.
using namespace CSE224::Math::Exceptions;
15.

16.
namespace CSE224 {
17.
namespace Math {

Having defined the interface concept as a variation on the class concept, we naturally should be cautious about speaking of the
implementation relation. This is indeed the case in C++: one can speak of the extension relation only. As a consequence support
for multiple inheritance is a must.

18.
class Rational : public Object, public IComparable {
19.
public:
20.
Rational(long num = 0, long den = 1) throw(ZeroDenominator) {
21.
if (den == 0) {
22.
cerr << "Error: ";
23.
cerr << "About to throw ZeroDenominator exception" << endl;
24.
throw ZeroDenominator();
25.
} // end of if (den == 0)
26.
_n = num;
27.
_d = den;
28.
this->simplify();
29.
} // end of constructor(long=, long=)
30.

31.
Rational(Rational& existingRat) {
32.
_n = existingRat._n;
33.
_d = existingRat._d;
34.
} // end of copy constructor

Notice the following functions, unlike the rest of the member functions, will be dispatched statically. In Java, such an effect can
be achieved by declaring methods to be final.

36.
long getNumerator(void) const { return _n; }
37.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

11 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

long getDenominator(void) const { return _d; }

In addition to marking functions virtual, we also declare them to return a reference. This is because a reference is the best
candidate for serving the purpose of handles in Java: it is an inheritance-aware, compiler-managed pointer.[2] That is, we can
pass as argument to the next function [or any function expecting a reference to Rational, for that matter] an object belonging
in the class hierarchy rooted by the Rational class; dereferencing of a reference is automatically done by the compilersynthesized code.
As an alternativealthough the resulting code would be less writable and readablewe could have used a plain pointer.
However, using a plain object type is out of question. This is due to the fact that polymorphism together with inheritance requires
sending the same messagethat is what polymorphism is all aboutto objects of probably varying sizesenter inheritance
which in turn implies passing and returning variable-sized objects. This is something compilers cannot deal with! We should
provide some assistance, which we do by injecting a fixed-size programming entity in between: pointer or reference.

39.
virtual Rational& add(const Rational&) const;
40.
virtual Rational& divide(const Rational&) const throw(ZeroDivisor)
41.
virtual Rational& inverse(void) const throw(NoInverse);
42.
virtual Rational& multiply(const Rational&) const;
43.
virtual Rational& subtract(const Rational&) const;
44.
virtual int compareTo(const Object&) const;

Note the following function serves a purpose akin to that of toString in Java. Replacing sstream instead of ostream and
changing the implementation accordingly would make the analogy a more perfect one.

46.
friend ostream& operator<<(ostream&, const Rational&);
47.

48.
private:
49.
long _n, _d;
50.
long min(long n1, long n2);
51.
Rational& simplify(void);
52.
}; // end of class Rational
53.
} // end of namespace Math
54.
} // end of namespace CSE224
55.

56.
#endif

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

12 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

Implementation (Rational)
Rational.cxx

1.
#include <iostream>
2.
#include <memory>
3.
using namespace std;
4.

5.
#include "Object"
6.
using namespace System;
7.

8.
#include "math/exceptions/NoInverse"
9.
#include "math/exceptions/ZeroDenominator"
10.
#include "math/exceptions/ZeroDivisor"
11.
using namespace CSE224::Math::Exceptions;
12.

13.
#include "math/Rational"
14.

15.
namespace CSE224 {
16.
namespace Math {
17.
Rational& Rational::
18.
add(const Rational& rhs) const {

Note the missing try-catch block! Unlike Java, C++ does not mandate the programmer to put all potentially problematic code
in a guarded region. One can say all C++ exceptions are treated like the Java exceptions deriving from the
RuntimeException class. This gives the programmer a degree of freedom that enables her to come up with cleaner code. For
instance, reaching the next line means we are adding two well-formed Rational objects. Result of such an action can never
create a problem!

19.
Rational* sum = new Rational(_n * rhs._d + _d * rhs._n, _d * rhs._d);
20.

21.
return sum->simplify();
22.
} // end of Rational& Rational::add(const Rational&) const
23.

24.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

13 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

Rational& Rational::
25.
divide(const Rational& rhs) const throw(ZeroDivisor) {
26.
try {
27.
Rational& tmp_inv = rhs.inverse();
28.
Rational& ret_rat = this->multiply(tmp_inv);

Now that we are done with the temporary object that holds the inverse of rhs, we must return it to the memory allocator or put
up with the consequences of creating garbage at each use of this function. Thats pretty annoying! But then again, a C/C++
programmer does not make such easy mistakes.
Notice the address-of operator before tmp_inv. Application of this operator to a reference returns the starting address of the
region aliased by the reference. [Remember references are silently dereferenced at their point of use] In our case, this will be the
address of the object created as a result of sending the message inverse to rhs.

28.
delete &tmp_inv;
29.
return ret_rat;
30.
} catch (NoInverse e) {
31.
cerr << "Error: About to throw ZeroDivisor exception" << endl;
32.
throw ZeroDivisor();
33.
}
34.
} // end of Rational& Rational::divide(const Rational&) const
35.

36.
Rational& Rational::
37.
inverse(void) const throw(NoInverse) {
38.
try {
39.
Rational *res = new Rational(_d, _n);
40.
return *res;
41.
} catch(ZeroDenominator e) {
42.
cerr << "Error: About to throw NoInverse exception" << endl;
43.
throw NoInverse(_n, _d);
44.
}
45.
} // end of Rational& Rational::inverse(void) const
46.

47.
Rational& Rational::
48.
multiply(const Rational& rhs) const {

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

14 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

49.
Rational *res = new Rational(_n * rhs._n, _d * rhs._d);
50.

51.
return res->simplify();
52.
} // end of Rational& Rational::multiply(const Rational&) const
53.

54.
Rational& Rational::
55.
subtract(const Rational& rhs) const {

We formulate subtraction in terms of other operations: instead of subtracting a value, we add the negated value. For doing this
we create two temporary objects meaningful only throughout the current call. Before returning to the caller we should return
them to the memory allocator.
A so-called smart pointer object is exactly what we want. Such an object is initialized to point to a dynamically allocated object
created by a new expression and frees itthe dynamically allocated objectat the end of its (smart pointers) lifetime. The
following figure showing the memory layout after execution of line 47 should make this clear.

Heap object local to the function is created together with the smart pointer object, which is itself a local object created on the
run-time stack.9 This means allocation-constructor call and destructor call-deallocation of this smart pointer object will be
handled by the compiler-synthesized code. In other words, programmer need not worry about the life-cycle management of the
smart pointer object. So, if we can guarantee the heap object is destroyed-deallocated together with this smart pointer, its
life-cycle management will not be a problem anymore. This is accomplished by delet(e)ing the heap object within the destructor
of the related smart pointer object, which means the heap object will have been destroyed-deallocated by the time the
destruction of the smart pointer object is over. The following then describes the life-cycle of the smart pointer and the related
heap object.
1. Create the smart pointer in the run-time stack.
1. Pass the related heap object to the constructor of the smart pointer.
2. Use the heap object.
3. Call the destructor of the smart pointer by means of the compiler-synthesized code.
1. Delete the heap object from within the destructor of the smart pointer.
According to this, anonymous Rational objectsnew Rational(-1) and &(rhs.multiply(*neg_1))created in
the following definitions will have been automaticallythat is, without the intervention of the programmerreturned before
leaving the function.

56.
auto_ptr< Rational > neg_1(new Rational(-1));
57.
auto_ptr< Rational > tmp_mul(&(rhs.multiply(*neg_1)));

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

15 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

Observe the following application of the dereferencing operator receives as its operand a non-pointer variable, which may at first
seem as an error. After all, * works by returning the contents of memory indicated by its sole operand. However, this rather
limited description ignores the possibility of overloading the dereferencing operator. It is indeed the overloaded version of this
operator that enables the use of a non-pointer type. The following application of * makes use of the overloaded version defined
within the auto_ptr class, which returns the contents of the heap object managed by the smart pointer.
To make things clearer, we can suggest the following implementation for the auto_ptr class.
template <class ManagedObjectType>
class auto_ptr {
public:
auto_ptr(ManagedObjectType* managedObj) {
_managed_heap_object = managedObj;
...
} // end of constructor(ManagedObjectType*)
...
ManagedObjectType operator*(void) {
...
return *_managed_heap_object;
} // end of ManagedObjectType operator*(void)
...
private:
ManagedObjectType* _managed_heap_object;
} // end of class auto_ptr<ManagedObjectType>

58.
Rational &ret_rat = add(*tmp_mul);
59.

60.
return(ret_rat);
61.
} // end of Rational& Rational::subtract(const Rational&) const
62.

63.
int Rational::
64.
compareTo(const Object& rhs) const {
65.
double this_equi = ((double) _n) / _d;

In addition to the traditional C-style casting C++ offers a variety of cast operators: const_cast, dynamic_cast,
static_cast, and reinterpret_cast. Each of these performs a subset of the functionality offered by the traditional cast
operator and therefore one can say the new operators do not add any new functionality. Nevertheless, thanks to the extra support
from the compiler, they enable writing more type-safe programs. Using the new operators we state our intentions explicitly and
therefore get more maintainable code.
Example: Removing const-ness property of an object.
class B { };
...

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

16 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

const B cb = B;
...
B b = const_cast<B&>(cb); // equivalent to (B) cb or B (cb).
...
It should be noted that const_cast can also be used to change volatile-ness property of an object.
Since our intention of removing the const-ness has been made explicit by the relevant operator, maintainer of the code will
more quickly spot the occurrence of cast and realize what is being done. Alternative scheme of using the C-style cast lacks these
qualities: it is both difficult to find the location of the cast and figure out that const-ness is being removed.
Using dynamic_cast will also provide us with the benefit of safer code. This particular operator is used to bi-directionally cast
between polymorphic classesthat is; classes that has at least one virtual functionthat are related with each other by a public
derivation.
Question
dynamic_cast can be used to convert only between pointer/reference types. Why?
Answer
Polymorphic classes enable behavioral and structural variability. This can only be utilized by means of a pointer/reference,
which acts as a facade between the static world required by the compiler/linker and the variability demanded by a dynamic
world. Therefore, dynamic_cast, used to cast between polymorphic classes, expects its parameter to be a
pointer/reference and returns a pointer/reference.
Definition: Converting from one class to another in the same class hierarchy is referred to as downcasting if the target type is
more specialized. In case the target type is less specialized the act of casting is called upcasting.
Upcasting to a public base class is always successful since the messages listed in the interface of the target type is a subset of the
source type interface. On the other hand, casting from a derived class to one of its non-public base classes leads to a
compile-time error. Similarly, downcasting may give rise to run-time errors since we can potentially send messages that are not
found in the interface of the source type.
Example: Safer code with dynamic_cast.
class PB { virtual void f(void) { } };
class PD : public PB { virtual void g(void) { } };
...
PB* pb; ...; pb = new D;
...
PD* pd = dynamic_cast<PD*>(pb);
if (pd == NULL) { ... }
...

The above code downcasts a PB* variable to PD*, through which one can send the extra message named g. For this example,
this doesn't seem to be a problem. But what if pb was used to point to an object of PB instead of PD? What if it is used to point
to objects of different types as is shown in the following fragment?
if(some_condition) { ... ; pb = new D; ... }
else { ...; pb = new B; ... }
...
PD* pd = dynamic_cast<PD*>(pb);
There is no guarantee that we can send g to the underlying object, which can be of type PB or PD. This guarantee we are seeking
can be provided only if we can check the object type at run-time. And this is exactly what dynamic_cast does: by checking
compatibility of the pointer/referencestatic typewith the objectdynamic typedynamic_cast decides whether the cast
taking place is valid or not. If so a legitimate value is returned. Otherwise, in the case of casting a pointer, a NULL value is
returned, which basically removes any possibility of sending an illegal message; in the case of failing to cast a reference
std::bad_cast exception is thrown. Same actions are taken also when source and target types are not related with
inheritance.
Note this cost due to the run-time check performed by the compiler-synthesized code is never experienced as a result of using
the traditional cast operator. This is because the C-style cast operator makes no use of any run-time information.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

17 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

Observe casting up the class hierarchysince messages received by an object of the derived class is a subset of that of its base
classesdoes not need any run-time checks. This means cost due to dynamic_cast is not rationalized: why should we pay for
making a control, whose result is known to us? Solution offered by C++ is another cast operator that does its job using
compile-time information: static_cast. This operator can be used for performing conversions that are implicitly carried out
by the compiler, performing these implicit conversions in the reverse direction. It can also be used in place of dynamic_cast if
skipping the run-time checks is guaranteed to be safe.
Example:
PD* ppd = new PD;
// Implicit conversion, equivalent to static_cast<PB*>(ppd)
PB* ppb = ppd;
...
PD pd = PD;
// Implicit conversion, equivalent to static_cast<PB>(pd)
PB pb = pd;
...
enum Iterator_Type {ITERATOR_FORWARD, ITERATOR_BACKWARD};
int one = ITERATOR_BACKWARD; // Implicit conversion
int val;
...
// reverse conversion
Iterator_Type itr_type = static_cast<Iterator_Type>(val);
PB* pb = new PB();
// Normally, next line should have been written with dynamic_cast. But if we can make
sure pd does not get a g message it is OK and will save us the run-time checks
performed by dynamic_cast.
PD* pd = static_cast<PD*>(pb);
// pd is initialized with a legitimate pointer
This doesn't fully cover the functionality offered by the traditional cast operator. Conversions between unrelated/incompatible
pointer types are missing, for instance. This missing functionality is covered by the reinterpret_cast operator, which
can also perform conversions between pointer and integral types.
Example:
float f = 1.0;
float* fp = &f;
int* ip = reinterpret_cast<int*>(fp);
cout << hex << *ip << '\t' << dec << *fp << '\t' << *ip << endl;
(*ip) = (*ip) + 8 * 1024 * 1024;
cout << hex << *ip << '\t' << dec << *fp << '\t' << *ip << endl;

It should be kept in mind that this operator makes no checks on the source and target types; it simply bitwise-copies the
contents of the target into the source.

66.
double rhs_equi = ((double) dynamic_cast<const Rational&>(rhs)._n) / dynamic_cast<const Rational&>(rhs)._d;
67.
if (this_equi > rhs_equi) return 1;
68.
else if (this_equi == rhs_equi) return 0;
69.
else return -1;
70.
} // end of int Rational::compareTo(const Object&) const
71.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

18 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

72.
long Rational::
73.
min(long n1, long n2) { return (n1 > n2 ? n1 : n2); }
74.

75.
Rational& Rational::
76.
simplify(void) {
77.
long upperLimit = min(_n, _d);
78.

79.
for (long i = 2; i <= upperLimit;)
80.
if ((_n % i == 0) && (_d % i == 0)) { _n /= i; _d /= i; }
81.
else i++;
82.
if (_d < 0) { _n *= -1; _d *= -1; }
83.

84.
return(*this);
85.
} // end of Rational& Rational::simplify(void)
86.

87.
ostream& operator<<(ostream& os, const Rational& rat) {
88.
os << rat._n << " ";
89.
if (rat._d != 1) os << "/ " << rat._d;
90.

91.
return os;
92.
} // end of ostream& operator<<(ostream&, const Rational&)
93.
} // end of namespace Math
94.
} // end of CSE224

Interface (Whole)
Whole

1.
#ifndef WHOLE_HXX
2.
#define WHOLE_HXX
3.

4.
#include <iostream>
5.
using namespace std;

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

19 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

6.

7.
#include "math/Rational"
8.

9.
namespace CSE224 {
10.
namespace Math {
11.
class Whole : public Rational {
12.
public:

Remember Whole derives from Rational. Put differentlysince inheritance can be seen as a compiler-managed
compositionall Whole objects have a Rational sub-object as part of their memory layouts. Following notation used in the
member initialization list with no reference to the member being initialized will initialize the Rational sub-object found in the
Whole object being constructed.

13.
Whole(long num) : Rational(num) { }
14.
Whole(void) : Rational(0) { }
15.
Whole(Whole& existingWhole) :
16.
Rational(existingWhole.getNumerator()) { }
17.
Whole& add(const Whole& rhs) const;
18.
virtual Rational& add(const Rational&) const;
19.
}; // end of class Whole
20.
} // end of namespace Math
21.
} // end of namespace CSE224
22.

23.
#endif

Implementation (Whole)
Whole.cxx

1.
#include <iostream>
2.
using namespace std;
3.

4.
#include "math/Rational"

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

20 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

5.
#include "math/Whole"
6.

7.
namespace CSE224 {
8.
namespace Math {
9.
Rational& Whole::
10.
add(const Rational& rhs) const {
11.
cout << "[In Whole::add(Rational)] ";
12.

13.
return (Rational::add(rhs));
14.
} // end of Rational& Whole::add(const Rational&) const
15.

16.
Whole& Whole::
17.
add(const Whole& rhs) const {
18.
cout << "[In Whole::add(Whole)] ";
19.
Whole *res = new Whole(getNumerator() + rhs.getNumerator());
20.

21.
return *res;
22.
} // end of Whole& Whole::add(const Whole&) const
23.
} // end of namespace Math
24.
} // end of namespace CSE224

Exception Classes
NoInverse

1.
#ifndef NOINVERSE_HXX
2.
#define NOINVERSE_HXX
3.

4.
#include <iostream>
5.
using namespace std;
6.

7.
namespace CSE224 {
8.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

21 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

namespace Math{
9.
namespace Exceptions {
10.
class NoInverse {
11.
public:
12.
NoInverse(long num, long den) {
13.
cerr << "Error: Throwing a NoInverse exception" << endl;
14.
_n = num; _d = den;
15.
} // end of constructor(long, long)
16.

17.
void writeNumber(void) {
18.
cerr << "The problematic number is " << _n << "/" << _d << endl;
19.
} // end of void writeNumber()
20.

21.
private:
22.
long _n, _d;
23.
}; // end of class NoInverse
24.
} // end of namespace Exceptions
25.
} // end of namespace Math
26.
} // end of namespace CSE224
27.

28.
#endif

ZeroDenominator

1.
#ifndef ZERODENOMINATOR_HXX
2.
#define ZERODENOMINATOR_HXX
3.

4.
namespace CSE224 {
5.
namespace Math {
6.
namespace Exceptions {
7.
class ZeroDenominator {
8.
public:
9.
ZeroDenominator(void) {

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

22 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

10.
cerr << "Error: Throwing a ZeroDenominator exception" << endl;
11.
} // end of default constructor
12.
}; // end of class ZeroDenominator
13.
} // end of namespace Exceptions
14.
} // end of namespace Math
15.
} // end of namespace CSE224
16.

17.
#endif

ZeroDivisor

1.
#ifndef ZERODIVISOR_HXX
2.
#define ZERODIVISOR_HXX
3.

4.
#include <iostream>
5.
using namespace std;
6.

7.
namespace CSE224 {
8.
namespace Math {
9.
namespace Exceptions {
10.
class ZeroDivisor {
11.
public:
12.
ZeroDivisor(void) {
13.
cerr << "Error: Throwing a ZeroDivisor exception" << endl;
14.
} // end of default constructor
15.
}; // end of class ZeroDivisor
16.
} // end of namespace Exceptions
17.
} // end of namespace Math
18.
} // end of namespace CSE224
19.

20.
#endif

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

23 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

Test Program
Test_Whole.cxx

1.
#include <iostream>
2.
#include <memory>
3.
using namespace std;
4.

5.
#include "math/Whole"
6.
using namespace CSE224::Math;
7.

8.
#include "math/exceptions/ZeroDenominator"
9.
using namespace CSE224::Math::Exceptions;
10.

11.
int main(void) {
12.
cout << "Creating a Whole object(five) and initializing it with 5..." << endl;
13.
auto_ptr < Whole > fivep(new Whole(5));
14.
Whole& five = *fivep;
15.
cout << "Creating a Rational object(three) and initializing it with 3..." << endl;
16.
auto_ptr < Rational > threep(new Rational(3));
17.
Rational& three = *threep;
18.

19.
cout << "Result of five.multiply(three) = ";
20.
cout << five.multiply(three) << endl;
21.

22.
cout << "***************************************************" << endl;
23.

24.
cout << "Result of three.add(three) = ";
25.
cout << three.add(three) << endl;
26.

27.
cout << "Result of three.add(five) = ";
28.
cout << three.add(five) << endl;
29.

30.
cout << "Result of five.add(three) = ";

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

24 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

31.
cout << five.add(three) << endl;
32.

33.
cout << "Result of five.add(five) = ";
34.
cout << five.add(five) << endl;
35.

36.
cout << "***************************************************" << endl;
37.

38.
cout << "Setting a Rational object(ratFive) as an alias for a Whole object(five)..." << endl;
39.
Rational& ratFive = five;
40.

41.
cout << "Result of ratFive.add(three) = ";
42.
cout << ratFive.add(three) << endl;
43.

44.
cout << "Result of ratFive.add(five) = ";
45.
cout << ratFive.add(five) << endl;
46.

47.
cout << "Result of ratFive.add(ratFive) = ";
48.
cout << ratFive.add(ratFive) << endl;
49.

50.
cout << "Result of five.add(ratFive) = ";
51.
cout << five.add(ratFive) << endl;
52.

53.
cout << "Result of three.add(ratFive) = ";
54.
cout << three.add(ratFive) << endl;
55.

56.
cout << "***************************************************" << endl;
57.

58.
cout << "Creating a Rational object(r1) and initializing it with 3/2..." << endl;
59.
auto_ptr < Rational > r1p(new Rational(3, 2));
60.
Rational& r1 = *r1p;
61.
cout << "Result of five.multiply(r1) = ";
62.
cout << five.multiply(r1) << endl;
63.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

25 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

cout << "Result of five.divide(r1) = ";


64.
cout << five.divide(r1) << endl;
65.

66.
return 0;
67.
} // end of int main(void)

Private Inheritance
Programming being a pragmatic endeavor, one must strive to do it as efficiently as possible. Arguably the most effective way to
achieve this is to re-use artifacts that have already been used in different stages of previous projects.[3] By doing so we save on
development and test time, which means our next product makes it to the market in a shorter time.
One way to achieve reuse is inheritance. And for many it seems to be the only affordable one. Nevertheless, there is a
contender: composition. This technique is realized by making an object a member of another.
class C {... B b; ...};
For the above example we say an object of C is composed of, apart from other things, an object of B. Put differently, we say an
object of C has-an (contains) object of B. This is certainly different than inheritance where the relationship is defined to be an
is-a relationship.
In C++, similar effect can be achieved through the so-called private inheritance.
class C : /* private */ B { ... }

Interface (List)
List

1.
#ifndef LIST_HXX
2.
#define LIST_HXX
3.

4.
#include "ds/exceptions/List_Exceptions"
5.
using namespace CSE224::DS::Exceptions;
6.

7.
namespace CSE224 {
8.
namespace DS {
9.
class List {
10.
friend ostream& operator<<(ostream&, const List&);
11.
public:
12.
List(void) : _size(0), _head(NULL), _tail(NULL) { }
13.
List(const List&);
14.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

26 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

~List(void);
15.
List& operator=(const List&);
16.
bool operator==(const List&);
17.

18.
double get_first(void) throw(List_Empty);
19.
double get_last(void) throw(List_Empty);
20.
void insert_at_end(double new_item);
21.
void insert_in_front(double new_item);
22.
double remove_from_end(void) throw(List_Empty);
23.
double remove_from_front(void) throw(List_Empty);
24.
bool is_empty(void);
25.
unsigned int size(void);
26.

27.
private:

What follows is the definition of a nested class, a class defined inside another. Such a class, when defined in a private
section, is not visible outside its enclosing class. This scheme is useful when the two classes are tightly coupled, as is the case in
our example: A List_Node object is used only in the context of a List object. What makes up our List objects is an
implementation detail and should not be a concern to their users.
Although it might be tempting to draw a parallel between nested classes and inner classes of Java, that would be a mistake. As
opposed to the special relation between the inner class and its enclosing class, in C++ the enclosing class has no special access
privileges with regard to the classes nested within it. For this reason, changing public to private or protected is not a
good idea.
Another remark to be made is that nested classes of C++ do not keep any record of the enclosing object in the objects of the
inner class, which makes them like more the static inner classes of Java.

28.
class List_Node {
29.
public:
30.
List_Node(double val) : _info(val), _next(NULL), _prev(NULL) { }
31.

32.
double _info;
33.
List_Node *_next, *_prev;
34.
}; // end of class List_Node
35.
private:
36.
List_Node *_head, *_tail;
37.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

27 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

unsigned int _size;

A function declared in the private section? Yes! Functions, which are used by other functions and are not part of the
interface, are declared in the private section. Note that you cannot get away without declaring such functions in the class
interface.

38.
void insert_first_node(double);
39.
}; // end of List class
40.
} // end of namespace DS
41.
} // end of namespace CSE224
42.

43.
#endif

Implementation (List)
List.cxx

1.
#include <iostream>
2.
using namespace std;
3.

4.
#include "ds/List"
5.
#include "ds/exceptions/List_Exceptions"
6.
using namespace CSE224::DS::Exceptions;
7.

8.
namespace CSE224 {
9.
namespace DS {
10.
List::
11.
List(const List& rhs) : _head(NULL), _tail(NULL), _size(0) {
12.
List_Node *ptr = rhs._head;
13.
for(unsigned int i = 0; i < rhs._size; i++) {
14.
this->insert_at_end(ptr->_info);
15.
ptr = ptr->_next;
16.
} // end of for(unsigned int i = 0; i < rhs._size; i++)

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

28 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

17.
} // end of copy constructor

Now that nodes of the List objects are allocated on the heap, we must make sure they are turned back over to the memory
allocator as the List object itself is, implicitly or explicitly, deleted. For this reason, we need to write a destructor to free these
nodes. Note a List object is made up of two pointers, which show the head and the tail of the list, and a field holding its size.
The nodes pointed, directly or indirectly, by these pointers are not part of the List object. So, they will not be automatically
freed together with the list object. For this reason, we do need a destructor.
Notice our decision is not affected by whether the List object itself is created on the heap or not. Where the List object is
created has an effect on who should call the destructor: Whoever the responsible party might be, compiler or programmer, in all
possible scenarios the destructor is implicitly called before deallocation of the object. If it is created on the heap the programmer
is responsible for making the call. Otherwise, the compiler will take care of the drudgery as the scope of the object is closed.

19.
List::
20.
~List(void) {
21.
for (int i = 0; i < _size; i++)
22.
remove_from_front();
23.
} // end of destructor
24.

25.
List& List::
26.
operator=(const List& rhs) {
27.
if (this == &rhs) return (*this);
28.

29.
for(unsigned int i = _size; i > 0; i--)
30.
this->remove_from_front();
31.
List_Node *ptr = rhs._head;
32.
for(unsigned int i = 0; i < rhs._size; i++) {
33.
this->insert_at_end(ptr->_info);
34.
ptr = ptr->_next;
35.
} // end of for(unsigned int i = 0; i < rhs._size; i++)
36.

37.
if (rhs._size == 0) {
38.
_head = _tail = NULL;
39.
_size = 0;
40.
} // end of if(rhs._size == 0)
41.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

29 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

42.
return (*this);
43.
} // end of assignment operator
44.

45.
bool List::
46.
operator==(const List& rhs) {
47.
if (_size != rhs._size) return false;
48.
if (_size == 0 || this == &rhs) return true;
49.

50.
List_Node *ptr = _head;
51.
List_Node *ptr_rhs = rhs._head;
52.
for (unsigned int i = 0; i < _size; i++) {
53.
if (!(ptr->_info == ptr_rhs->_info))
54.
return false;
55.
ptr = ptr->_next;
56.
ptr_rhs = ptr_rhs->_next;
57.
} // end of for(unsigned int i = 0; i < _size; i++)
58.

59.
return true;
60.
} // end of equality-test operator
61.

62.
double List::
63.
get_first(void) throw(List_Empty) {
64.
if (is_empty()) throw List_Empty();
65.

66.
return (_head->_info);
67.
} // end of double List::get_first(void)
68.

69.
double List::
70.
get_last(void) throw(List_Empty) {
71.
if (is_empty()) throw List_Empty();
72.

73.
return (_tail->_info);
74.
} // end of double List::get_last(void)

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

30 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

75.

76.
void List::
77.
insert_at_end(double new_item) {
78.
if (is_empty()) insert_first_node(new_item);
79.
else {
80.
List_Node *new_node = new List_Node(new_item);
81.
_tail->_next = new_node;
82.
new_node->_prev = _tail;
83.
_tail = new_node;
84.
}
85.
_size++;
86.
} // end of void List::insert_at_end(double)
87.

88.
void List::
89.
insert_in_front(double new_item) {
90.
if (is_empty()) insert_first_node(new_item);
91.
else {
92.
List_Node *new_node = new List_Node(new_item);
93.
new_node->_next = _head;
94.
_head->_prev = new_node;
95.
_head = new_node;
96.
}
97.
_size++;
98.
} // end of void List::insert_in_front(double)
99.

100.
double List::
101.
remove_from_end(void) throw(List_Empty) {
102.
if (is_empty()) throw List_Empty();
103.

104.
double ret_val = _tail->_info;
105.
List_Node *temp_node = _tail;
106.

107.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

31 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

if (_size == 1) { head = _tail = NULL; }


108.
else {
109.
_tail = _tail->_prev;
110.
_tail->_next = NULL;
111.
}
112.

113.
delete temp_node;
114.
_size--;
115.

116.
return ret_val;
117.
} // end of double List::remove_from_front(void)
118.

119.
double List::
120.
remove_from_front(void) throw(List_Empty) {
121.
if (is_empty()) throw List_Empty();
122.

123.
double ret_val = _head->_info;
124.
List_Node *temp_node = _head;
125.

126.
if (_size == 1) { _head = _tail = NULL; }
127.
else {
128.
_head = _head->_next;
129.
_head->_prev = NULL;
130.
}
131.

132.
delete temp_node;
133.
_size--;
134.

135.
return ret_val;
136.
} // end of double List::remove_from_front(void)
137.

138.
bool List::
139.
is_empty(void) { return(_size == 0); }

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

32 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

140.

141.
unsigned int List::
142.
size(void) { return _size; }
143.

144.
void List::
145.
insert_first_node(double new_item) {
146.
List_Node *new_node = new List_Node(new_item);
147.
_head = _tail = new_node;
148.
} // end of void List::insert_first_node(double)
149.

150.
ostream& operator<<(ostream& os, const List& rhs) {
151.
List tmp_list = rhs;
152.

153.
os << "<" << rhs._size << "> <head: ";
154.
for (int i = 0; i < rhs._size - 1; i++) {
155.
os << tmp_list._head->_info << ", ";
156.
tmp_list._head = tmp_list._head->_next;
157.
} // end of for(int i = 0; i < rhs._size; i++)
158.
if (rhs._size > 0) os << tmp_list._head->_info;
159.
os << ": tail>";
160.

161.
return(os);
162.
} // end of ostream& operator<<(ostream&, const List&)
163.
} // end of namespace DS
164.
} // end of namespace CSE224

Interface (Stack)
Stack

1.
#ifndef STACK_HXX
2.
#define STACK_HXX
3.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

33 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

4.
#include <iostream>
5.
using namespace std;
6.

7.
#include "ds/exceptions/Stack_Exceptions"
8.
using namespace CSE224::DS::Exceptions;
9.

10.
#include "ds/List"
11.

12.
namespace CSE224 {
13.
namespace DS {

The List class offers a superset of the functionality expected from a stack. This may at first lead us to think that we may
define a new class, Stack, and have it (publicly) derive from the List class. The problem with this approach is that the public
interface of the base class will be exposed as part of the public interface of the derived class. Not really what we would want in
this case: the List class offers a lot more than what we would expect from the Stack class. For this reason we should resort
to some other method, such as composition.
C++ offers an alternative: private inheritance. Using private inheritance, the derived class can still make use of the
functionality offered by the base class but the base class interface is not exposed through the derived class. For this reason,
Stack class privately inherits from the List class.

14.
class Stack : private List {
15.
public:

Now that the derived class can reuse the base class functionality but do not expose it to its users, this type of inheritance is also
called implementation inheritance. For a similar reason, public inheritance is also called interface inheritance.
We do not need to write the functions of the orthodox canonical form because compiler-synthesized versions provide the
equivalent of what we are required to do. This is basically because the only data field of Stack is the List sub-object it
inherits from List.

16.
// Stack(void);
17.
// Stack(const Stack&);
18.
// ~Stack(void);
19.
// Stack& operator=(const Stack&);
20.
// bool operator==(const Stack&);
21.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

34 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

22.
double peek(void) throw(Stack_Empty);
23.
double pop(void) throw(Stack_Empty);
24.
void push(double new_item);

Thanks to the following statement, we selectively expose a function from the privately inherited base class. Its as if the
is_empty function(s) from the List class were publicly inherited.

26.
using List::is_empty;
27.
}; // end of Stack class
28.
} // end of namespace DS
29.
} // end of namespace CSE224
30.

31.
#endif

Implementation (Stack)
Stack.cxx

1.
#include <iostream>
2.
using namespace std;
3.

4.
#include "ds/Stack"
5.
#include "ds/exceptions/Stack_Exceptions"
6.
using namespace CSE224::DS::Exceptions;
7.

8.
namespace CSE224 {
9.
namespace DS {
10.
double Stack::
11.
peek(void) throw(Stack_Empty) {
12.
double ret_val;

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

35 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

Users of our class should not be aware of how we implement the Stack class. Thats why we need to re-throw the exceptions
thrown by the List class so that it makes more sense to the user.

14.
try { ret_val = get_first();}
15.
catch(List_Empty) { throw Stack_Empty(); }
16.

17.
return ret_val;
18.
} // end of double Stack::peek(void)
19.

20.
void Stack::
21.
push(double new_item) { List::insert_in_front(new_item); }
22.

23.
double Stack::
24.
pop(void) throw(Stack_Empty) {
25.
double ret_val;
26.

27.
try { ret_val = remove_from_front(); }
28.
catch(List_Empty) { throw Stack_Empty(); }
29.

30.
return ret_val;
31.
} // end of double Stack::pop(void)
32.
} // end of namespace DS
33.
} // end of namespace CSE224

Virtual Inheritance
With the possibility of multiple inheritance rises the issue of the so-called virtual inheritance. Consider the class hierarchy
shown in Figure 2. Question that awaits your answer is: How many Animal sub-objects will there be in a Politician
object? Looking at the figure the correct answer seems to be two. However, our logic tells us a different story: there can be only
one Animal sub-object in a Politician.

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

36 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

Whichever one is the right answer, there might be cases where either one turns out to be a better choice. We must find a way to
tell the difference between the options. This is where the notion of virtual inheritance comes into the picture. We define the
Humanoid and Ape classes to be virtually derived from Animal.
Example: Virtual inheritance
class Animal { ... };
class Humanoid : public virtual Animal { ... };
class Ape : public virtual Animal { ... };
class Politician : public Humanoid, public Ape { ... };

Thanks to these definitions, there is now only one Animal sub-object in a Politician. This is achieved by assuring that
pointersnot objects themselvesare inserted into the derived classes. That is; instead of containing two Animal sub-objects,
a Politician object now has two pointers both pointing to the same Animal sub-object.
Note use of virtual inheritance causes the order of constructor call to be changed: Virtual base classes are always constructed
prior to non-virtual base classes regardless of where they appear in the inheritance hierarchy.
A typical use of virtual inheritance involves implementation of mix-in classes. Mix-in classes are used for tuning the behavior of
a base class and can be combined to obtain even more specialized classes. For example, using the following code one can create
windows with different styles: plain window, window with menu, window with border, and window with menu and border. As a
matter of fact, we can come up with our own mix-in, say scroll-bar mix-in, and get scroll-bar-supporting versions of these
window styles.
Example: Implementing mix-ins by virtual inheritance.
class Plain_Window {
public:
/* Basic window functions common to all windows */
private:
...
}; // end of class Plain_Window
class Menu_Mixin : public virtual Plain_Window {
public:
/* Functions specially required for manipulating window menus. */
private:
...
}; // end of class Menu_Mixin
class Border_Mixin : public virtual Plain_Window {
public:
/* Functions specially required for manipulating window borders. */
private:
...

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

37 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

}; // end of class Border_Mixin


class Window_with_Menu_and_Border : public Menu_Mixin, public Border_Mixin {
public:
...
private:
...
}; // end of class Window_with_Menu_and_Border
Note the number of window styles grows exponentially with the number of mix-ins. But, thanks to virtual inheritance, we do
not have to consider each and every combination. We start out with the base class and a few mix-ins. As we need more refined
window styles we come up with a new class inheriting from the relevant mix-in classes. If we see certain attributes are missing
from the mix-in classes we can write our own mix-in and use it like the others.

Implementing Polymorphism
In this section, we take a look at two widely used methods of implementing polymorphism. It is worth noting that both rely on
dynamically dispatching the function call to the relevant [function] entry point. In other words, polymorphism is implemented
by means of dynamic dispatch. Another point to make is the tool we use for expressing polymorphism: inheritance.
Mentioning polymorphism, inheritance, and dynamic dispatch in different contexts may make some think of them as unrelated
concepts. This, however, is utterly wrong. Truth of the matter is, in an object-oriented context these are complementary
concepts and cooperate for enabling the implementation of the is-a relation in a natural way. In order to express an is-a relation
between two classes we need help from both inheritance and polymorphism: inheritance is used to define a common message
protocol between the base and derived classes, whereas polymorphism is needed for providing the behavioral variability.[4]
This is further made possible by dynamically dispatching the messages.
As was mentioned in previous section, inheritance without polymorphism leads to inflexible, object-based solutions. Likewise,
polymorphism alone is generally not what you want. So, it is well-advised that you consider using these two together.
Previous remarks should not make you think we are bound to use the duo of inheritance and polymorphism only. Our success
in software industry depends on producing reliable, easily-extensible, efficient software. The keyword in reaching this goal is
reuse and aforementioned concepts are not without alternatives. Apart from the age-old composition technique, we can use
generics for instance. Parameterizing a class or a subprogram will also give us the benefits of reuse. An example to the former
is given in the Parameterized Types chapter, while use of the latter is provided below. Thanks to this method, users can sort any
array provided that a Comparator<V> object for the component type is also supplied.
Example: Generic methods in Java.
public class BubbleSort {
public static <V> void sort(V[] arr, Comparator<? super V> c) {
for (int i = arr. length - 1; i > 0; i--) {
int noOfSwaps = 0;
for (int j = 0; j < i; j++)
if (c. compare(arr[j], arr[j + 1]) > 0) {
CommonMethods.swap(arr, j, j + 1);
noOfSwaps++;
}
} // end of outer for-loop
} // end of <V> void sort(V[], Comparator<? super V>)
} // end of class BubbleSort

Such a method is said to provide parametric polymorphism. It is polymorphic in the sense that same method does the same
thing for parameters of different types; to the user of the method, it looks as though there were separate methods for each
different type.[5]
A similar effect can be experienced in the case of overloaded subprograms, where calls with different argument lists are
dispatched to subprograms with different signatures and users get different behavior as a result of calling the seemingly same
subprogram. For this reason, overloading is sometimes referred to as ad-hoc polymorphism.[6]
Having done away with the confusion about inheritance and polymorphism, let's move on to the techniques commonly used in

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

38 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

implementing polymorphism. But before we do that, we will give a listing of the sample classes used in our presentation.
class B1 {
...
public:
virtual void f(int);
void fs(int);
...
...
} ; // end of base class B1
class B2 {
...
public:
virtual void f(int);
virtual void g(void);
...
...
} ; // end of base class B2
class D : public B1, public B2 {
...
public:
void f(int);
...
...
}; // end of derived class D

vtables
The vtable technique, generally used in compilers of the PC world, utilizes a table containing rows of function entry address
and offset pair. Objects of all classes with at least one dynamically dispatched function has a pointer, called the vptr, pointing
to the start of this table. The offset column is used for adjusting the value of the this pointer. This adjustment is required
when an object of a derived class is used through a pointer of a base class. Take the definition of D, whose object memory
layout is given below. Given an object of D, we can use it through pointers of D and any type that is an ancestor type of D,
which in our case are D*, B1*, and B2*. Put another way, through a variable of type B1* we can manipulate any object that is
an instance of a class deriving from B1, which in our case are D and B1. Accordingly, the following is possible.
...
D* d_obj = new D(...);
B1* b1_obj = new B1(...);
B2* b2_obj = new B2(...);
... fd(D* d_par) {
...
d_par->f(...);
d_par->g(...);
...
} // end of ... fd(D*)
... fb1(B1* b1_par) {
...
b1_par->f(...);
...
...
} // end of ... fb1(B1*)
... fb2(B2* b2_par) {
...
b2_par->f(...);
b2_par->g(...);
...

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

39 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

} // end of ... fb2(B2*)


...
...
...
...
...

fd(d_obj) ...;
fb1(b1_obj) ...;
fb1(d_obj) ...;
fb2(b2_obj) ...;
fb2(d_obj) ...;

Having overridden f in the derived class (D) means invoking this version of f will potentially make use of all properties found
in B1, B2, and D, which implies the receiver object of this function must at least be a D object. As was mentioned before such
an object can also be manipulated through B2*. This is exemplified by executing line 7 of the above fragment following the
call on line 15. Note also, upon executing the function call on line 14, the function call on line 7 is dispatched with a B2 object.
Since b2_par is of type B2*, both cases are handled via the vptr field of a B2 object. However, in one case this object is B2
part of a D object whereas in the other it is a plain B2 object. This is shown in figures above.
B2 as part of a D object needs our attention. Starting address of the object (D) and the pointer used for manipulating this object
(B2*) indicate different locations in memory. This requires thatin order to enable use of all properties of a D objectwe
adjust the pointer value as many bytes as there are before the B2 sub-object, which is referred to as delta(B2) in our figure.

Adjustor Thunks
The second technique we will look at can be seen as a less portable [7] optimization on the first one. As in the vptr technique,
one column in the vtable contains a pointer-to-function. But we now utilize thunks instead of the adjustment column. In case
an adjustment to this is needed, pointer-to-function in the one and only column of the vtable points to the thunk code generated
by the compiler, which modifies this and jumps to the function entry. If no adjustment to this is required, the pointerto-function contains the address of the entry of the function to be invoked.
Insert Adjustor Thunks figure here

Complications due to Multiple Inheritance


Observe implementation of polymorphism is complicated by requirements of multiple [implementation] inheritance. Did we
content ourselves with single inheritance an object would always have a single vptr and we would never need to make any
adjustments to this.
Question: Do we face the same complications while implementing multiple interface inheritance? As a starting point consider
memory layouts of objects of a class that implements multiple interfaces or realizes an interface that multiply inherits from
other interfaces.

Constructor and Destructor Call Order


Object construction involves allocating memory for the object and initializing its contents [and acquiring outside resources, if
needed] by means of a call to the relevant constructor. This atomic sequence is triggered on three conditions:
1. A variable with static extent is defined: In the case of a global variable, memory for the object is allocated in the static
data region at compile time and constructor call [that is, initialization and resource acquisition] is executed as the first
statement of the program. If there is more than one such variable, constructors are executed in the order in which the
corresponding definitions occur in the text. Static local variables differ in two aspects: call to the constructor is executed
once upon first entry to the function and this call does not modify the order of statement execution in the function.
2. A block variable is defined: Both allocation and initialization take place at run-time each time control flow reaches the
point of the variable definition.
3. An object is created using the new operator: Object is allocated and initialized as the control flow reaches the statement
where the new operator is applied.
Example: Construction order of variables with static extent.
class C {
public:
C(void) { cout << "In the constructor of C..." << endl; }
...
}; // end of class C

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

40 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

void f(void) {
cout << "First statement in f..." << endl;
static C c_sl;
cout << "Last statement in f..." << endl;
} // end of void f(void)
C c_g;
int main(void) {
f();
cout << "In main..." << endl;
f();
return 0;
} // end of int main(void)

In the constructor of C...


First statement in f...
In the constructor of C...
Last statement in f...
In main...
First statement in f...
Last statement in f...

// Global variable c_g


// Static local variable c_sl

In case there may not be any programmer-defined constructors, the C++ compiler synthesizes a default constructor that makes
calls to default constructors of the sub-objects. In an object with primitive type fieldssince primitive types do not support the
notion of constructorthis means no constructor call is ever made, which leaves the newly created object in a random state.
Complementing construction, destruction of the object is accomplished by a call to the destructor function, which is followed
by release of the object's memory. The destructor, if there is any, is meant to release resources- both heap memory and outside
resources- being used by the soon-to-be recycled object.
Destructor invocations of static variables take place following the last statement of the program while block variables are
destroyed upon exit from the block they are defined in.[8] In the presence of multiple variable declarations in the same scope,
the order of destructor calls for both kinds of variables is the reverse of the constructor call order.

An Object with Primitive Type Fields


Unlike Java and C#, where primitive type fields are guaranteed to have specific initial values, C++ does not perform any
implicit initialization of such fields. In other words, unless provided by the programmer, such fields are left uninitialized.
Example:
class C1 {
public:
C1(void) { cout << "In the default constructor of C1" << endl; }
C1(int i) {
_i = i;
cout << "In the C1 constructor taking an int... i = " << _i << endl;
} // end of constructor(int)
~C1(void) { cout << "In the destructor of C1... i = " << _i << endl; }
private:
int _i;
}; // end of class C1
...
{
C1 *o3 = new C1(3);
C1 o, o1(1), o2(2);
...;

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

41 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

delete o3;
}
...

...
In the
In the
In the
In the
In the
In the
In the
In the
...

C1 constructor taking an int... i = 3


default constructor of C1
C1 constructor taking an int... i = 1
C1 constructor taking an int... i = 2
destructor of C1... i = 3
destructor of C1... i = 2
destructor of C1... i = 1
destructor of C1... i = 138645

//
//
//
//
//
//
//
//

for
for
for
for
for
for
for
for

o3
o
o1
o2
o3
o2
o1
o

An Object Composed of Sub-object(s)


In the process of creating a composite object, a call to the constructor [of the composite object] is preceded by the relevant
constructor call(s) for the sub-object(s). Unless these calls are explicitly made in the member initialization list, sub-objects are
initialized using the default constructor(s).
Example:
class C2 {
public:
C2(void) { cout << In the default constructor of C2 << endl; }
~C2(void) { cout << In the destructor of C2 << endl; }
private:
C1 _o;
}; // end of class C2
...
{
C2 o;
...;
}
...

...
In the
In the
In the
In the
...

default constructor of C1
default constructor of C2
destructor of C2
destructor of C1

//
//
//
//

for
for
for
for

o._o
o
o
o._o

In the case of multiple sub-objects constructor calls are made in the order in which they occur in the class definition.
Example: An object with multiple sub-objects.
class C3 {
public:
C3(void) { cout << "In the default constructor of C3" << endl; }
~C3(void) { cout << "In the destructor of C3" << endl; }
private:
C1 _o1, _o2;
}; // end of class C3
...
{
C3 o;
...;

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

42 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

}
...

...
In the
In the
In the
In the
In the
In the
...

default constructor of C1
default constructor of C1
default constructor of C3
destructor of C3
destructor of C1
destructor of C1

//
//
//
//
//
//

for
for
for
for
for
for

o._o1
o._o2
o
o
o._o2
o._o1

Creating an Array of Objects


A facility to define group of variables belonging to the same type, an array variable is initialized by initializing each and every
one of its components. Similarly, destroying this array requires destruction of each and every one of its components.
Example:
class C2 {
public:
C2(void) { cout << "In the default constructor of C2" << endl; }
~C2(void) { cout << "In the destructor of C2" << endl; }
private:
C1 _o;
}; // end of class C2
...
{
C2 o[2];
...;

// Equivalent to C2 o0, o1;

}
...

...
In the
In the
In the
In the
In the
In the
In the
In the
...

default constructor
default constructor
default constructor
default constructor
destructor of C2
destructor of C1
destructor of C2
destructor of C1

of
of
of
of

C1
C2
C1
C2

//
//
//
//
//
//
//
//

for
for
for
for
for
for
for
for

o[0]._o
o[0]
o[1]._o
o[1]
o[1]
o[1]._o
o[0]
o[0]._o

Example: An object with an array of sub-objects.


class C4 {
public:
C4(void) { cout << "In the default constructor of C4" << endl; }
~C4(void) { cout << "In the destructor of C4" << endl; }
private:
C1 _o[2];
// equivalent to C1 _o0, _o1;
}; // end of class C4
...
{
C4 o;
...;
}
...

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

43 of 48

...
In the
In the
In the
In the
In the
In the
...

default constructor of C1
default constructor of C1
default constructor of C4
destructor of C4
destructor of C1
destructor of C1

//
//
//
//
//
//

for
for
for
for
for
for

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

o._o[0]
o._o[1]
o
o
o._o[1]
o._o[0]

Example:
class C5 {
public:
C5(void) { cout << "In the default constructor of C5" << endl; }
~C5(void) { cout << "In the destructor of C5" << endl; }
private:
C2 _o[2];
}; // end of class C5
...
{
C5 o;
...;
}
...

...
In the
In the
In the
In the
In the
In the
In the
In the
In the
In the
...

default constructor
default constructor
default constructor
default constructor
default constructor
destructor of C5
destructor of C2
destructor of C1
destructor of C2
destructor of C1

of
of
of
of
of

C1
C2
C1
C2
C5

//
//
//
//
//
//
//
//
//
//

for
for
for
for
for
for
for
for
for
for

o._o[0]._o
o._o[0]
o._o[1]._o
o._o[1]
o
o
o._o[1]
o._o[1]._o
o._o[0]
o._o[0]._o

Inheritance
Seeing inheritance as "compiler-managed composition" is key to figuring out constructor and destructor call order. Rest is all
the same.
Example:
class IC1 : public C1 {
public:
IC1(void) { cout << "In the default constructor of IC1" << endl; }
~IC1(void) { cout << "In the destructor of IC1" << endl; }
}; // end of class IC1
...
{
IC1 o1;
...;
}
...

First line of the above fragment can be thought of having been converted by the compiler to the following. Note the identifier
name is arbitrary and cannot be in any way referenced in the class implementation.
class IC1 {

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

44 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

public:
C1 _c1_part;
private:

...
In the
In the
In the
In the
...

default constructor of C1
default constructor of IC1
destructor of IC1
destructor of C1

//
//
//
//

for
for
for
for

C1 part of o1
o1
o1
C1 part of o1

Example: Inheritance and composition.


class IC2 : public C1 {
public:
IC2(void) { cout << "In the default constructor of IC2" << endl; }
~IC2(void) { cout << "In the destructor of IC2" << endl; }
private:
C1 _o;
}; // end of class IC2
...
{
IC2 o1;
...;
}
...

...
In the
In the
In the
In the
In the
In the
...

default constructor of C1
default constructor of C1
default constructor of IC2
destructor of IC2
destructor of C1
destructor of C1

//
//
//
//
//
//

for
for
for
for
for
for

C1 part of o1
o1._o
o1
o1
o1._o
C1 part of o1

Member Initialization List


Unless otherwise told all implicit constructor calls are made to the default constructor. This behavior can be changed by
appending member initialization list(s) to the function header of the constructor(s).
Example:
class IC3 : public C1 {
public:
IC3(void) { cout << "In the default constructor of IC3" << endl; }
IC3(int i) : C1(i), _o(i) {
cout << "In the IC3 constructor taking an int" << endl;
} // end of constructor(int)
~IC3(void) { cout << "In the destructor of IC3" << endl; }
private:
C1 _o;
}; // end of class IC3
...
{
IC3 o1(5);
...;
}
...

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

45 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

For pedagogical purposes, line 4 of the above fragment can be seen as the following. However, since it replaces two sequences
of "an initialization followed by an assignment" with two initializations, using a member initialization list is a more efficient
choice. This is becauseeven when you don't have a member initialization listconstructor(s) of the sub-objects are called
before that of the composite object, which means the two lines in the following fragment are actually assignments, not
initializations.[9] Before> they get executed each sub-object will have already been initialized using the default constructor of
C1.
IC3(int i) {
_c1_part = C1(1);
_o = C1(1);

...
In the
In the
In the
In the
In the
In the
...

C1 constructor taking an int


C1 constructor taking an int
IC3 constructor taking an int
destructor of IC3
destructor of C1
destructor of C1

//
//
//
//
//
//

for
for
for
for
for
for

C1 part of o1
o1._o
o1
o1
o1._o
C1 part of o1

Multiple Inheritance
Building on our informal definition of inheritance as compiler-managed composition we can treat objects of multiply inheriting
classes as being composed of more than one sub-object. Therefore, for pedagogical purposes, we can accordingly consider line
1 of the following fragment as below.
class IC4 {
public:
C1 _c1_part;
C2 _c2_part;
Example:

1.
class IC4 : public C1, public C2 {
2.
public:
3.
IC4(void) { cout << "In the default constructor of IC4" << endl; }
4.
~IC4(void) { cout << "In the destructor of IC4" << endl; }
5.
}; // end of class IC4
6.

7.
...
8.
{
9.
IC4 o1;
10.
...;
11.
}
12.
...

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

46 of 48

...
In the
In the
In the
In the
In the
In the
In the
In the
...

default constructor
default constructor
default constructor
default constructor
destructor of IC4
destructor of C2
destructor of C1
destructor of C1

of
of
of
of

C1
C1
C2
IC4

//
//
//
//
//
//
//
//

for
for
for
for
for
for
for
for

C1
_o
C2
o1
o1
C2
_o
C1

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

part of o1
of the C2 part of o1
part of o1

part of o1
of the C2 part of o1
part of o1

Restating the Formula: Inheritance is Compiler-Managed Composition


What follows are three pairs of equivalent class definitions meant to provide an insight into what the compiler accomplishes
behind the scenes. While perusing through the code keep in mind that code given in the right column reflects only what the
compiler does, not how it does it.
class SC1 {
...;
public:
...;
void SC1_f1(...);
void SC1_f2(...);
...;
}; // end of class SC1
Public inheritance enables use of base class interfaces through that of the derived class. No effort is required on the
programmer side, all is taken care of by the compiler. If for some reason you want to do it without inheritance, you must
explicitly expose the functions of the base class and delegate calls to these functions to the corresponding functions in the base
class. With private inheritance and selective exposition of the base class interface, this extra burden is lessened for C++
programmers.

class DC1 : public SC1 {


...;
public:
...;
// void SC1_f1(...);
// void SC1_f2(...);
... DC1_f1(...);
... DC1_f2(...);
...;
}; // end of class DC1

class CC1 {
public: // public!!!
SC1 _o; // SC1 part
private:
...;
public:
...;
void SC1_f1(...) { _o.SC1_f1(...); }
void SC1_f2(...) { _o.SC1_f2(...); }
... CC1_f1(...);
... CC1_f2(...);
...;
}; // end of class CC1

Private derivation means the functionality in the base class is not visible through an object of the derived class. However, it is
still possible to utilize this functionality in implementing the functions found in the interface of the derived class.

class DC2 : private SC1 {


...;
public:
...;
... DC2_f1(...);
... DC2_f2(...);
... DC2_SC1_f1(...) {
...; this->SC1_f1(...); ...;
} // end of ... DC2_SC1_f1(...)
... DC2_SC1_f2(...) {
...; this->SC1f2(...); ...;

class CC2 {
SC1 _o;
...;
public:
...;
... CC2_f1(...);
... CC2_f2(...);
... CC2_SC1_f1(...) {
...; _o.SC1_f1(...); ...;
} // end of ... CC2_SC1_f1(...)
... CC2_SC1_f2(...) {

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

47 of 48

} // end of ... DC2_SC1_f2(...)


...;
}; // end of class DC2

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

...; _o.SC1_f2(...); ...;


} // end of ... CC2_SC1_f2(...)
...;
}; // end of class CC2

Sometimes a mixture of the two cases may be needed. That is; part of the functionality in the base class is visible and rest has
to to be hidden. This selective exposition can be accomplished by a combination of private inheritance and the using
declaration.

class DC3 : private SC1 {


...;
public:
using SC1::SC1_f2;
...;
... DC3_f1(...);
... DC3_f2(...);
... DC3_SC1_f1(...) {
...; this->SC1_f1(...); ...;
} // end of ... DC3_SC1_f1(...)
...;
}; // end of class DC3

class CC3 {
SC1 _o;
...;
public:
void CC3_SC1_f2(...) {
_o.SC1_f2(...);
} // end of void CC3_SC1_f2(...)
...;
... CC3_f1(...);
... CC3_f2(...);
... CC3_SC1_f1(...) {
...; _o.SC1_f1(...); ...;
} // end of ... CC3_SC1_f1(...)
...;
}; // end of class CC3

Virtual Inheritance
Object of a virtual base class is always constructed prior to the objects of non-virtual classes. It should be kept in mind that
"virtualness" is actually the property of the derivation, not that of the base class itself.
Example:
class VC2 : public virtual C1 {
public:
VC2(void) { cout << "In the default constructor of VC2" << endl; }
~VC2(void) { cout << "In the destructor of VC2" << endl; }
}; // end of class VC2
...
{
VC2 o;
...;
}
...

...
In the
In the
In the
In the
...

default constructor of C1
default constructor of VC2
destructor of VC2
destructor of C1

Example:
class VC4 : public VC2, public VC3 {
public:
VC2(void) { cout << "In the default constructor of VC2" << endl; }
~VC2(void) { cout << "In the destructor of VC2" << endl; }
}; // end of class VC2

20-11-2014 13:08

Programming Language Concepts Using C and C++/Object Orientation a...

48 of 48

http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...

...
{
VC4 o;
...;
}
...

...
In the
In the
In the
In the
In the
In the
In the
In the
...

default constructor
default constructor
default constructor
default constructor
destructor of VC4
destructor of VC3
destructor of VC2
destructor of C1

of
of
of
of

C1
VC2
VC3
VC4

Notes
1. One other option would be to make use of the code generation aspect of Java annotations.
2. The only thing that lacks in this picture is garbage-collection. A partial solution to this will be provided by means of
smart pointers, which will be introduced in the implementation part.
3. Reuse is usually taken to be code reuse. However, analysis and design documents, program code, test cases and test
code are all candidates for reuse. As a matter of fact, reuse of artifacts from the early stages of the software production
process has a larger impact on productivity.
4. We can achieve the same result by defining the common message protocol in an interface and get classes to implement
this interface.
5. Notice it is once again dynamic dispatch that makes the magic work. It is not known which method the compare
message will be dispatched to until the program is run and line 6 is executed. Use of pointer-to-function in the equivalent
C code of Callback section in the Sample C Programs chapter is a testimony to this.
6. Unlike other contexts where the word "polymorphism" passes, the function callby matching the arguments with the
signature of the functionis resolved at compile- or link-time and therefore statically dispatched.
7. For instance, some architectures do not allow the goto instruction into the body of another function.
8. Destructor invocations of dynamically allocated objects take place at the point of the relevant application of delete.
9. This belies our definition of a constructor, which states that a constructor is used to initialize an object. As far as C++
is concerned, what takes place in a constructor is assignment; initialization is made using the member initialization list.

Retrieved from "http://en.wikibooks.org/w/index.php?title=Programming_Language_Concepts_Using_C_and_C%2B%2B


/Object_Orientation_and_Inheritance_in_C%2B%2B&oldid=2718898"

This page was last modified on 28 October 2014, at 12:46.


Text is available under the Creative Commons Attribution-ShareAlike License.; additional terms may apply. By using this
site, you agree to the Terms of Use and Privacy Policy.

20-11-2014 13:08

Você também pode gostar