Polymorphism and Virtual Functions
Week 7
Copyright © 2017 Pearson Education, Ltd.
Type Compatibility in Inheritance Hierarchies
q Classes in a program may be part of an inheritance hierarchy
q Classes lower in the hierarchy are special cases of those Animal
above
class Cat:public Animal {
public: Cat Dog
void talk() {
cout << "cat hiss..\n";
class Animal { } Poodle
public: void walk() {
void talk() { cout << "cat walk..\n";
cout << "Animal sound ...\n"; }
} };
void walk() {
cout << "Animal walk\n"; class Poodle:public Dog {
} class Dog:public Animal { public:
void sleep() { public: void talk() {
cout << "Animal Sleep\n"; void talk() { cout << "poodle bark..\n";
} cout << "dog bark..\n"; }
}; } void walk() {
void walk() { cout << "poodle walk..\n";
cout << "dog walk..\n"; }
} };
};
Copyright © 2017 Pearson Education, Ltd.
Type Compatibility in Inheritance
q A pointer to a derived class can be assigned to a pointer to a base class.
q A base class pointer can point to derived class objects
auto main() -> int
{
Cat *cptr = new Cat;
Animal *aptr1 = cptr;
Animal *aptr2 = new Dog;
Copyright © 2017 Pearson Education, Ltd.
Type Compatibility in Inheritance
q Assigning a base class pointer to a derived class pointer requires a cast
q The base class pointer must already point to a derived class object for this
to work correctly.
auto main() -> int
{
Animal *aptr = new Cat;
Cat *cptr;
cptr = static_cast<Cat *> (aptr);
Copyright © 2017 Pearson Education, Ltd.
Using Type Casts with Base Class Pointers
q C++ uses the declared type of a pointer to determine access to the
members of the pointed-to object
q If an object of a derived class is pointed to by a base class pointer,
all members of the derived class may not be accessible
q Type cast the base class pointer to the derived class (via
static_cast) in order to access members that are specific to the
derived class
15-5
Copyright © 2017 Pearson Education, Ltd.
Using Type Casts with Base Class Pointers
auto main() -> int
{
Animal *aptr = new Dog;
aptr->talk();
aptr->walk();
Animal sound ...
Cat *cptr = new Cat; Animal walk
cptr->talk(); cat hiss..
cat walk..
cptr->walk(); dog bark..
dog walk..
Dog *dptr = static_cast<Dog *> (aptr);
dptr->talk();
dptr->walk();
}
Copyright © 2017 Pearson Education, Ltd.
Casting in C++
q C++ is a strong-typed language. int main () {
q There are 6 different types of casts: float x = 3.5;
int y = x;
– C-style casts, cout << y << endl;
– functional casts, int a = 10, b = 20;
– static casts, float c;
c = a / b; 3
– const casts, cout << c << endl; 0
– dynamic casts, and 0
c = float (a/b); 0
– reinterpret casts. cout << c << endl;
c = (float)(a/b); 0.5
cout << c << endl; 0.5
int main () { 0.5
int a=115, b=104, c=97, d=114, e=97,f=102; c = float(a)/b;
cout << c << endl;
cout << a << b << c << d << e << f << endl; c = (float)a/b;
cout << char(a) << char(b) << char(c) cout << c << endl;
<< char(d) << char(e) << char(f) c = static_cast<float>(a)/b;
<< endl; cout << c << endl;
return 0; 1151049711497102 return 0;
} sharaf }
Copyright © 2017 Pearson Education, Ltd.
Upcasting and downcasting
q Upcasting is a process of treating a pointer or a reference of derived class object
as a base class pointer and performed automatically.
q Downcasting is converting base class pointer (or reference) to derived class
pointer and must be explicitly done by programmer.
class Animal { class Cat:public Animal { class Dog:public Animal {
public: public: public:
void talk() { void talk() { void talk() {
cout << "Animal sound ...\n"; cout << "cat hiss..\n"; cout << "dog bark..\n";
} } }
void walk() { void walk() { void walk() {
cout << "Animal walk\n"; cout << "cat walk..\n"; cout << "dog walk..\n";
} } }
void sleep() { void run() { void run() {
cout << "Animal Sleep\n"; cout << "cat run..\n"; cout << "Dog run..\n"
} } }
}; }; };
class Poodle:public Dog {
public:
void talk() {
cout << "poodle bark..\n";
}
void walk() {
cout << "poodle walk..\n";
}
};
Copyright © 2017 Pearson Education, Ltd.
Upcasting and downcasting
Upcasting downcasting
auto main() -> int auto main() -> int
{ {
Cat c; Cat c;
Animal *aptr = &c;
Animal *aptr = &c;
// aptr->run();
aptr->talk(); // Cat *cptr = aptr;
aptr->walk();
Cat *cptr = static_cast<Cat *>(aptr);
// aptr->run();
cptr->talk();
} cptr->walk();
cptr->run();
Copyright © 2017 Pearson Education, Ltd.
dynamic_cast
q Given a pointer or a reference to an object of Animal *getAnimal ( ) {
type B, dynamic_cast attempts to convert that if ( rand()%2 == 0 ) return new Cat;
object to a pointer or a reference to an object else return new Dog;
of type D if D is a sub class of B. }
q dynamic_cast is an operator that converts
safely one type to another type. auto main() -> int
{
q dynamic_cast is used at run-time to find out srand(time(NULL));
correct down-cast. for (int i=0;i<5;i++)
{
q The base class must have at least one
cout << "........\n" << endl;
virtual method Animal *aptr = getAnimal();
q If the cast is successful, it returns a value cout << typeid(*aptr).name() << endl;
of that type
Cat *cptr = dynamic_cast<Cat *> (aptr);
q If the cast fails and the type is a pointer, it
returns a nullptr of that type. if (cptr != nullptr)
{ ........
3Dog
cout << "It is a cat \n"; It is a dog
class Animal{ ........
cptr->talk(); 3Cat
public: It is a cat
cptr->walk(); cat hiss..
virtual void talk() { cout << "Animal sound...\n"; }
} cat walk..
virtual void walk() { cout << "Animal walk\n"; } ........
else 3Dog
virtual void sleep() { cout << "Animal Sleep\n"; } It is a dog
cout << "It is a dog \n"; ........
}; 3Dog
} It is a dog
} ........
3Dog
It is a dog
Copyright © 2017 Pearson Education, Ltd.
static_cast
q static_cast is aimed at replacing explicit casts
q static_cast can be used to cast from one primitive type to another (such
as int to char)
auto main() -> int
{
Animal *aptr = new Cat;
Must be a cat pointer
Cat *cptr = static_cast<Cat *> (aptr);
cptr->talk();
cptr->walk();
}
Must point to a cat
object
Copyright © 2017 Pearson Education, Ltd.
reinterpret_cast (1)
q Converts between types by reinterpreting the underlying bit pattern.
class Apple{ int main()
public: {
void eatApple(){ Orange *o = new Orange();
cout << "eating Apple" << endl; Apple *a = new Apple();
}
}; Orange *orange = reinterpret_cast<Orange*>(a);
orange->eatOrange();
class Orange {
public: return 0;
void eatOrange(){ }
cout << "eating Orange" << endl;
}
};
eating Orange
Copyright © 2017 Pearson Education, Ltd.
reinterpret_cast (2)
struct MyStruct{ int main() 10
int x; { 20
int y; MyStruct s {10,20,3.14,true}; 3.14
float c; cout << s.x << endl 1
bool b; << s.y << endl 10
}; << s.c << endl 20
<< s.b << endl; 3.14
1
s int *p = reinterpret_cast<int*> (&s);
cout << *p << endl;
s
x p++; p
cout << *p << endl;
x
p++; p++
y float *cc = reinterpret_cast<float*>(p); p
cout << *cc << endl;
y
cc++; p++
c bool *bb = reinterpret_cast<bool*> (cc); p
cout << *bb << endl;
b return 0; cc c
} cc++;
cc b
bb
Copyright © 2017 Pearson Education, Ltd.
const_cast
q Used to change the const or volatile qualifiers of pointers or references.
const_cast<T>(v)
q T must be a pointer, reference, or pointer to member type.
int main()
{
const int constInt = 10;
const int *constPtr = &constInt;
int *d1 = const_cast<int*>(constPtr);
*d1 = 15;
int varInt = 20;
const int *varPtr = &varInt;
int *d2 = const_cast<int*> (varPtr);
*d2 = 30;
return 0;
}
Copyright © 2017 Pearson Education, Ltd.
Pointers, Inheritance, and Overridden Functions
If
– a derived class overrides a member function in the base class, and
– a base class pointer points to a derived class object,
then
the compiler determines the version of the function to use by the type
of the pointer, not by the type of the object.
1-15
Copyright © 2017 Pearson Education, Ltd.
Polymorphism and Virtual Member Functions
q Polymorphic code: Code that behaves differently when it acts on objects of
different types
q Virtual Member Function: The C++ mechanism for achieving polymorphism
class Animal { auto main() -> int
public: {
virtual void talk(){ vector<Animal *> v;
cout << "Animal sound ...\n"; v.push_back(new Cat);
} v.push_back(new Cat);
virtual void walk(){ v.push_back(new Dog);
cout << "Animal walk\n"; v.push_back(new Cat);
} v.push_back(new Dog);
virtual void sleep(){
cout << "Animal Sleep\n"; for (auto x:v)
} x->talk();
};
for (int i=0;i<[Link]();i++)
v[i]->walk();
Copyright © 2017 Pearson Education, Ltd.
Polymorphism
Consider the Animal, Cat, Dog hierarchy where each class has its own
version of the member function id( )
class Animal {
public:
void id() {
cout << "animal";
}
Animal
};
class Cat : public Animal {
public:
void id() {
Cat Dog
cout << "cat";
}
};
class Dog : public Animal {
public: Poodle
void id() {
cout << "dog";
}
};
Copyright © 2017 Pearson Education, Ltd.
Polymorphism
q Consider the collection of different Animal objects
q Prints: animal animal animal, animal, ignoring the more specific
versions of id() in Dog and Cat
auto main() -> int
{
Animal *arr[] = { new Animal, new Dog, new Dog, new Cat};
for (int i=0;i<4;i++)
arr[i]->id();
}
q The preceding code is not polymorphic: it behaves the same way even
though Animal, Dog and Cat have different types and different id()
member functions
q Polymorphic code would have printed "animal dog dog cat"
instead of "animal animal animal animal"
Copyright © 2017 Pearson Education, Ltd.
Polymorphism
q The code is not polymorphic because in the expression
arr[i]->id();
the compiler sees only the type of the pointer arr[i], which is pointer
to Animal
q Compiler does not see type of actual object pointed to, which may be
Animal, or Dog, or Cat
15-19
Copyright © 2017 Pearson Education, Ltd.
Virtual Functions
q Declaring a function virtual will make the compiler check the type of
each object to see if it defines a more specific version of the virtual
function
class Animal { auto main() -> int
public: {
virtual void id() { Animal *arr[] ={ new Animal, new Dog,
cout << "animal"; new Dog, new Cat};
}
}; for (int i=0;i<4;i++)
arr[i]->id();
class Cat : public Animal { }
public:
void id() { animal
cout << "cat"; dog
} dog
cat
};
class Dog : public Animal {
public:
void id() {
cout << "dog";
}
};
Copyright © 2017 Pearson Education, Ltd.
Virtual Functions
q It is also possible to use virtual with the functions in the derived classes.
q Base class function must be virtual.
class Animal {
public:
virtual void id() {
cout << "animal";
}
};
class Cat : public Animal {
public:
virtual void id() {
cout << "cat";
}
};
class Dog : public Animal {
public:
virtual void id() {
cout << "dog";
}
};
Copyright © 2017 Pearson Education, Ltd.
Function Binding
q In arr[i]->id(), Compiler must choose which version of id() to
use.
q There are different versions in the Animal, Dog, and Cat classes
q Function binding is the process of determining which function definition
to use for a particular function call
q The alternatives are static and dynamic binding
q Static binding chooses the function in the class of the base class
pointer, ignoring any versions in the class of the object actually pointed
to Static binding is done at compile time
q Dynamic Binding determines the function to be invoked at execution
time
q Can look at the actual class of the object pointed to and choose the
most specific version of the function
q Dynamic binding is used to bind virtual functions
Copyright © 2017 Pearson Education, Ltd.
Virtual Tables
class Animal { class Cow:public Animal {
public: public:
virtual void talk(){ void talk() {
cout << "moo..heha..woof\n"; cout << ”Moo…\n";
} }
virtual void walk() { };
cout << "Animal walk\n";
}
void sleep() { class Donkey:public Animal {
cout << "Animal Sleep\n"; public:
} void talk() {
}; cout << ”bray…\n";
}
int main( ) { };
srand((unsigned int)time(0));
Animal *a;
switch(rand()%3){ class Dog:public Animal {
case 0: a = new Cow(); break; public:
case 1: a = new Donkey(); break; void talk(){
case 2: a = new Dog(); cout << ”bark…\n";
} }
a->talk();
a->walk(); void walk(){
a->sleep(); cout << "Dog walk..\n";
return 0; }
} };
Copyright © 2017 Pearson Education, Ltd.
Virtual tables
Copyright © 2017 Pearson Education, Ltd.
Overriding vs. Overloading
q Recall that Overloaded functions have the same name but different signature
(parameter list).
q If a base class has a virtual member function and a derived class has an
overloading member function, use the override key word at the end of the function
header or prototype to indicate that it should be treated as an overriding function.
class Animal { class Cow:public Animal {
public: public:
void talk() override {
virtual void talk() {
cout << "Moo…\n";
cout << "moo..heha..woof\n"; }
} };
virtual void walk() { class Donkey:public Animal {
cout << "Animal walk\n"; public:
} void talk() override {
virtual void sleep() { cout << "bray…\n";
}
cout << "Animal Sleep\n";
};
} class Dog:public Animal {
}; public:
void talk() override {
cout << "bark…\n";
}
void walk(){
cout << "Dog walk..\n";
}
Copyright © 2017 Pearson Education, Ltd. };
The final Key Word and Overriding
q The key word final can be used at the end of the header or prototype of
a function in an inheritance hierarchy if you want to ensure that no
classes below this one override this function.
q If an attempt is made to override this function in a derived class, a
compiler error will occur.
class Animal { class Cow:public Animal {
public: public:
virtual void talk() { void talk() override {
cout << "moo..heha..woof\n"; cout << "Moo…\n";
} }
virtual void walk() { void sleep() {
cout << "Animal walk\n"; cout << "Cow Sleep\n";
} }
virtual void sleep() final };
{
cout << "Animal Sleep\n";
} Generates a compiler error
};
Copyright © 2017 Pearson Education, Ltd.
Abstract Base Classes and Pure Virtual Functions
q An abstract class is a class that contains no objects that are not
members of subclasses (derived classes)
q For example, in real life, Animal is an abstract class: there are no
animals that are not dogs, or cats, or lions…
q Abstract classes are an organizational tool. They are useful in
organizing inheritance hierarchies
q Abstract classes can be used to specify an interface that must be
implemented by all subclasses
Copyright © 2017 Pearson Education, Ltd.
Abstract Functions
q The member functions specified in an abstract class do not have to
be implemented
q The implementation is left to the subclasses
q In C++, an abstract class is a class with at least one abstract member
function
Copyright © 2017 Pearson Education, Ltd.
Pure Virtual Functions
q In C++, a member function of a class is declared to be an abstract
function by making it virtual and replacing its body with = 0;
class Animal{
public:
virtual void id()=0;
};
q A virtual function with its body omitted and replaced with =0 is called a
pure virtual function, or an abstract function
15-29
Copyright © 2017 Pearson Education, Ltd.
Abstract Classes
q An abstract class can not be instantiated
q An abstract class can only be inherited from; that is, you can derive
classes from it
q Classes derived from abstract classes must override all pure virtual
functions with concrete member functions before they can be instantiated.
class Animal { class Donkey:public Animal int main( ) {
public: { srand((unsigned int)time(0));
virtual void talk()=0; public:
//Animal an;
virtual void sleep() { void talk() override {
//Animal ptr = new Animal;
cout << "Animal Sleep\n"; cout << "bray…\n";
} } Animal *a;
}; }; switch(rand()%3) {
case 0: a = new Cow();
class Cow:public Animal { class Dog:public Animal { break;
public: public: case 1: a = new Donkey();
break;
void talk() override { void talk() override { case 2: a = new Dog();
cout << "Moo…\n"; cout << "bark…\n"; }
} } cout << typeid(*a).name()
void sleep() override { }; << endl;
cout << "Cow Sleep\n"; a->talk();
} 3Dog a->sleep();
}; bark… return 0;
Animal Sleep }
Copyright © 2017 Pearson Education, Ltd.
Composition vs. Inheritance
q Inheritance models an 'is a' relation between classes. An object of a
derived class 'is a(n)' object of the base class
q Example:
– an UnderGrad is a Student
– a Mammal is an Animal
– a Poodle is a Dog
Copyright © 2017 Pearson Education, Ltd.
Composition vs. Inheritance
q When defining a new class:
q Composition is appropriate when the new class needs to use an object
of an existing class
q Inheritance is appropriate when
– objects of the new class are a subset of the objects of the existing
class, or
– objects of the new class will be used in the same ways as the objects
of the existing class
Copyright © 2017 Pearson Education, Ltd.
Composition
class Name { class Student {
string first; Name name;
string last;
public: public:
Name(string f="X", string l="Y") { Student () = default;
first = f;
last = l; Student (Name &name) {
} this->name = name;
void setFirst(string f){ }
first = f;
} operator string (){
void setLast(string l){ return (string)name;
last = l; }
} };
string getFirst(){
return first; int main() {
} Name s1("John", "Watson");
string getLast(){ Student p(s1);
return last; cout << (string)p << endl ;
} return 0;
operator string(){ } John Watson
return first+" "+last;
}
};
Copyright © 2017 Pearson Education, Ltd.
Polymorphism and Virtual Functions
Week 7
Copyright © 2017 Pearson Education, Ltd.