You are on page 1of 21

Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

yet another insignificant programming notes... | HOME

TABLE OF CONTENTS (HIDE)


1. Revision on Classes and Objects

C++ Programming Language 2. Inheritance


2.1 Terminology
2.2 Example: Superclass Point and subclass

OOP Part 2 2.3 Example: Point and MovablePoint


3. Polymorphism
3.1 Subs�tu�on
3.2 Polymorphism
3.3 Pure Virtual Func�on and Abstract Superclass
3.4 Example: Shape and its Subclasses
1. Revision on Classes and Objects 3.5 Dynamic Binding vs. Sta�c Binding
4. More On OOP
Let us revise the basics of OOP with an example of modeling 2D
4.1 const Objects and const Member Func�ons
points with integer coordinates (x, y) in a class called Point,
as shown in the class diagram. 4.2 const Data Member
4.3 "friend" Func�on and "friend
4.4 The static Class Members
5. More On Inheritance
5.1 Mul�ple Inheritance
5.2 Virtual Inheritance
6. Object with Dynamically Allocated Data Members
6.1 Implicitly-generated Special member Func�ons
6.2 Dynamic Memory Alloca�on of Object Data Member

Header File: Point.h


1 /* The Point class Header file (Point.h) */
2 #ifndef POINT_H
3 #define POINT_H
4
5 class Point {
6 private:
7 int x, y; // Private data members
8
9 public:
10 Point(int x = 0, int y = 0); // Constructor with default arguments
11 int getX() const; // Getter
12 void setX(int x); // Setter
13 int getY() const;
14 void setY(int y);
15 void setXY(int x, int y);
16 void print() const;
17 };
18
19 #endif

Implementation File: Point.cpp


1 /* The Point class Implementation file (Point.cpp) */
2 #include "Point.h"
3 #include <iostream>
4 using namespace std;
5
6 // Constructor - The default values are specified in the declaration
7 Point::Point(int x, int y) : x(x), y(y) { }
8
9 // Getters
10 int Point::getX() const { return x; }
11 int Point::getY() const { return y; }
12
13 // Setters
14 void Point::setX(int x) { this->x = x; }
15 void Point::setY(int y) { this->y = y; }
16
17 // Public Functions

1 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

18 void Point::setXY(int x, int y) { this->x = x; this->y = y; }


19
20 void Point::print() const {
21 cout << "Point @ (" << x << "," << y << ")";
22 }

A Test Driver: TestPoint.cpp


1 /* A test driver program (TestPoint.cpp) */
2 #include "Point.h"
3 #include <iostream>
4 using namespace std;
5
6 int main() {
7 // Instances (Objects)
8 Point p1; // Invoke default constructor
9 // OR Point p1 = Point(); NOT Point p1();
10 Point p2(2, 2); // Invoke constructor
11 // OR Point p2 = Point(2, 2);
12 p1.print(); // Point @ (0,0)
13 cout << endl;
14 p2.print(); // Point @ (2,2)
15 cout << endl;
16
17 // Object Pointers with dynamic allocation
18 Point * ptrP3, * ptrP4; // Declare two Point pointers
19 ptrP3 = new Point(); // Dynamically allocate storage via new
20 // with default constructor
21 ptrP4 = new Point(4, 4);
22 ptrP3->print(); // Point @ (0,0)
23 // prtPoint1->print() is the same as (*ptrP3).print()
24 cout << endl;
25 ptrP4->print(); // Point @ (4,4)
26 cout << endl;
27
28 delete ptrP3; // Remove storage via delete
29 delete ptrP4;
30
31 // Object Reference (Alias)
32 Point & p5 = p2; // Reference (alias) to an existing object
33 p5.print(); // Point @ (2,2)
34 cout << endl;
35
36 /********************
37 * ARRAYS *
38 ********************/
39
40 // Array of Objects - Static Memory Allocation
41 Point ptsArray1[2]; // Array of Point objects
42 // Use default constructor for all elements of the array
43 ptsArray1[0].print(); // Point @ (0,0)
44 cout << endl;
45 ptsArray1[1].setXY(11, 11);
46 (ptsArray1 + 1)->print(); // Point @ (11,11)
47 // same as ptsArray1[1].print()
48 cout << endl;
49
50 Point ptsArray2[3] = {Point(21, 21), Point(22, 22), Point()};
51 // Initialize array elements via constructor
52 ptsArray2[0].print(); // Point @ (21,21)
53 cout << endl;
54 (ptsArray2 + 2)->print(); // Point @ (0,0)
55 // same as ptsArray2[2].print()
56 cout << endl;
57
58 // Array of Object Pointers - Need to allocate elements dynamically
59 Point * ptrPtsArray3 = new Point[2];
60 ptrPtsArray3[0].setXY(31, 31);
61 ptrPtsArray3->print(); // Point @ (31,31)
62 // same as ptrPtsArray3[0].print()
63 cout << endl;
64 (ptrPtsArray3 + 1)->setXY(32, 32); // same as ptrPtsArray3[1].setXY(32, 32)
65 ptrPtsArray3[1].print(); // Point @ (32,32)
66 cout << endl;
67
68 delete[] ptrPtsArray3; // Free storage

2 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

69
70 // C++ does not support Array of References
71 // Point & pts[2] = {p1, p2}; // error: declaration of 'pts' as array of references
72 }

Classes: A class is a abstract or user-defined data type, contrast to built-in fundamental types such as int or double . A class is represented as a three-
compartment box: name, date members (or variables or a�ributes) and member func�ons (or methods, or opera�ons). The data member and member
func�ons are collec�vely called class members. The syntax of defining a class consists of two sec�ons: class declara�on and class implementa�on. Class
declara�on is further divided into two sec�ons: private and public sec�ons. Class implementa�on contains member func�on defini�ons.

// Class Declaration Section - in Header file


class ClassName {
private:
private-data-members
private-member-functions
public:
public-data-members
public-member-functions
}; // Class declaration must end with a semi-colon

// Class Implementation Section - in the Implementation file


member-function-definitions

Objects (or instances): An object (or instance) is a concrete realiza�on of a class. For example, Point is a class, we can create instances (objects) p1, p2,
p3, belonging to the class Point. You can invoke the constructor implicitly or explicitly as follows:

// Invoke the constructor implicitly


Point p2(1, 2);
Point p3; // No empty brackets for default constructor

// Invoke the constructor explicitly


Point p5 = Point(1, 2);
Point p6 = Point(); // Need empty brackets for default constructor

There are a few ways to use a class to create instances, as shown in the above test driver program:
1. Construct instances (or objects) via constructors, either implicitly or explicitly.
2. Declare object pointers, and construct the objects dynamically via new operator.
3. Declare object references, and ini�alized to an exis�ng object, or received as func�on reference argument.
4. Array of objects, or Array of object pointers (dynamically allocated via new[] operator).

Data Members: Similar to normal variables, but having so-called class scope such as private or public. The syntax of declaring a data member is the
same as declaring a normal variable:

type variableName;

A data member cannot be ini�alized (except const sta�c data variables) in C++03.

Member Functions: Again, similar to normal func�ons, but having class scope. The syntax of declaring a member func�on is the same as normal func�on:

returnType functionName(parameter-type-list);

Constant Member Functions: A const member func�on, iden�fied by a const keyword at the end of the member func�on's header, cannot modifies
any data member of this object. For example,

int getX() const;


// This function cannot modify any data member

Implementing Member Functions: Member func�ons are usually implemented outside class declara�on. You need to use the scope resolu�on operator
:: to iden�fy functionName as a member of a par�cular ClassName.

returnType ClassName::functionName(parameter-list) {
function-body;
}

public vs. private Access Specifier: private members are accessible by the member func�ons of this class only. public members are
accessible everywhere. For example, if p1 is an instance of Point, p1.getX() is allowed outside the class defini�on (such as main()) as getX() is
public. However, p1.x is not allowed in main(), as x is declared private.

Constructor: A constructor is a special func�on having the "same name" as the classname, with no return-type. As the name implied, constructor is called
each �me when an instance is declared (or constructed). Constructor is used for ini�alizing the data members of the instances created. The syntax is:

// Declare constructor inside the class declaration


class ClassName {
ClassName(parameter-list); // prototype only
}

// Constructor implementation - identified via scope resolution operator


ClassName::ClassName(parameter-list) {
function-body;

3 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

Member Initializer List : used to ini�alize data members in the constructor. For example,

Point::Point(int x, int y) : x(x), y(y) {


// The body runs after the member initializer list
}

The member ini�alizer list is placed a�er the func�on parameter list, separated by a colon. For fundamental-type data members (e.g., int, double), x(x) is
the same as this->x = x. For object data members, the copy constructor will be invoked for each of the object. The func�on body will be executed a�er the
member ini�alizer list, which is empty in this case.

Alterna�vely, you could ini�alize the data members inside the constructor's body:

Point::Point(int x, int y) {
this->x = x;
this->y = y;
}

where this->x refers to the data member x; and x refer to the func�on parameter x.

Another alterna�ve that avoids naming conflict, but is hard to read:

Point::Point(int x_, int y_) {


x = x_;
y = y_;
}

Default Constructor: The default constructor refers to the constructor that takes no parameter - either it has no parameter or all parameters have their
default value (e.g., the above Point's constructor). If no constructor is defined in the class, the compiler inserts a default constructor that takes no argument
and does nothing (i.e., ClassName::ClassName() {}). However, if you define one (or more) constructors, compiler will not insert the default constructor.
You need to define your default constructor, if desired.

Function Overloading: A func�on (including constructor) can have many versions, differen�ated by its parameter list (number, types and orders of
parameters). Caller can choose to invoke a par�cular version by matching the parameter list.

Function Default Argument: In C++, default values can be assigned to trailing func�on's parameters. If the caller does not supply these arguments,
compiler would insert the default value accordingly. For example,

// The Point class declaration (in header file "Point.h")


class Point {
......
Point(int = 0, int = 0);
}

// The Point class implementation (Point.cpp)


Point::Point(int x, int y) : x(x), y(y) { }

// Test Driver (TestPoint.cpp)


Point p1; // (0, 0)
Point p2(4); // (4, 0)
Point p3(5, 6); // (5, 6)

Notes:
The default value can only be assigned to the trailing arguments.
The default value shall be specified in the class declara�on. I shall NOT be specified in the func�on implementa�on.
Default argument is applicable to all func�ons, including constructor func�on.

Constructing Instances: As men�oned, instances (or objects) are concrete realiza�ons of a class. A constructor would be invoked when declaring an
instance to ini�alize the instance. The syntax to declare an instance is:

ClassName instanceName; // Invoke default constructor


ClassName instanceName(constructor-parameter-list); // Invoke the constructor with matching parameter list

Public Getters and Setters for Private Variables: Member variables are usually declared private to prevent direct access (called data hiding).
Instead, public ge�er and se�er are defined to retrieve (get) and modify (set) the private member variable. The conven�on is as follows:

class ClassName {
private:
type xxx; // private variable
public:
type getXxx() const; // public getter
void setXxx(type); // public setter (use const type if type is a class)
}

// Getter Implementation
type ClassName::getXxx() const { return xxx; }

// Setter Implementation
void ClassName::setXxx(type x) { xxx = x; }
// OR

4 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

void ClassName::setXxx(type xxx) { this->xxx = xxx; }

Implementing Member Functions in Class Declaration: You can include the func�on's implementa�on inside the class declara�on, as follows:

1 /* Point with integer coords with inline functions (PointInline.h) */


2 #include <iostream>
3 using namespace std;
4
5 // Class Declaration with inline implementation
6 class Point {
7 private:
8 int x, y;
9
10 public:
11 Point(int x = 0, int y = 0) : x(x), y(y) { }
12 int getX() const { return x; }
13 int getY() const { return y; }
14 void setX(int x) { this->x = x; }
15 void setY(int y) { this->y = y; }
16 void setXY(int x, int y) { this->x = x; this->y = y; }
17 void print() const {
18 cout << "Point @ (" << x << "," << y << ")";
19 }
20 };

Func�ons that implemented inside the class declara�on are automa�cally treated as inline func�ons. That is, they will be expanded in place by the compiler (if
the compiler chooses to do so), instead of performing a more expensive func�on call.

Dot (.) Member Selection Operator: Dot operator (.) is used to access public class members, in the form of instanceName.memberName, e.g.,
point1.getX(), point2.print().

Arrow (->) Member Selection Operator: Arrow operator (->) is used with object pointer. Suppose *pObj is a pointer to an object, instead of using
(*pObj).member to select a member, it is more convenient to use the arrow nota�on, in the form of pObj->member.

Memberwise Assignment: The assignment operator (=) can be used to assign one object into another object (of the same class), in memberwise manner.
For example,

Point p1(4, 5);


Point p2 = p1; // Also (4, 5)

(Note: The compiler automa�cally generates an implicit assignment operator operator=(), which performs memberwise copying.)

Passing Objects into Function: Objects are passed by value into func�on. A copy is created (via memberwise assignment) and passed into the func�on.
The caller's copy cannot be modified inside the func�on.

Pass by reference can be archived by passing an object pointer or an object reference.

Object Pointer and Dynamic Allocation: To allocate an instance of a class dynamically, define an object pointer and use new operator to allocate the
storage. The new operator return a pointer poin�ng to the storage allocated. You could use arrow operator -> to access the members with object pointer in the
form of pObj->member (same as (*pObj).member). You need to use the delete operator to free the allocated storage.

Similarly, you can use new[] and delete[] to dynamically allocate array of objects.

1 /* A test driver for Dynamic Allocation (TestDynamicAllocation.cpp) */


2 #include "Point.h"
3 #include <iostream>
4 using namespace std;
5
6 int main() {
7 // Object Pointers with dynamic allocation
8 Point * ptrP1;
9 ptrP1 = new Point(); // Dynamically allocate storage via new
10 // with default constructor
11 ptrP1->print(); // Point @ (0,0)
12 cout << endl;
13
14 Point * ptrP2 = new Point(2, 2); // with constructor
15 ptrP2->print(); // Point @ (0,0)
16 cout << endl;
17
18 delete ptrP1; // Remove storage via delete
19 delete ptrP2;
20
21 // Array of Dynamic Objects
22 Point * ptrPtsArray = new Point[2];
23 ptrPtsArray[1].setXY(31, 31);
24 ptrPtsArray[1].print(); // Point @ (31,31)
25 cout << endl;
26 delete[] ptrPtsArray; // Free storage for entire array

5 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

27 }

Object Reference: You can create an object reference to an exis�ng object. A reference is similar to a pointer, but it is a name constant that is referenced
and de-reference implicitly by the compiler.

Point p1(1, 2); // Construct an object


Point & p2 = p1; // p2 is an alias to p1

Object references are useful in passing object into func�on by reference (by default, objects are passed into func�on by value).

Destructor: Similar to constructor, a destructor has the same name as the classname, but preceded with a �lde (~). The destructor is called automa�cally
when the instance expires. It has no-argument, no return type. There can be only one destructor in a class. If there is no destructor defined, the compiler
supplies a destructor that does nothing.

Destructor shall do the clean up, in par�cular, the dynamically allocated memory. If the constructor uses new to dynamically allocate storage, the destructor
should delete them.

2. Inheritance

2.1 Terminology
Superclass (Base Class) & Subclass (Derived Class): In OOP, we could organize classes in hierarchy to avoid redundancy. We can extend a subclass
(or derived class) from a superclass (or base class). The subclass inherits the members of the superclass, known as inheritance.

The syntax for deriving a subclass from a superclass is as follows:

class SubclassName : inheritance-access-specifier SuperclassName {


......
};

The subclass inherits all the members of the superclass. The subclass shall define its own constructor(s). It may define addi�onal members (data or func�ons).

Access Specifier: C++ supports three access specifier: private, public and protected. A private member is accessible within the class by
member func�ons and by friends of that class. A public member is accessible by all. A protected member can be accessed by itself and its friend, as well
as its subclasses and their friends.

To access a superclass's member explicitly, you could use the scope resolu�on operator in the form of superclassName::memberName.

Inheritance Access Specifier: It specifies the type of inheritance: public, private or protected. The most commonly used is public-
inheritance. In this case, the inherited members in the subclass have the same visibility as the superclass. There is no further restric�on. In other words,
public members in the superclass becomes public members in the derived class; protected members in the base class become protected member
in the derived class. In this case, every subclass object is also a superclass object (known as is-a rela�onship), and can be subs�tuted for a superclass reference.

private- and protected-inheritance, which are rarely used, may further restrict the access of the inherited members (equal or lower than the access in
superclass). In protected-inheritance, public and protected members in the base class become protected members in the derived class. In
private-inheritance, public and protected members in the base class become private member in the derived class. Take note the private
members in the superclass cannot be directly accessed in the subclass; while protected members can be directly accessed.

2.2 Example: Superclass Point and subclass MovablePoint


[TODO] Descrip�on

6 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

Superclass Point.h, Point.cpp


No change.

Subclass Header: MovablePoint.h


1 /* Header for Moving 3D Points with int coords (MovablePoint.h) */
2 #ifndef MOVING_POINT_H
3 #define MOVING_POINT_H
4
5 #include "Point.h" // Include header of the base class
6
7 class MovablePoint : public Point { // MovablePoint is a subclass of Point
8 private:
9 int xSpeed, ySpeed;
10
11 public:
12 MovablePoint(int x, int y, int xSpeed = 0, int ySpeed = 0);
13 int getXSpeed() const;
14 int getYSpeed() const;
15 void setXSpeed(int xSpeed);
16 void setYSpeed(int ySpeed);
17 void move();
18 void print() const;
19 };
20
21 #endif

Subclass Implementation: MovablePoint.cpp


1 /* Implementation for Moving 3D Points with int coords (MovablePoint.cpp) */
2 #include <iostream>
3 #include "MovablePoint.h" // Include header containing the class declaration
4 using namespace std;
5
6 MovablePoint::MovablePoint(int x, int y, int xSpeed, int ySpeed)
7 : Point(x, y), xSpeed(xSpeed), ySpeed(ySpeed) { }
8
9 // Getters
10 int MovablePoint::getXSpeed() const { return xSpeed; }
11 int MovablePoint::getYSpeed() const { return ySpeed; }
12
13 // Setters
14 void MovablePoint::setXSpeed(int xs) { xSpeed = xs; }
15 void MovablePoint::setYSpeed(int ys) { ySpeed = ys; }
16
17 // Functions
18 void MovablePoint::print() const {
19 cout << "Movable";
20 Point::print(); // Invoke base class function via scope resolution operator
21 cout << " Speed=" << "(" << xSpeed << "," << ySpeed << ")";
22 }
23
24 void MovablePoint::move() {
25 // Subclass cannot access private member of the superclass directly
26 // Need to go thru the public interface
27 Point::setX(Point::getX() + xSpeed);
28 Point::setY(Point::getY() + ySpeed);
29 }

Notes:
When the subclass construct its instance, it must first construct a superclass object, which it inherited.
The subclass does not have direct access to superclass' private members x, and y. To ini�alize these inherited members, the subclass constructor
invokes the superclass constructor, which is public, in the member ini�alizer list.
You need to use the member ini�alizer list (: Point(x, y)) to invoke the superclass Point's constructor to ini�alize the superclass, before ini�alizing
the subclass. Object data member can only be ini�alized via member ini�alizer list.
If you did not explicitly invoke the superclass' constructor, the compile implicitly invoke the superclass' default constructor to construct a superclass object.
To use the superclass members, use scope resolu�on operator in the form of SuperclassName::memberName. For example, Point::print(),

7 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

Point::getX().

A Test Driver: TestMovablePoint.cpp


1 /* Test Driver Program for MovablePoint (TestMovablePoint.cpp) */
2 #include <iostream>
3 #include "MovablePoint.h" // included "Point.h"
4 using namespace std;
5
6 int main() {
7 Point p1(4, 5); // superclass
8 p1.print(); // Point @ (4,5)
9 cout << endl;
10
11 MovablePoint mp1(11, 22); // subclass, default speed
12 mp1.print(); // MovablePoint @ (11,22) Speed=(0,0)
13 cout << endl;
14 mp1.setXSpeed(8);
15 mp1.move();
16 mp1.print(); // MovablePoint @ (19,22) Speed=(8,0)
17 cout << endl;
18
19 MovablePoint mp2(11, 22, 33, 44);
20 mp2.print(); // MovablePoint @ (11,22) Speed=(33,44)
21 cout << endl;
22 mp2.move();
23 mp2.print(); // MovablePoint @ (44,66) Speed=(33,44)
24 cout << endl;
25 }

To compile/link (aka build) the program:

// Compile Point.cpp (with Point.h) into object code Point.o


// -c option specifies compile-only
> g++ -c Point.cpp

// Compile MovablePoint.cpp (with MovablePoint.h) into object code MovablePoint.o


// Does not require Point.o for compilation (but needed in linkage)
> g++ -c MovablePoint.cpp

// Compile and link TestMovablePoint.cpp with object files into executable file
> g++ -o TestMovablePoint.exe TestMovablePoint.cpp MovablePoint.o Point.o

2.3 Example: Point and MovablePoint with protected Data Members


Recall that a private data member in the superclass is not accessible in the subclass. For example, in the func�on move() of MovablePoint, you cannot
reference x of superclass Point directly.

void MovablePoint::move() {
x += xSpeed; // error: 'int Point::x' is private
// Point::setX(Point::getX() + xSpeed);
Point::setY(Point::getY() + ySpeed);
}

However, if we make x protected instead of private, the subclass can access x directly.

// Superclass Point
class Point {
protected:
int x, y;
......
};

// Subclass MovablePoint
class MovablePoint : public Point {
......
}

void MovablePoint::move() {
x += xSpeed;
y += ySpeed;
}

[TODO] more examples

3. Polymorphism

8 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

Polymorphism works on object pointers and references using so-called dynamic binding at run-�me. It does not work on regular objects, which uses sta�c
binding during the compile-�me.

We typically allocate object dynamically via the new operator and manipulate the return pointer in polymorphism. Recall that we can dynamically allocate
objects for the Point and MovablePoint classes as follows:

Point * p1 = new Point(1, 2); // Allocate a Point instance dynamically


p1->print(); // Invoke function via ->
delete p1; // Free storage

MovablePoint * mp1 = new MovablePoint(1, 2, 3, 4);


mp1->print();
mp1->move();
delete mp1;

3.1 Substitution
A subclass instance inherits all the proper�es of the superclass, in the case of public-inheritance. It can do whatever a superclass instance can do. This is
known as a "is-a" rela�onship. Hence, you can subs�tute a subclass instance to a superclass reference.

Example
Using the above example of superclass Point and subclass MovablePoint,

1 /* Test Substituting a subclass instance to a superclass reference


2 (TestSubstitution.cpp) */
3 #include <iostream>
4 #include "MovablePoint.h" // included "Point.h"
5 using namespace std;
6
7 int main() {
8 // Substitute a subclass instance to a superclass reference
9
10 // Using Object Pointer
11 Point * ptrP1 = new MovablePoint(11, 12, 13, 14); // upcast
12 ptrP1->print(); // Point @ (11,12) - Run superclass version!!
13 cout << endl;
14 // ptrP1->move(); // error: 'class Point' has no member named 'move'
15 delete ptrP1;
16
17 // Using Object Reference
18 MovablePoint mp2(21, 22, 23, 24);
19 Point & p2 = mp2; // upcast
20 p2.print(); // Point @ (21,22) - Run superclass version!!
21 cout << endl;
22 // p2.move(); // error: 'class Point' has no member named 'move'
23
24 // Using object with explicit constructor
25 Point p3 = MovablePoint(31, 32, 33, 34); // upcast
26 p3.print(); // Point @ (31,32) - Run superclass version!!
27 cout << endl;
28 // p3.move(); // error: 'class Point' has no member named 'move'
29 }

Once subs�tuted, it can invoke all the func�ons defined in the superclass, but CANNOT invoke func�ons defined in the subclass. This is because the reference is
a superclass reference, which is not aware of subclass members.

3.2 Polymorphism
1. A subclass instance can be subs�tuted for a superclass reference.
2. Once subs�tuted, only the superclass' func�ons can be called, no the subclass'.
3. If the subclass overrides a superclass func�on. We wish to run the overridden version in the subclass, instead of the superclass' version (as in the previous
example).

Virtual Functions: To implement polymorphism, we need to use the keyword virtual for func�ons that are meant to be polymorphic. In this case, if a
superclass pointer is aiming at a subclass objects, and invoke a virtual func�on that is overridden by the subclass, the subclass version will be invoked,
instead of the superclass version. For example,

// Point.h
class Point {
......
virtual void print() const;
}

1 /* Test Substituting a subclass instance to a superclass reference.


2 (TestSubstitution.cpp) */

9 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

3 #include <iostream>
4 #include "MovablePoint.h" // included "Point.h"
5 using namespace std;
6
7 int main() {
8 // Substitute a subclass instance to a superclass reference
9
10 // Using Object Pointer
11 Point * ptrP1 = new MovablePoint(11, 12, 13, 14); // upcast
12 ptrP1->print(); // MovablePoint @ (11,12) Speed=(13,14)
13 // - Run subclass version!!
14 cout << endl;
15 delete ptrP1;
16
17 // Using Object Reference
18 MovablePoint mp2(21, 22, 23, 24);
19 Point & p2 = mp2; // upcast
20 p2.print(); // MovablePoint @ (21,22) Speed=(23,24)
21 // - Run subclass version!!
22 cout << endl;
23
24 // Using object with explicit constructor
25 Point p3 = MovablePoint(31, 32, 33, 34); // upcast
26 p3.print(); // Point @ (31,32) - Run superclass version!!
27 cout << endl;
28 }

The keyword virtual determines which method is used if the method is invoked by a pointer (or reference). Without virtual, the program chooses the
method based on the pointer type; with virtual, the program chooses the method based on the type of the object pointed-to.

Take note that virtual func�ons work on object pointers (and references), but not on regular objects.

If the subclass override a method inherited from its superclass, the usual prac�ce is to declare the superclass method as virtual. In this case, the program
will choose the method based on the type of the object, instead of the type of pointer.

For non-virtual func�on, the compiler selects the func�on that will be invoked at compiled-�me (known as sta�c binding). For virtual func�ons, the selec�on is
delayed un�l the run�me. The func�on selected depends on the actual type that invokes the func�on (known as dynamic binding or late binding).

Using Polymorphism:
1. Create instances of concrete subclass.
2. Declare superclass (possibly abstract) pointers (or references).
3. Aim the superclass pointers to the subclass instances.
4. Invoke virtual func�on, with implementa�on provided by subclass.

Using Virtual Functions


Using keyword virtual on a superclass func�on makes the func�on virtual for the superclass, as well as ALL its subclasses.
If a virtual func�on is invoked using a pointer (or reference), the program uses the method defined for the object type instead of the pointer type. This is
called dynamic binding or late binding, contrast to sta�c binding during the compile �me.
It is recommended that func�ons to be overridden in the subclass be declared virtual in the superclass.
Constructor can't be virtual, because it is not inherited. Subclass defines its own constructor, which invokes the superclass constructor to ini�alize the
inherited data members.
Destructor should be declared virtual, if a class is to to be used as a superclass, so that the appropriate object destructor is invoked to free the dynamically
allocated memory in the subclass, if any.
Friends can't be virtual, as friends are not class member and are not inherited.
If you override func�on in the subclass, the overridden func�on shall have the same parameter list as the superclass' version.

Upcasting and Downcasting


Normally, C++ does not allow you to assign an address of one type to pointer (or reference) of another type. For example,

int i = 8;
double * ptr1 = &i;
// error: cannot convert 'int*' to 'double*' in initializatin
double & d = i;
// error: invalid initialization of reference of type 'double&' from expression of type 'int'

However, a pointer or reference of superclass can hold a subclass object without explicit type cast:

MovablePoint mp(.....);
Point * ptrP1 = &mp; // Okay - Implicit upcast
Point & p2 = mp; // Okay - Implicit upcast

Conver�ng a subclass to superclass reference or pointer is called upcas�ng. (Because in UML diagram, we o�en draw the superclass on top of the subclass, with
an arrow poin�ng up from the subclass to the superclass.) Upcas�ng is always allow for public-inheritance without the need for an explicit type cast, because

10 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

public-inheritance exhibits is-a rela�onship. A subclass object is a superclass object, because it inherits all the a�ributes and opera�ons from the superclass,
and can do whatever the superclass object can do.

The reverse opera�on, conver�ng a superclass reference or pointer to subclass, is called downcas�ng. Downcas�ng requires explicit type cast.

1 /* Test Up and Down Cast (TestCast.cpp) */


2 #include <iostream>
3 #include "MovablePoint.h" // included "Point.h"
4 using namespace std;
5
6 int main() {
7 // Object Pointer
8 Point * ptrP1 = new MovablePoint(11, 12, 13, 14);
9 // Upcast is always permissible and safe
10 ptrP1->print();
11
12 // MovablePoint * ptrMP1 = ptrP1; // error
13 MovablePoint * ptrMP1 = (MovablePoint *) ptrP1;
14 // Downcast requires explicit casting operator
15 delete ptrP1;
16 }

Operator dynamic_cast
C++ provides a new cas�ng operator called dynamic_cast<type>(value), which returns a null pointer if the type cast fails. For example,

MovablePoint * ptrMP1 = dynamic_cast<MovablePoint *>(ptrP1);

Operator typeid
The operator typeid returns a reference to an object of class type_info (in header <typeinfo>, which contains informa�on about the type of its operands.
You can use type_info's member func�on name() to get the type name. For example,

1 /* Test typeid operator, which return an object of type_info (TestTypeID.cpp) */


2 #include <iostream>
3 #include <typeinfo> // Need for typeid operator
4 #include "MovablePoint.h" // included "Point.h"
5 using namespace std;
6
7 int main() {
8 // Object Pointer
9 Point * ptrP1 = new MovablePoint(11, 12, 13, 14); // upcast
10 cout << typeid(*ptrP1).name() << endl; // 12MovablePoint
11
12 MovablePoint * ptrMP1 = dynamic_cast<MovablePoint *>(ptrP1);
13 cout << typeid(*ptrMP1).name() << endl; // 12MovablePoint
14 delete ptrP1;
15
16 Point p2;
17 cout << typeid(p2).name() << endl; // 5Point
18
19 MovablePoint mp2(1, 2, 3, 4);
20 cout << typeid(mp2).name() << endl; // 12MovablePoint
21 }

Program Notes:
The number in front of the name gives the length of the string.

3.3 Pure Virtual Function and Abstract Superclass


A pure virtual func�on is specified by placing "= 0" (called pure specifier) in its declara�on. For example,

virtual double getArea() = 0; // Pure virtual function, to be implemented by subclass

A pure virtual func�on usually has no implementa�on body, because the class does not know how to implement the body. A class containing one or more pure
virtual func�on is called an abstract class. You cannot create instances from an abstract class, because its defini�on may be incomplete.

Abstract class is meant to be a superclass. To use an abstract class, you need to derive a subclass, override and provide implementa�on to all the pure virtual
func�ons. You can then create instances from the concrete subclass.

C++ allows implementa�on for pure virtual func�on. In this case, the =0 simply make the class abstract. As the result, you cannot create instances.

3.4 Example: Shape and its Subclasses


[TODO] Descrip�on

11 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

Shape.h
1 /* Header for Shape class (Shape.h) */
2 #ifndef SHAPE_H
3 #define SHAPE_H
4
5 #include <string>
6 using namespace std;
7
8 class Shape {
9 private:
10 string color; // Private data member
11
12 public:
13 Shape(const string & color = "red"); // Constructor
14 string getColor() const; // Getter
15 void setColor(const string & color); // Setter
16 // Virtual function, run subclass version if overridden
17 virtual void print() const;
18 // Pure virtual, to be implemented by subclass
19 // You cannot create instance of Shape
20 virtual double getArea() const = 0;
21 };
22
23 #endif

Shape.cpp
1 /* Implementation for Shape class (Shape.cpp) */
2 #include "Shape.h"
3 #include <iostream>
4
5 // Constructor
6 Shape::Shape(const string & color) {
7 this->color = color;
8 }
9
10 // Getter
11 string Shape::getColor() const {
12 return color;
13 }
14
15 // Setter
16 void Shape::setColor(const string & color) {
17 this->color = color;
18 }
19
20 void Shape::print() const {
21 std::cout << "Shape of color=" << color;
22 }

Circle.h

12 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

1 /* Header for Circle (Circle.h) */


2 #ifndef CIRCLE_H
3 #define CIRCLE_H
4
5 #include "Shape.h"
6
7 // The class Circle is a subclass of Shape
8 class Circle : public Shape {
9 private:
10 int radius; // Private data member
11
12 public:
13 Circle(int radius = 1, const string & color = "red"); // Constructor
14 int getRadius() const; // Getter
15 void setRadius(int radius); // Setter
16 void print() const; // Override the virtual function
17 double getArea() const; // to implement virtual function
18 };
19
20 #endif

Circle.cpp
1 /* Implementation for Circle (Circle.cpp) */
2 #include "Circle.h"
3 #include <iostream>
4 #define PI 3.14159265
5
6 // Constructor
7 Circle::Circle(int radius, const string & color)
8 : Shape(color), radius(radius) { }
9
10 // Getters
11 int Circle::getRadius() const {
12 return radius;
13 }
14
15 // Setters
16 void Circle::setRadius(int radius) {
17 this->radius = radius;
18 }
19
20 void Circle::print() const {
21 std::cout << "Circle radius=" << radius << ", subclass of ";
22 Shape::print();
23 }
24
25 // Implement virtual function inherited for superclass Shape
26 double Circle::getArea() const {
27 return radius * radius * PI;
28 }

Rectangle.h
1 /* Header for Rectangle class (Rectangle.h) */
2 #ifndef RECTANGLE_H
3 #define RECTANGLE_H
4
5 #include "Shape.h"
6
7 // The class Rectangle is a subclass of Shape
8 class Rectangle : public Shape {
9 private:
10 int length;
11 int width;
12
13 public:
14 Rectangle(int length = 1, int width = 1, const string & color = "red");
15 int getLength() const;
16 void setLength(int length);
17 int getWidth() const;
18 void setWidth(int width);
19 void print() const; // Override the virtual function
20 double getArea() const; // to implement virtual function
21 };
22

13 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

23 #endif

Rectangle.cpp
1 /* Implementation for Rectangle (Rectangle.cpp) */
2 #include "Rectangle.h"
3 #include <iostream>
4
5 // Constructor
6 Rectangle::Rectangle(int length, int width, const string & color)
7 : Shape(color), length(length), width(width) { }
8
9 // Getters
10 int Rectangle::getLength() const {
11 return length;
12 }
13 int Rectangle::getWidth() const {
14 return width;
15 }
16
17 // Setters
18 void Rectangle::setLength(int length) {
19 this->length = length;
20 }
21 void Rectangle::setWidth(int width) {
22 this->width = width;
23 }
24
25 void Rectangle::print() const {
26 std::cout << "Rectangle length=" << length << " width=" << width << ", subclass of ";
27 Shape::print();
28 }
29
30 // Implement virtual function inherited from superclass Shape
31 double Rectangle::getArea() const {
32 return length * width;
33 }

Test Driver: TestShape.cpp


1 /* A test driver program for polymorphism (TestShape.cpp) */
2 #include "Circle.h"
3 #include "Rectangle.h"
4 #include <iostream>
5 using namespace std;
6
7 int main() {
8 // Circle object
9 Circle c1(5, "blue");
10 c1.print();
11 cout << endl;
12 cout << "area=" << c1.getArea() << endl;
13
14 // Rectangle object
15 Rectangle r1(5, 6, "green");
16 r1.print();
17 cout << endl;
18 cout << "area=" << r1.getArea() << endl;
19
20 // Shape s1; // Cannot create instance of abstract class Shape
21
22 // Polymorphism
23 Shape * s1, * s2; // Shape pointers
24
25 s1 = new Circle(6); // Dynamically allocate a subclass instance
26 s1->print(); // Run subclass version
27 cout << endl;
28 cout << "area=" << s1->getArea() << endl; // Run subclass version of getArea()
29
30 s2 = new Rectangle(7, 8); // Dynamically allocate a subclass instance
31 s2->print(); // Run subclass version
32 cout << endl;
33 cout << "area=" << s2->getArea() << endl; // Run subclass version of getArea()
34
35 delete s1;
36 delete s2;
37

14 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

38 // Shape s3 = Circle(6); // error: cannot allocate an object of abstract type 'Shape'


39
40 Circle c3(8);
41 Shape & s3 = c3; // Object reference
42 s3.print();
43 cout << endl;
44 cout << "area=" << s3.getArea() << endl;
45
46 Circle c4(9);
47 Shape * s4 = &c4; // Object pointer
48 s4->print();
49 cout << endl;
50 cout << "area=" << s4->getArea() << endl;
51 }

Circle radius=5, subclass of Shape of color=blue


area=78.5398
Rectangle length=5 width=6, subclass of Shape of color=green
area=30
Circle radius=6, subclass of Shape of color=red
area=113.097
Rectangle length=7 width=8, subclass of Shape of color=red
area=56
Circle radius=8, subclass of Shape of color=red
area=201.062
Circle radius=9, subclass of Shape of color=red
area=254.469

[TODO] Explana�on

3.5 Dynamic Binding vs. Static Binding


[TODO]

4. More On OOP

4.1 const Objects and const Member Functions


Constant Object: We can use const to specify that an object is not mutable. For example,

const Point p1; // Constant object


Point p2; // Non-constant object
// p1 = p2; // error: const object cannot be reassigned
p2 = p1; // okay

Constant Member Functions: We declare a member func�on constant by placing the keyword const a�er the parameter list. A const member
func�on cannot modify any member variable. For example,

int getX() const { return x; }


void print() const { cout << "(" << x << "," << y << ")" << endl; }

A constant member func�on cannot modify data members too. For example,

void setX(int x) const {


this->x = x; // ERROR!
}

The constructor and destructor cannot be made const, as they need to ini�alize data members. However, a const object can invoke non-const constructor.
The const property begins a�er construc�on.

A const object can invoke only const member functions


In C++, a const object can only invoke const member func�ons, and cannot invoke non-const member func�ons. On the other hand, a non-const object
can invoke both const and non-const member func�ons. For example,

1 /* Testing constant objects and constant member functions


2 (TestConstantObject.cpp) */
3 #include <iostream>
4 using namespace std;
5
6 class Point {
7 private:
8 int x, y;
9 public:
10 Point(int x = 0, int y = 0) : x(x), y(y) { }
11 int getX() const { return x; } // const function (cannot modify data)

15 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

12 int getY() const { return y; } // const function


13 void setX(int x) { this->x = x; } // non-const function
14 void setY(int y) { this->y = y; } // non-const function
15 void print() const { cout << "(" << x << "," << y << ")" << endl; } // const
16 };
17
18 int main() {
19 // non-const object can invoke const and non-const member functions
20 Point p1(5, 6); // non-const object
21 p1.setX(55);
22 p1.print(); // (55,6)
23
24 // const object can only invoke const member functions
25 const Point p2(7, 8); // const object
26 p2.print(); // (7,8)
27 // p2.setX(55); // error: const object cannot invoke non-const member function
28 }

Member Function Overloading with const


As an example, if you check the string class' func�on at() (which returns the character at the given posi�on), you will see two overloaded versions:

// C++'s std::string class


char & at (size_t pos);
const char & at (size_t pos) const;

A non-const string object will run the non-const version, which returns a non-const char reference. The return reference can be used as the lvalue to
modify the char, e.g., str.at(1) = 'x'. On the other hand, a const string object will invoke the const member func�on, which returns a const
char reference. A const reference cannot be used as lvalue. For example,

1 /* Test const overloading function (TestConstFn.cpp) */


2 #include <iostream>
3 #include <string>
4 using namespace std;
5
6 int main() {
7 const string s1("Apple"); // const object
8 string s2("Banana"); // non-const object
9
10 cout << s1.at(3) << endl; // run const version, non-mutable
11 s2.at(0) = 'A'; // run non-const version, mutable
12 cout << s2 << endl;
13
14 // s1.at(0) = 'B'; // error: assignment of read-only location
15 }

4.2 const Data Member


You can declare a data member const. A const data member cannot be modified by any member func�on. It can only be ini�alized by the constructor using
a special syntax called member ini�alizer list. For example,

1 /* Testing constant data members (TestConstantData.cpp) */


2 #include <iostream>
3 using namespace std;
4
5 class Point {
6 private:
7 int x;
8 const int y; // constant data member, cannot be modified by member functions
9 public:
10 Point(int x = 0, int y = 0) : x(x), y(y) { }
11 // init const data member via member initializer list
12 int getX() const { return x; }
13 int getY() const { return y; }
14 void setX(int x) { this->x = x; } // modify non-const data member x
15 // void setY(int y) { this->y = y; } // error: assignment of read-only member
16 void print() const { cout << "(" << x << "," << y << ")" << endl; } // const
17 };

A member ini�alizer list is placed a�er the parameter list, in the form of : data-member-name(value), .... Using this->y = y triggers a compila�on
error due to the assignment into const data member y.

For object data member, you can use the member ini�alizer list to trigger its constructor. member ini�alizer list is also use to invoke superclass constructor from
the subclass constructor.

4.3 "friend" Function and "friend" Class

16 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

friend Functions
A "friend" func�on of a class, marked by the keyword friend, is a func�on defined outside the class, yet its argument of that class has unrestricted access to
all the class members (private, protected and public data members and member func�ons).

For example,

1 /*
2 * Test Friend Function (TestFriend.cpp)
3 */
4 #include <iostream>
5 using namespace std;
6
7 class Point {
8 // A friend function defined outside this class, but its argument of
9 // this class can access all class members (including private members).
10 friend void set(Point & point, int x, int y); // prototype
11 private:
12 int x, y;
13 public:
14 Point(int x = 0, int y = 0) : x(x), y(y) { }
15 void print() const { cout << "(" << x << "," << y << ")" << endl; }
16 };
17
18 // Friend function is defined outside the class
19 void set(Point & point, int x, int y) {
20 point.x = x; // can access private data x and y
21 point.y = y;
22 }
23
24 int main() {
25 Point p1;
26 p1.print(); // (0, 0)
27 set(p1, 5, 6);
28 p1.print(); // (5, 6)
29 }

Notes:
A friend func�on is a regular func�on, NOT a member func�on of the class. Hence it is invoked without the dot operator in the form of set(p1, 5, 6),
instead of p1.set(5, 6) for a member func�on.
The above example is meant for illustra�on. This opera�on is be�er served by a member func�on void set(int x, int y), instead of friend
func�on.
The friend func�on prototype is provided inside the class declara�on. You do not need to provide another prototype outside the class declara�on, but
merely provide its implementa�on.
Friend func�ons can enhance the performance by directly accessing the private data members, elimina�ng the overhead of going thru the public member
func�ons.
Friend func�ons are neither public nor private, and it can be declared anywhere inside the class. As friends are part of the extended interface of the class,
you may group them together with the public func�ons.
Friend func�ons will not be inherited by the subclass. Friends can't be virtual, as friends are not class member.

friend Class
To declare all member func�ons of a class (says Class1) friend func�ons of another class (says Class2), declared "friend class Class1;" in
Class2.

Friends are not symmetric. That is, if Class1 is a friend of Class2, it does not imply that Class2 is a friend of Class1. Friends are also not transi�ve. That
is, if Class1 is a friend of Class2, and Class2 is a friend of Class3, it does not imply that Class1 is a friend of Class3.

Use friend with care. Incorrect use of friends may corrupt the concept of informa�on hiding and encapsula�on.

4.4 The static Class Members


A sta�c class member has only one copy, belonging to the class instead of the instances. All instances share the same storage for a static class member. A
static members is referenced via scope resolu�on operator in the form of ClassName::variableName or ClassName::functionName().
It can be used to implement "global" class variables and func�ons, that can be used without crea�ng instances of a class.
It can also be used to share informa�on among all instances, e.g., a count on the number of instances created.

A static func�on can only access static variables, and cannot access non-static variables. A sta�c variable/func�on can be referenced without any
instan�a�on (i.e., no instance is created).

Example (static Class Member) : This example uses a static data member to keep track of the number of instances created.

Point.h

17 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

1 /* Header for Point class (Point.h) */


2 #ifndef POINT_H
3 #define POINT_H
4 class Point {
5 private:
6 int x, y; // x and y coords
7 static int count; // Count the number of instances created
8
9 public:
10 Point(int x = 0, int y = 0);
11 void print();
12 static int getCount(); // A static function
13 };
14 #endif

Point.cpp
1 /* Implementation for Point class (Point.cpp) */
2 #include <iostream>
3 #include "Point.h"
4 using namespace std;
5
6 // Initialize the static data member (outside the class declaration)
7 int Point::count = 0;
8
9 // A static function can access static data member
10 int Point::getCount() {
11 return count;
12 }
13
14 // Constructor
15 Point::Point(int x, int y) : x(x), y(y) {
16 ++count; // one more instance created
17 }
18
19 // A non-static member function can also access static data member (of the class)
20 void Point::print() {
21 cout << "Point number " << count << " @ (" << x << "," << y << ")" << endl;
22 }

You cannot ini�alize the static variable in the class declara�on. This is because class declara�on merely describe the memory alloca�on but does not actually
allocate the memory. Instead, it is ini�alized outside the declara�on as shown above. The ini�aliza�on is kept in the implementa�on file, instead of header, so as
not the repeat the same step when header file in included.

TestPoint.cpp
1 /* Test Driver Program for Point class (TestPoint.cpp) */
2 #include <iostream>
3 #include "Point.h"
4 using namespace std;
5
6 int main() {
7 Point p1;
8 p1.print();
9 cout << Point::getCount() << " instances created" << endl;
10 Point p2(1, 2);
11 p2.print();
12 cout << Point::getCount() << " instances created" << endl;
13 Point p3(3);
14 p3.print();
15 cout << Point::getCount() << " instances created" << endl;
16 }

Program Notes:
A static data member retains this value throughout its life span.
To reference a static class member, you need to use ClassName::staticMemberName. You CANNOT invoke static member func�on from an
instance, such as p1.getCount().
A static data member can be accessed by static and non-static member func�ons. However, a static func�on member can only access
static data members, and CANNOT access non-static data members. For example,

// static member function


int Point::getCount() {
cout << "(" << x << "," << y << ")" << endl;
// error: invalid use of member 'Point::x' in static member function
return count;

18 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

5. More On Inheritance

5.1 Multiple Inheritance


A class derived from more than one base classes.

[TODO]

5.2 Virtual Inheritance


A base class instance is shared by mul�ple derived class instances.

[TODO]

6. Object with Dynamically Allocated Data Members

6.1 Implicitly-generated Special member Functions


C++ compiler automa�cally generates the following special member func�ons if they are required in your program:
A default constructor if you did not define any constructor.
A copy constructor if you did not define one.
An assignment operator if you did not define one.
An address-of operator if you did not define one.

Default Constructor
If you did not define any constructor in a class, C++ provide a default constructor that takes no argument and does nothing. For example, if you did not define
any constructor in Point class, the following default constructor will be generated:

Point::Point() {}

The default constructor does not ini�alize the data members. For example,

1 #include <iostream>
2 using namespace std;
3
4 class Point {
5 private:
6 int x, y;
7 public:
8 void print() const { cout << "(" << x << "," << y << ")" << endl; }
9 };
10
11 int main() {
12 Point p1; // Invoke implicitly-generated default constructor
13 // Value of x and y uninitialized
14 p1.print(); // (???,???)
15 }

If you have define a constructor of with any parameter-list. C++ will not generate the default constructor. In this case, if you use default constructor without
defining one, you will get a compila�on error For example,

1 #include <iostream>
2 using namespace std;
3
4 class Point {
5 private:
6 int x, y;
7 public:
8 Point(int x, int y) : x(x), y(y) { }
9 void print() const { cout << "(" << x << "," << y << ")" << endl; }
10 };
11
12 int main() {
13 Point p1;
14 // error: no matching function for call to 'Point::Point()'
15 }

A constructor with arguments can be treated as a default constructor if all arguments have a default value. For example,

19 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

point(int x = 0, int y = 0) : x(x), y(y) { }

Copy Constructor
A copy constructor is used to construct a new object by copying the given object. It take a const reference of an object of the same class, as follows:

ClassName(const ClassName &);

The copy constructor is used in the following situa�ons:

Point p1(p2); // Explicitly invoke the copy constructor

Point p1 = p2;
Point p1 = (Point)p2;
// Depending on compiler implementation, they may invoke the copy
// constructor to create a temporary object, and then copy over
// via memberwise assignment

Point * ptrP1 = new Point(p2);


// Invoke the copy constructor to construct an object and
// assign the address to the pointer.

In addi�on, when an object is passed by value into a func�on, and when a func�on returns an object by value, a compiler also uses the copy constructor to
generate a temporary object and then copy over via memberwise assignment. [Hence, it is more efficient to pass an object by reference into func�on, which
avoids the overhead of copying.]

The default copy constructor performs a memberwise copy of all the non-static data members. Each data member is copied by value. If the data member is
an object, the copy constructor of that object is used to do the copy. Sta�c members are not copy as they belong to the class (one copy shared by all instances).
However, if the data member is a pointer, the value of the pointer is copied - no dynamic memory alloca�on is performed to duplicate the contents pointed to
by the pointer. This is called shadow copying.

Assignment Operator
C++ allows object assignment via the assignment operator (=). It does so by automa�cally overloading the assignment operator, as follows:

ClassName & operator=(const ClassName &);

The overloaded assignment operator takes an object reference and return an object reference.

Like the copy constructor, the implicit assignment operator performs memberwise copy. For object data members, the assignment operator of that class will be
used for copying. Sta�c members are not copied. Again, for pointers, the value of pointer is copied, but no dynamic alloca�on is performed to duplicate the
contents of the pointer (shadow copying).

6.2 Dynamic Memory Allocation of Object Data Member


In C++, you can allocate memory for object during run�me, instead during compile-�me, using operators new and new[]. However, you are responsible for
memory management, and are required to free the memory via delete and delete[] to prevent memory leak. If you introduce new in your constructor,
you need to use delete in destructor to free the memory.

If you use new (or new[]) to dynamically allocate memory in the constructor to object data member pointers, for example,

class ClassName {
private:
T * pObj; // object data member pointer
public:
// Constructors
ClassName(...) {
pObj = new T(...); // or new[]
....
}
// Destructor
~ClassName() {
delete pObj; // OR delete[]
}
// Copy constructor
ClassName & ClassName(const ClassName &);

// Overload Assignment Operator


ClassName & operator=(const ClassName &);
......
}

You should use delete (or delete[]) in the destructor to free the dynamic memory allocated.
If you have more than one constructors, all constructors should be compa�ble with the destructor, including the default constructor. You may need to
provide you own default constructor, instead of using the implicitly generated one.
You should define a copy constructor that ini�alizes the object by deep copying the given object. The default copy constructor implicitly generated by the
compiler does shadow copying, which does not copy the contents of the pointers.
You should also define an assignment operator that deep copies one object into another.

20 of 21 22/03/2020, 19:45
Object-oriented Programming (OOP) in C++ https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp6_Inheritan...

Using Object Pointer


Declare an object pointer:

T * pObj;

Either ini�alize the pointer to an exis�ng object, or dynamically allocate an object.

pObj = &obj;
// OR
pObj = new T(...);

Use member-of (->) operator to refer to its class members, e.g. obj->memberName.
Use dereferencing (*) operator to get its content, e.g., *obj.

Link to "C++ References & Resources"

Latest version tested: Cygwin/MinGW GCC 4.6.2


Last modified: April, 2013

Feedback, comments, corrections, and errata can be sent to Chua Hock-Chuan (ehchua@ntu.edu.sg) | HOME

21 of 21 22/03/2020, 19:45

You might also like