OOPSCPP Module 3

Download as pdf or txt
Download as pdf or txt
You are on page 1of 14

Module III: Ovderloading of Operators and Inheritance

Overloading of Operators:
Operators like arithmetic and comparison can be done directly on numeric data types. But they cannot be
applied on programmer defined data types such as classes and structures. One way to do this is given below.
Here the class Point has two variables x and y as coordinates of a point.
Eg:
#include<iostream>
using namespace std;
class Point
{
int x,y;
public:
Point(int a=0, int b=0) { x=a; y=b; } // constructor
void setPoint(int a, int b) { x=a; y=b; }
int getX() { return x; }
int getY() { return y; }
void showPoint() { cout<< "(" << x << ", " << y << ")"; }
};
int main()
{
Point p1(2,3), p2(5,8), p3;
p1.showPoint(); cout<<endl; // shows (2,3)
p2.showPoint(); cout<<endl; // shows (5,8)
p3.setPoint(p1.getX()+p2.getX(), p1.getY()+p2.getY()); //set the resultant point
p3.showPoint(); // shows (7,11)
cout<<endl;
return 0;
}

Another way is to define functions which accepts the operands as parameters and returns the resultant object.
Point addPoints(Point p, Point q)
{ Point r;
int rx, ry;
rx=p.getX()+q.getX();
ry=p.getY()+q.getY();
r.setPoint(rx,ry);
return r;
}
Thus the addition can be done by the function call p3=addPoints(p1,p2).
But it will be nice if we can do the addition as p3 = p1 + p2 just like the addition of two numeric values. This
can be done by overloading the operator +. This can be done by simply changing the above function header
by a special type of function header, ie using the keyword operator.
Point operator + (Point p, Point q)
{ Point r;
int rx, ry;
rx=p.getX()+q.getX();
ry=p.getY()+q.getY();
r.setPoint(rx,ry);
return r;
}

OOP CPP Module 3 1


The above function can simply be written as;
Point operator + (Point p, Point q)
{ return Point(p.getX()+q.getX() , p.getY()+q.getY()); }
This enables us to write the addition as just p3=p1+p2. By seeing this statement, the compiler will call the
overloaded function operator+(). It will use the left operand of the expression as the first actual argument of
the function, and the right operand as the second argument of the function. Thus the statement p3=p1+p2 will
have a form p3=operator+(p1,p2). We can use this type of function call too.
int main()
{
Point p1(2,3), p2(5,8), p3;
p1.showPoint(); cout<<endl; // shows (2,3)
p2.showPoint(); cout<<endl; // shows (5,8)
p3=p1+p2; // or p3 = operator+(p1,p2); //set the resultant point
p3.showPoint(); // shows (7,11)

Limitations on Operator Overloading:


• We cannot overload all the operators. There are some operators such as operator :: (scope), operator .
(class object selector), and operator ?: (conditional operator or arithmetic ‘if’ ).
• We cannot overload an operator that is not available in C++. For example some programming
languages use the double asterisk operator (**) for exponentiation. But since this operator is not
available in C++ we cannot overload it.
• We cannot overload operators for built-in numeric types. For example int operator+ (int a, int b) will
give error. This is because the compiler will get confused whether to use the standard operation or
overloaded operation.
• Limitations on Return Types: We can define return types for operator overloaded functions as void, int,
float, same class, etc. But the return type must be carefully selected if there is a recursive operation
exists for the operator. Eg: p3=p1+p2+p3.
• Limitations on the Number of Parameters: We cannot change the arity of the operator, that is, the
number of operands that has to be specified when the operator is used (two for binary operators, one
for unary operators). The arity of the overloaded operator should be the same as the arity of the original
built-in operator.
• Limitations on Operator Precedence: The precedence of overloaded operators cannot be changed. They
remains as such, as with other operands.

Overloaded Operators as Class Members


The operator can be defined as a member function of the class of its parameters. The number of parameters is
one less than the arity of the operator (one for binary, none for unary). This absent parameter becomes the
target of the message when the operator is used.
For example, the binary operator+() and the binary operator+=() implemented as class Point member functions
should have only one parameter.
class Point
{
int x,y;
public:
Point(int a=0, int b=0) { x=a; y=b; }
void setPoint(int a, int b) { x=a; y=b; }
int getX() { return x; }
int getY() { return y; }

OOP CPP Module 3 2


void showPoint()
{ cout<<"("<<x<<", "<<y<<")"; }
Point operator + (Point q)
{ return Point(x+q.getX() , y+q.getY()); }
};
The overloaded operator function defined inside the class can be used in the same way as its global
definition. Thus, here too the + operator can be used as p3=p1+p2. Here the meaning of p3=p1+p2 is
p3=p1.operator+(p2). We can also use this statement instead of p1+p2. If we are returning object, we can use
this function for chain operations such as p1=p2+p3+p4+p5 etc.
We can define the class member overloading function outside the class scope. This is done by specifying a
prototype of the function inside the class and defining the function outside the class along with amending the
class name and scope operator (::). An example is given below.
class Point
{
int x,y;
public:
Point(int a=0, int b=0) { x=a; y=b; }
void setPoint(int a, int b) { x=a; y=b; }
int getX() { return x; }
int getY() { return y; }
void showPoint()
{ cout<<"("<<x<<", "<<y<<")"; }
Point operator + (Point q); // or simply Point operator+(Point); This is the prototype
};
Point Point::operator + (Point q)
{ return Point(x+q.getX(),y+q.getY()); }

Friend Functions
One of the important concept of C++ is data hiding, i.e., a nonmember function cannot access an object's
private or protected data. But, sometimes this restriction may force programmer to write long and complex
codes. So, there is mechanism built in C++ programming to access private or protected data from non-member
function which is friend function and friend class.
If a function is defined as a friend function then, the private and protected data of class can be accessed from
that function. The complier knows a given function is a friend function by its keyword friend. The declaration
of friend function should be made inside the body of class (can be anywhere inside class either in private or
public section) starting with keyword friend.
class class_name
{
...... .... ........
friend return_type function_name(arguments);
...... .... ........
}
Now, we can define friend function of that name and that function can access the private and protected data of
that function. The keyword 'friend' is not used in function definition of the friend function. A friend function
can be either a global function or a member function of another class. Inside it, the object variables can be
directly accessed using dot operator.
class Point
{
int x,y;
public:
Point(int a=0, int b=0) { x=a; y=b; }

OOP CPP Module 3 3


void setPoint(int a, int b) { x=a; y=b; }
int getX() { return x; }
int getY() { return y; }
void showPoint()
{ cout<<"("<<x<<", "<<y<<")"; }
friend void showXY(Point p); // declaration of the friend function.
};
void showXY(Point p) // no keyword 'friend'
{ cout<<"x: "<<p.x<<", y: "<<p.y<<endl; } // directly accesses x and y.

Friend Class: A class can be made a friend of another class using keyword friend. For example:
class B; // declare class B before use
class A{
friend class B; // class B is a friend class
..... ..... .....
};
class B{
..... ..... .....
};

When a class is made a friend class, all the member functions of that class becomes friend function. In this
program, all member functions of class B will be friend function of class A. Thus, any member function of
class B can access the private and protected data of class A. But, member functions of class A cannot access
the private and protected data of class B.

Operator Overloading for Nonnumeric Classes And Overloading the Assignment Operator
In most of the programmer-defined classes, operator overloading can be done and the objects can be used in
the same way as built-in types. The overloading works simply in numeric classes, but there are nonnumeric
elements such as character arrays (strings) where the overloading needs extra care. To avoid memory leakage
and memory overflow, the strings are allocated memory dynamically. Thus the overloading function will be
more complex.
Eg: a String class with ‘=’ and ‘+=’ operators overloaded.
#include<iostream>
#include<cstring>
#include<cstdlib>

using namespace std;


class String
{
char *str;
int len;

public:
String(const char *s) // constructor
{ len=strlen(s);
str=new char(len+1);
if(str==NULL) { cout<<"Memory allocation failed!!!\n"; exit(1); }
strcpy(str,s);
cout<<"String object created - "<<str<<endl;
}
String() // constructor
{ len=0;
str=new char(len+1);

OOP CPP Module 3 4


if(str==NULL) { cout<<"Memory allocation failed!!!\n"; exit(1); }
str[0]=0;
cout<<"Null String object created"<<str<<endl;
}
~String() // destructor
{
cout<<"String object deleted. "<<str<<endl;
delete str;
}
char* show() { return str; }
char* operator=(const String &src) // overloading assignment ('=') operator
{
if (&src == this) return this->str;
delete str;
len=src.len;
str=new char(len+1);
if(str==NULL) { cout<<"Memory allocation failed!!!\n"; exit(1); }
strcpy(str,src.str);
return str;
}
char* operator+=(const String &src) // overloading '+=' operator
{
char* p;
len = strlen(str) + strlen(src.str);
p = new char[len + 1];
if (p==NULL) { cout<<"Memory allocation failed!!!\n"; exit(1); };
strcpy(p,str);
strcat(p,src.str);
delete str;
str = p;
return str;
}

};

int main()
{
String s1("good"), s2("morning"), s3;
s1+=s2; cout<<"s1: "<<s1.show()<<endl;
s3=s1; cout<<"s3: "<<s3.show()<<endl;
return 0;
}

Aggregation and Inheritance


Using Class Objects as Data Members
A class is a collection of data members and their manipulating functions. The data members can be of any
built-in or programmer-defined types such as arrays. Also, these types of variables can be used inside the class
as local variables, parameters for member functions or return values from member functions. I addition to
these types, C++ allows us to use objects of other classes as data members. If one class has many data
members, we can merge a group of related data members into a larger object, and declare this object to be a
member of the class. Instead of a small number of large classes with many components, we wind up with a
larger number of classes with fewer components. Classes that have objects of other classes as their data

OOP CPP Module 3 5


members are called composite classes. Using objects of one class as components in another class is called
class aggregation or class composition. For example, we can create a class Point and define functions to do
operations on its data members (co-ordinate values). Since a Rectangle contains a number of points, we can
define a class Rectangle which contains some objects of the class Point too as data members, local variables or
function parameters/return values.
If a class has data members that belong to other classes, then the class member functions cannot access private
components of these data members. The composite class must use data member's methods to get access to
components of its own components.
Eg:
class Point
{
int x,y;
public:
Point(int a=0, int b=0) { x=a; y=b; }
void setPoint(int a=0, int b=0) { x=a; y=b; }
void show()
{
cout<< "(" << x << ", " << y << ")";
}
};
class Rectangle // here Rectangle is the composite class
{
Point p1,p2; // Here Point is the component class
int borderWidth;
public:
void show()
{
cout<<"Rectangle: ";p1.show(); p2.show(); //cannot use p1.x or p2.x directly
cout<<endl;
}
};

Initialization of Composite Objects


While defining a class, initialisation of member data is not possible. But they can be initialised using
constructors while object creation. This is similar in the case of composite classes too. Data members in
composite classes cannot be initialized in class specifications using the syntax appropriate for initialization of
objects of the component class.
C++ offers two ways to initialize components of a composite class. One way to do this is to assign the values
in the body of a constructor of the composite class. The constructor can assign data to appropriate data
members whether they are aggregates or simple components of built-in types.
Eg:
class Rectangle
{
Point p1,p2;
int borderWidth;
public:
Rectangle (Point& pt1, Point& pt2, int width)
{ p1 = pt1; p2 = pt2; // give values to aggregate components
borderWidth = width; // give values to built-in components
}
...
};

OOP CPP Module 3 6


int main()
{
Point pt1 (11, 22), pt2 (33, 44);
Rectangle r2(pt1, pt2, 5);
}

Another way to do it is to use a member initialization list that calls the constructor of the class components. In
this example, the member initialization list calls a Point constructor to initialize Point components of class
Rectangle.
Eg:
class Rectangle
{
Point p1,p2;
int borderWidth;
public:
Rectangle (Point& pt1, Point& pt2, int width):p1(pt1), p2(pt2) //member initialization list
{ borderWidth = width; } // give values to built-in components
...
};
int main()
{
Point pt1(11,22), pt2(33,44);
Rectangle r2(pt1,pt2,5);
}

There is another way for initialising the components; using the components' Default Constructors. When an
object of class Rectangle is created, events take place in the following order:
1. The data member p1 of class Point is created, its x and y are allocated, then its constructor is called.
2. The data member p2 of class Point is created, its x and y are allocated, then its constructor is called.
3. The data member borderWidth of type int is created.
4. The body of the Rectangle class constructor is executed.
When a composite object is destroyed, the process of memory management and function calls is reversed. The
composite class destructor is called first, before any memory is deallocated. When the destructor terminates,
the data members are destroyed in the order opposite to the order of their creation.

Data Members with Special Properties


Constant Data Members: Sometimes we want some data members that is get initialises but must not be
changed after that by any member function or friend function. Such data members can be represented as const
members. A constant data member has to be initialized immediately after this data member is created. Thus the
name of the constant data member must be included in the initializer list of the constructor. It shows a syntax
error on any assignment to this data member, even in a constructor. A constant data member can be either a
programmer-defined type or a built-in type.
Eg:
class Rectangle
{
Point p1,p2;
const int borderWidth; // Constant data member
public:
Rectangle(int a, int b, int c, int d, int bw): borderWidth(bw) // constant member in the initializer list, ok
{ p1.setPoint(a,b);

OOP CPP Module 3 7


p2.setPoint(c,d);
}
Rectangle(Point &pt1, Point &pt2, int bw):p1(pt1), p2(pt2),borderWidth(bw)
{
borderWidth=bw; // constant member inside constructor, error
}
void show()
{
borderWidth=10; // constant member cannot be changed, error
cout<<"Rectangle: ";p1.show(); p2.show();
cout<<endl;
}
};

Reference Data Members: The data members can be references. These references can point to the objects
outside of the composite object. All references in C++ are constants. They cannot change after they are
initialized. Hence, reference data members (similar to constant data members) must be initialized only in a
member initialization list: No initialization in the body of the constructor is allowed.
#include<iostream>
using namespace std;
class Point // component class
{
int x,y;
public:
Point(int a=0, int b=0)
{x=a; y=b; cout<<"Contructor of Point called...(" << x << ", " << y << ")\n"; }
void showPoint() { cout<< "(" << x << ", " << y << ")"; }
void setPoint(int a, int b) {x=a; y=b; }
};

class Rectangle // composite class


{
Point &p1,&p2; // reference
int borderWidth;
public:
Rectangle(Point &pt1, Point &pt2, int bw=0):borderWidth(bw),p1(pt1),p2(pt2)
{ cout<<"Contructor of Rectangle called...\n";
showRectangle();
}
void showRectangle() {cout<< "Rectangle: "; p1.showPoint(); p2.showPoint();
cout<<borderWidth<<endl; }
};
main()
{

OOP CPP Module 3 8


Point pt1(1,2), pt2(4,4);
Rectangle r(pt1,pt2, 1); // set rectangle to (1,2)(4,4)1
pt1.setPoint(6,7);
r.showRectangle(); // will print (6, 7)(4, 4)1
}

Using Objects as Data Members of Their Own Class: Whan we define a class, the same class object can be
used as a data member inside the class, but these data members must be references or pointers.
Eg:
class Point {
int x, y;
Point &anchor;
public:
Point (int a=0, int b=0)
{ x = a; y = b; }
};

Container Classes
Container classes are a special case of composite classes, which are useful when the application needs a data
type that contains a dynamic collection of values. Almost any application needs one or more container classes
created by others. Often the application needs a container or a collection class that can contain a variable
number of objects. Usually, a container object is initially empty and has no components. While the application
is executing, the component objects become available and are added to the container for temporary storage or
for processing.
There are many different kinds of container classes (such as arrays, lists, trees, stack, queue, etc.), each of
which has various advantages, disadvantages, and restrictions in their use. By far the most commonly used
container in programming is the array. Unlike built-in arrays, array container classes generally provide
dynamic resizing (when elements are added or removed), remember their size when they are passed to
functions, and do bounds-checking. This not only makes array container classes more convenient than normal
arrays, but safer too.
Container classes typically implement a fairly standardized minimal set of functionality. Most well-defined
containers will include functions that:
• Create an empty container (via a constructor)
• Insert a new object into the container
• Remove an object from the container
• Report the number of objects currently in the container
• Empty the container of all objects
• Provide access to the stored objects
• Sort the elements (optional)

Container classes are designed to perform these operations on behalf of the client code. The client code
requests the container to add a component, find a component, and access each component in the collection;
and the container object performs these operations, insulating the client code from the low-level details. As a
result, responsibility is pushed down from the client code to the server code (container class).

OOP CPP Module 3 9


Similar Classes: Inheritance
There are situations where we have to create several classes based our needs and some classes may have
similar data members and functions too. Such similar classes makes the code repetitive. It also cause
difficulties in maintenance too. Such problems can be solved by using inheritance. We create a class, which
contains the features common to all subtypes. In terms of object-oriented analysis and design, this class
represents the generalization of state and behavior of these subclasses. Then we reuse these common features
for other specialized classes. Each specialized class adds specialized features to the generalized class.
For example, a class Vehicle is a generalized class which contains the common characteristics of a vehicle. A
car is type of vehicle which has all the common properties of a Vehicle class plus some characteristics of its
own. Thus a class Car is a specialized class that inherits the class Vehicle and adds some additional data
members and/or functions.
A generalized class is otherwise called a base class, parent class or superclass. A specialized class is otherwise
called a derived class, a child class or a subclass. Derived classes add and sometimes replace features of a
more-general base class. Additional data and methods in derived classes reflect the relationship of
specialization among classes. Any number of subclasses can be derived from a superclass. Also, classes can be
derived from a subclass and the parent-child names will be changed accordingly.
In the above example, an SUV is a subclass of the class Car. Here the Car becomes the superclass of the SUV
and the SUV class becomes a subclass.
Inheritance can be either direct or indirect. Vehicle is a direct superclass or a direct base class of Car. Car is a
direct superclass or a direct base class of SUV. Vehicle is an indirect superclass or indirect base class of SUV.
Inheritance has some advantages and uses.
* a derived class is defined that only provides new operations; all others are provided by the base class.
* inheritance can be used as a tool for division of labor in software development.
* In addition to enhance abstraction, the use of commonality in classes results in less code to write and in
better modularization.
* run-time binding of methods; other terms for run-time binding are dynamic binding and polymorphism.

Syntax of C++ Inheritance:


class derived_class_name: memberAccessSpecifier base_class_name
{
...
};

Eg:
class Shape
{ private: ...
protected: ...
public: ...
};
class Rectangle: public Shape //Rectangle is a subclass of Shape
{ private: ...
protected: ...
public: ...
};

Different Modes of Derivation from the Base Class:


The mode of derivation represents the granting of access of the members of the base class to the derived class.
It is also called the member access specification type. The member Access Specifier may be public, protected
or private. This access specifier describes the access level for the members that are inherited from the base

OOP CPP Module 3 10


class. If the specifier is not given it will be private by default.

Member How Members of the Base Class Appear in the Derived Class
Access
Specifier
Private Private members of the base class are inaccessible to the derived class.
Protected members of the base class become private members of the derived class.
Public members of the base class become private members of the derived class.
Protected Private members of the base class are inaccessible to the derived class.
Protected members of the base class become protected members of the derived class.
Public members of the base class become protected members of the derived class.
Public Private members of the base class are inaccessible to the derived class.
Protected members of the base class become protected members of the derived class.
Public members of the base class become public members of the derived class.

In principle, a derived class inherits every member of a base class except constructor and destructor. It means
private members are also become members of derived class. But they are inaccessible by the members of
derived class.

Eg:
class Shape
{
protected:
float width, height;
public:
void setData (float a, float b) { width = a; height = b; }
void showValues( ) { cout<<”Shape values: “<<width<<”, “<<height<<endl; }
};

class Rectangle: public Shape


{
public:
float area () { return (width * height); }
void showValues( ) { cout<<”Rectangle values: “<<width<<”, “<<height<<endl; }
};

class Triangle: public Shape


{
public:

OOP CPP Module 3 11


float area () { return (width * height / 2); }
void showValues( ) { cout<<”Triangle values: “<<width<<”, “<<height<<endl; }
};

int main ()
{
Rectangle rect;
Triangle tri;
rect.setData (5,3); // function of base class
tri.setData (2,5);
cout << rect.area() << endl; // function of derived class Rectangle
cout << tri.area() << endl; // function of derived class Triangle
return 0;
}

A derived class inherits all base class methods with the following exceptions:
• Constructors, destructors and copy constructors of the base class.

• Overloaded operators of the base class.

• The friend functions of the base class.

Defining and Using Objects of Base and Derived Classes:


While defining methods in the derived class, we meet three situations;
1. The method is inherited from the base class as is and is not redefined in the derived class.
2. The method is absent in the base class and is added to the derived class.
3. The method is present in the base class and is redefined by the derived class.

Accessing Base and Derived Class Services


Normally, a derived class "is a" base class; each object of the derived class has all base class data and function
members plus added and redefined data and methods. The derived class uses the base class services (data and
functions). The base class does not know about derived classes because the server in programming never
knows the identity of its clients. The derived class must know the name of its base class, and it must know the
names of its non-private services to be able to use them. Members of the base class have no access to features
that are added or redefined in the derived classes. Base class objects do not have data members and member
functions described in derived classes. Derived class objects can be viewed as a sum of the derived class parts
(its private, protected, and public components) and the base class parts (its private, protected, and public
components). The memory allocated for the derived class object is a sum of the memory allocated for the base
part and the memory allocated for the derived part.

Accessing Base Components of a Derived Class Object


Access to the base class components from a derived class depends on the access rights of the base class
members (data and functions) and the mode with which the subclass is derived. For public mode of derivation,
inherited members of the base class retain their access status (private, protected, and public) in the objects of
the derived class; all public services, those defined in the derived class and those inherited from the base class,
are available for the client code. This is the most natural mode of inheritance. (see the table above)
Scope Rules and Name Resolution Under Inheritance
The derived class scope is nested within the base class scope, which means that the derived class names hide
base class names within the derived class. Similarly, the derived class names hide base class names in the
derived class client code. If the derived and base classes use the same name, the base class name does not have
a chance, as the meaning of the derived class name will be used.

OOP CPP Module 3 12


C++ supports function name overloading in the same scope only. In independent scopes, function names do
not conflict, and we can use the same name with the same or different signatures. In nested scopes, the name
in the nested scope hides the name in the outer scope, whether or not these names have the same signatures. If
classes are related through inheritance, the function name in the derived class hides the function name in the
base class. Again, the signature is not important. But we can call the base method hidden by the derived class
by using scope operator as derived_class_object.base_class_name::base_class_method(). A better solution is
to overload the derived class method itself.

Types of Inheritance

Single Inheritance Multiple Inheritance


class A { .. }; class A { .. };
class B: public A { .. }; class B { .. };
class C: public A, private B { .. };

Multilevel Inheritance Hierarchical Inheritance


class A { .. }; class A { .. };

class B: public A { .. }; class B: public A { .. };

class C: public B { .. }; class C: public A { .. };

Hybrid Inheritance
class A { .. };
class B: public A { .. };
class C: public A { .. };
class D: public B, protected C { .. };

Constructors and Destructors for Derived Classes


For composition, the object data members are created (and their constructors are called) before the composite
class constructor is executed. For class inheritance, the base part of the object is always created (and its
constructor is called) before the derived part of the object is created and before the derived class constructor is
executed. As for any object, data members for the derived class object are allocated before the body of the
derived class constructor is executed. However, even before the data described in the derived class is allocated,
the base part of the derived object is created.

OOP CPP Module 3 13


Choosing between Inheritance and Composition
Both composition and inheritance place subobjects inside the new class. Both use the constructor initializer list
to construct these subobjects.
Composition is generally used when we want the features of an existing class inside your new class, but not its
interface. That is, we embed an object to implement features of our new class, but the user of the new class
sees the interface we’ve defined rather than the interface from the original class.
Inheritance is a good abstraction tool. For the client programmer and for the maintainer, there is no need to
study the classes separately or compare the code of one class against that of another class. The use of
inheritance helps us to save development effort. It does not always make the source code for class
implementation shorter. Still, many programmers believe that it is easier to develop a complex class in simple
stages rather than as a single large unit. However, the use of inheritance introduces extra implicit dependencies
between classes, which might not be obvious to the designer (or maintainer) of the client code. Studying the
derived class description does not provide the reader with the list of services available to the client. The reader
has to study the base class as well.
The composite (aggregate) class provides the reader with the complete list of services that the class supports.
Composition also introduces dependencies between classes, but these dependencies are explicit, in the form of
one-liner methods, which push the work from the container class to the methods of the component class.
The choice between inheritance and composition depends on the degree of similarity between the related
classes. If the number of common methods is relatively small and the number of additional services to be
supported is relatively large, composition is useful. The aggregate class will have only a few functions, and the
expense of writing them will be offset by the availability of the explicit list of supported services.
If the number of common methods is relatively large and the number of additional services is relatively small,
inheritance might be useful. Many programmers are annoyed at the need to write one-liner methods. The use
of inheritance will eliminate these one-liner methods, the base class methods will be inherited by the derived
class directly. Redefining base methods in the derived class is pleasing.
The is-a relationship is expressed with inheritance, and the has-a relationship is expressed with composition.

OOP CPP Module 3 14

You might also like