OOP C7 Polymorphism

Subtyping: subtype and supertype

A class defines a type.

  • subtype: A type defined by a derived class.
  • supertype: A type defined by its base class.

RECALLL The inheritance relationship enables a derived class to inherit features from its base class with additional new features. A derived class is a specialization of its base class; every instance of a derived class is also an instance of its base class, but not vice versa.

Polymorphism

Polymorphism means that a variable of a supertype can refer to a subtype object. i.e An object of a derived class polymorphism can be used wherever its base class object is used.

Conversions and upcasting

Conversions

Public Inheritance should imply substitution.

  • If B is-a A, you can use a B any where an A can be used.
  • if B is-a A, then everything that is true for A is also true of B.
  • Be careful if the substitution is not valid!

Given Derived is derived from Base, serval ways:

1
2
3
4
5
Base* B;
Derived* D;
B = D; //nothing happened to data stored
*B = *D; //copy the commen data of *D to *B
B& = D&;

Upcasting, 向上造型

  • Upcasting is to regard an object of the derived class as an object of the base class.

  • Upcasting is the act of converting from a Derived reference or pointer to a base class reference or pointer.

    1
    2
    3
    4
    5
    6
    7
    /* Now give the Manager derived from Employee */
    /* the Member function Print is not virtual. It is redefined in the Manager class.*/
    Manager pete( "Pete", "444-55-6666", "Bakery");
    Employee* ep = &pete; // Upcast
    Employee& er = pete; // Upcast
    ep -> print(cout); //perform in base class version
    er.print(cout);

Virtual Functions and Dynamic Binding

Virtual Functions

A function can be implemented in several classes along the inheritance chain. Virtual functions enable the system to decide which function is invoked at runtime based on the actual type of the object.

The keyword virtual means that there may be other functions with the same name in the inheritance chain.

  • If a function is defined virtual in a base class, it is automatically virtual in all its derived classes. That’s to say virtual need not be added in the function declaration in the derived class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*Point:(x,y)*/
class XYPos{ ... };
/*Base class: Shape*/
class Shape {
public:
Shape(); virtual ~Shape();
virtual void render();
void move(const XYPos&);
virtual void resize();
protected:
XYPos center;
};
/*Derived class:Ellipse from Shape*/
class Ellipse : public Shape {
public:
Ellipse(float maj, float minr);
virtual void render(); // will define its own
protected:
float major_axis, minor_axis;
};
/*Derived class:Circle from Ellipse*/
class Circle : public Ellipse {
public:
Circle(float radius) : Ellipse(radius, radius){}
virtual void render();
virtual void resize();
virtual float radius();
protected:
float area;
};
/*call by pointer;calls correct render function*/
void render(Shape* p) {
p->render();
}
1
2
3
4
5
6
Ellipse ell(10, 20); 
ell.render(); // static -- Ellipse::render();
Circle circ(40);
circ.render(); // static -- Circle::render();
render(&ell); // dynamic -- Ellipse::render();
render(&circ); // dynamic -- Circle::render()

Dynamic Binding

The capability of determining which function to invoke at runtime is known as dynamic binding.

In C++, redefining a virtual function in a derived class is called overriding a function.

static binding vs. dynamic binding

Matching a function signature and ②binding a function implementation are two separate issues.

static binding

The declared type of the variable(i.e static type) decides which function to match at compile time. The compiler finds a matching function according to parameter type, number of parameters, and order of the parameters at compile time.

dynamic binding

A virtual function may be implemented in several derived classes. C++ dynamically binds the implementation of the function at runtime, decided by the actual class of the object referenced by the variable( i.e dynamic type).

virtual is the only symbol to generate a dynamic bindging.

function type binding type example
free function static func()
member function by obj static obj.func()
member function by ref static ref.func()
member function by pointer static p -> func()
virtual function by ref dynamic ref.vfunc()
virtual function by pointer dynamic p -> vfunc()
  • Static function are faster to execute.
  • If a function defined in a base class needs to be redefined in its derived classes, you should define it virtual to avoid confusions and mistakes.
  • On the other hand, if a function will not be redefined, it is more efficient not to declare it virtual, because more time and system resource are required to bind virtual functions dynamically at
    runtime.
  • polymorphic type: a class with a virtual function.
  • Polymorphic variables: Pointers or reference variables of objects. They can hold objects of the declared type, or of subtypes of the declared type.

How virtuals work?

Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class A{
public:
A(){}
};
class B{
public:
B(){}
virtual void vf(){};
};
class C{
private:
int x;
public:
C(){}
C(int a){x = a;}
};
class D{
private:
int x;
public:
D(){}
D(int a){x = a;}
virtual void vf(){}
};

Then, give the instances of the classes.

1
2
3
4
5
A a;    
B b;
C c;
D d;
cout << sizeof(a) << endl << sizeof (b) << endl<<sizeof(c) << endl <<sizeof(d);

The output is

1
2
3
4
1   //there's something 
8 //size of pointer
4 //size of int
16
1
2
3
4
5
6
7
8
9
10
11
C cc(11);
D dd(12);
int* pc = (int*) &cc;
int* pd = (int*) &dd;
long long* pdl = (long long*)&dd;
cout << pc <<" "<<*pc<<endl;
cout << pd <<" "<<*pd<<endl;
cout << pdl <<" "<<*pdl<<endl;
cout << pd + 1<<" "<<*(pd + 1)<<endl;
cout << pd + 2<<" "<<*(pd + 2)<<endl;
cout << pdl + 1<<" "<<*(pdl + 1)<<endl;

The output is

1
2
3
4
5
6
0x26bdff7ac 11
0x26bdff790 -1885507728
0x26bdff790 140696948141936
0x26bdff794 32758
0x26bdff798 12
0x26bdff798 12
Virtual pointer and virtual table

Polymorphic variables has virtual pointer, which points to virtual table. All virtual function pointers are stored in virtual table.

virtual pointer is only initialized in the initialize list of constructor and never changes again!

Virtual destructors

Please always make destructors virtual since all classes might be inherited.

Overriding

Overriding redefines the body of a virtual function.

  • Superclass and subclass define methods with the same signature.
  • Each has access to the fields of its class.
  • Superclass satisfies static type check.
  • The Relaxation:
1
2
3
4
5
6
7
8
9
10
11
12
class Expr { 
public:
virtual Expr* newExpr();
virtual Expr& clone();
virtual Expr self();
};
class BinaryExpr : public Expr {
public:
virtual BinaryExpr* newExpr(); // Ok
virtual BinaryExpr& clone(); // Ok
virtual BinaryExpr self(); // Error
};

Overloading virtual functions

If an overloaded function is overrided, then all of the variants should be overrided ! Or functions that are not overrided will be hidden.

1
2
3
4
5
6
7
8
9
10
11
12
class Base { 
public:
virtual void func();
virtual void func(int);
};
class Derived : public Base {
public:
virtual void func() {
Base::func(); //not override
}
virtual void func(int) { ... } ; //override
};
  • Never redefine an inherited non-virtual function:
    • Non-virtuals are statically bound
    • No dynamic dispatch!
  • Never redefine an inherited default parameter value: They’re statically bound too!

OOP C7 Polymorphism
http://example.com/2023/04/18/OOP-7/
Author
Tekhne Chen
Posted on
April 18, 2023
Licensed under