Inheritance Inheritance The main ideas are similar to

  • Slides: 124
Download presentation
Inheritance

Inheritance

Inheritance • The main ideas are similar to what you already know from Java

Inheritance • The main ideas are similar to what you already know from Java • C++ has no interfaces but it allows multiple inheritance • We will discuss later the problems that arise: Class A Class B Class C Class D 2

Inheritance • The main ideas are similar to what you already know from Java

Inheritance • The main ideas are similar to what you already know from Java • Inheritance should be used for “is-a” and not for code-reuse (in this case, composition should be preferred) • Inheritance lets you use runtime polymorphism – List of pointer to Shapes where some are Circle, some are Rectangle… 3

Person class Person { private: std: : string _name; int _id; static constexpr int

Person class Person { private: std: : string _name; int _id; static constexpr int NO_ID_VAL= -1; public: Person (const std: : string& name, int id); void change. Name(const string& name); void change. Id(int id); std: : string get. Name() const; int get. Id() const; } ; 4

protected • Class members that should be accessible by subclasses only are declared as

protected • Class members that should be accessible by subclasses only are declared as protected. 5

protected • To allow class Programmer to access the members of class Person, we

protected • To allow class Programmer to access the members of class Person, we would define: class Person { protected: std: : string _name; int _id; static constexpr int NO_ID_VAL= -1; public: . . . 6

Programmer class Base class #include "Person. hpp" class Programmer : public Person { Derived

Programmer class Base class #include "Person. hpp" class Programmer : public Person { Derived class std: : string _company; public: Programmer(const std: : string& name, int id, const std: : string& company); … ; 7

Programmer class implementation #include "Programmer. hpp" Programmer: : Programmer (const std: : string& name,

Programmer class implementation #include "Programmer. hpp" Programmer: : Programmer (const std: : string& name, int id, const std: : string& company) : Person(name, id), _company(company) { // EMPTY Considered elegant } 8

Objects of Programmer can use Person’s methods int main() { Programmer yoram("Yoram", 1226611, "N.

Objects of Programmer can use Person’s methods int main() { Programmer yoram("Yoram", 1226611, "N. G. C ltd. "); cout << yoram. get. Company() << endl; yoram. change. Company("Microsoft"); cout << yoram. get. Name() << " " << yoram. get. Id() << endl; yoram. change. Name("Yori"); yoram. change. Id(2266110); . . . } 9

Functions you don’t inherit: • Ctors, Dtors (may be called automatically) • Operator= (may

Functions you don’t inherit: • Ctors, Dtors (may be called automatically) • Operator= (may be called automatically) 10

public, protected and private inheritance

public, protected and private inheritance

public, protected and private inheritance A base class also has an access modifier: class

public, protected and private inheritance A base class also has an access modifier: class Programmer : public Person or class Programmer : protected Person or class Programmer : private Person Private inheritance: • Inside Programmer you can access Person public members • Objects of Programmer do not have Person members 12

public, protected and private inheritance A base class also has an access modifier: class

public, protected and private inheritance A base class also has an access modifier: class Programmer : public Person or class Programmer : protected Person or class Programmer : private Person Default for structs Default for classes Private inheritance: • Inside Programmer you can access Person public members • Objects of Programmer do not have Person members 13

public, protected and private inheritance class Base {…}; class Public. Derived : public Base{…};

public, protected and private inheritance class Base {…}; class Public. Derived : public Base{…}; class Protected. Derived : protected Base{…}; class Private. Derived : private Base{…}; Access rules: (1) No access to Base private section from derived classes (2) Public. Derived inherits public Base members as public, and protected members as protected (3) Protected. Derived inherits public and protected Base members as protected (4) Private. Derived inherits public and protected Base members as private 14

public inheritance – regular Is-A 15

public inheritance – regular Is-A 15

private inheritance – can, but often shouldn’t, be used as a replacement for composition

private inheritance – can, but often shouldn’t, be used as a replacement for composition (code reuse, implementation) Prefer composition! 16

private/protected are in class level and not in instance level class A { private:

private/protected are in class level and not in instance level class A { private: int _i; public: A(int i) : _i(i) {} void set. Ifrom. A(A a) { _i = a. _i; ok } }; 17 int main () { A a(5); A b(2); b. set. Ifrom. A(a); }

C-tor & D-tor order of execution

C-tor & D-tor order of execution

C-tor & D-tor order of execution 1. Constructor of the base class is executed

C-tor & D-tor order of execution 1. Constructor of the base class is executed 2. Constructor of the class itself is executed Destruction is done in the opposite order 19

C-tor & D-tor order of execution 1. Constructor of the base class is executed

C-tor & D-tor order of execution 1. Constructor of the base class is executed 1. First members in initialization list 2. Then body 2. Constructor of the class itself is executed 1. First members in initialization list 2. Then body Destruction is done in the opposite order 20

C-tor & D-tor order of execution class A { int _a; public: A(int a)

C-tor & D-tor order of execution class A { int _a; public: A(int a) : _a(a) { cout << "A ctorn"; } ~A() { cout << "A dtorn"; } }; class B : public A { int _b; public: B(int a, int b) : A(a), _b(b) { cout << “B ctorn"; } ~B() { cout << “B dtorn“; } };

C-tor & D-tor order of execution int main() { B b(1, 2); } What

C-tor & D-tor order of execution int main() { B b(1, 2); } What will be the output?

C-tor & D-tor order of execution int main() { B b(1, 2); } What

C-tor & D-tor order of execution int main() { B b(1, 2); } What will be the output? A ctor B dtor A B

Overriding

Overriding

Person class Person { . . . void output. Details(std: : ostream& os) const;

Person class Person { . . . void output. Details(std: : ostream& os) const; . . . } ; 25

Programmer class – Override #include "Person. hpp" class Programmer : public Person { .

Programmer class – Override #include "Person. hpp" class Programmer : public Person { . . . void output. Details(std: : ostream& os) const; . . . }; 26

Overridden member functions void Person: : output. Details(std: : ostream& os) const { os

Overridden member functions void Person: : output. Details(std: : ostream& os) const { os << "{"; if(_name != "") os << " name: " << _name; if(_id != NO_ID_VAL) os << " ID: " << _id; os << '}'; } void Programmer: : output. Details(std: : ostream& os) const { Person: : output. Details(os); 27 os << '-' << _company << '}'; }

Default Operator= not inherited but the default one uses the father’s automatically 28

Default Operator= not inherited but the default one uses the father’s automatically 28

Explicit Operator= Person& Person: : operator=(const Person& other) { . . . return *this;

Explicit Operator= Person& Person: : operator=(const Person& other) { . . . return *this; } Programmer& Programmer: : operator=(const Programmer& other) { Person: : operator=(other); . . . 29 return *this; }

Virtual Classes & Polymorphism

Virtual Classes & Polymorphism

Example (revisited) • We want to implement a graphics system • We plan to

Example (revisited) • We want to implement a graphics system • We plan to have lists of shape. Each shape should be able to draw itself, compute its size, etc. 31

Solution #1 class Shape { public: enum Type { Square, Circle, Triangle }; Shape(

Solution #1 class Shape { public: enum Type { Square, Circle, Triangle }; Shape( Type t, const Point& Center, double width, double height); void draw() const; double area() const; private: Type _type; Point _center; }; 32

Solution #1 This solution gives a nice “wrapping” to the solution we consider in

Solution #1 This solution gives a nice “wrapping” to the solution we consider in C It does not solve the problems of that solution �Lack of extendibility 33

Solution #2 – different classes class Square { public: void draw() const; double area()

Solution #2 – different classes class Square { public: void draw() const; double area() const; }; 34 class Circle { public: void draw() const; double area() const; };

Solution #2 – Discussion �Each shape has its own implementation �We can easily add

Solution #2 – Discussion �Each shape has its own implementation �We can easily add new shapes However, �Shapes are different types �One cannot view them as part of the same class - we cannot share code between them 35

Solution #3 – hierarchy class Shape { public: void draw() const {cout<<'h'; } double

Solution #3 – hierarchy class Shape { public: void draw() const {cout<<'h'; } double area() const; }; class Square: public Shape { public: void draw() const {cout<<'q'; } 36 double area() const; }; class Circle: public Shape { public: void draw() const; {cout<<'c'; } double area() const; };

Solution #3 Now if we write Shape my. Shapes[2]; my. Shapes[0] = Circle(); my.

Solution #3 Now if we write Shape my. Shapes[2]; my. Shapes[0] = Circle(); my. Shapes[1] = Square(); What will happen? 37

Solution #3 Now if we write Shape my. Shapes[2]; my. Shapes[0] = Circle(); my.

Solution #3 Now if we write Shape my. Shapes[2]; my. Shapes[0] = Circle(); my. Shapes[1] = Square(); What will happen? The Circle and Square will be constructed and then sliced to fit inside the Shape objects 38

Solution #3 Now if we write (like in Java): Shape* my. Shapes[2]; my. Shapes[0]

Solution #3 Now if we write (like in Java): Shape* my. Shapes[2]; my. Shapes[0] = new Circle(); my. Shapes[1] = new Square(); What will happen when we call my. Shapes[0]->draw(); ? 39

Solution #3 Now if we write (like in Java): Shape* my. Shapes[2]; my. Shapes[0]

Solution #3 Now if we write (like in Java): Shape* my. Shapes[2]; my. Shapes[0] = new Circle(); my. Shapes[1] = new Square(); What will happen when we call my. Shapes[0]->draw(); ? h will be printed! 40

Why? When we write: Circle circle; circle. draw(); The compiler calls Circle: : draw()

Why? When we write: Circle circle; circle. draw(); The compiler calls Circle: : draw() When we write: Shape* p = new Circle(); p->draw(); The compiler calls Shape: : draw() Why? *p has type Shape 41

Static resolution (aka early/static binding/resolution) 42

Static resolution (aka early/static binding/resolution) 42

Static resolution How does the compiler determine which method to call? Static Resolution: Based

Static resolution How does the compiler determine which method to call? Static Resolution: Based on the type of the variable. �Not the type of the object! The compiler finds the most specific implementation of the method, and calls it 43

Static resolution in our example Circle* circle = new Circle(1, 1, 2); Square* square

Static resolution in our example Circle* circle = new Circle(1, 1, 2); Square* square = new Square(2, 2, 1); Shape* my. Shapes[2]; my. Shapes[0] = circle; my. Shapes[1] = square; circle->draw(); // Calls Circle: : Draw() square->draw(); // Calls Square: : Draw() my. Shapes[0]->draw(); // Calls Shape: : Draw() my. Shapes[1]->draw(); // Calls Shape: : Draw() 44

Underneath the Hood: Inheritance class Base { double _x; int _a; }; class Derived

Underneath the Hood: Inheritance class Base { double _x; int _a; }; class Derived : public Base { double _z; }; 45 Base a; Derived b;

Underneath the Hood: Inheritance class Base { double _x; int _a; }; class Derived

Underneath the Hood: Inheritance class Base { double _x; int _a; }; class Derived : public Base { double _z; }; Base a; Derived b; a: _x _a b: _x _a _z 46 Base

Pointing to an Inherited Class Derived b; Base* p = &b; p: When using

Pointing to an Inherited Class Derived b; Base* p = &b; p: When using *p, we treat b as though it was a Base object The compiler cannot know if *p is from a derived class or not 47 b: _x _a _z

Dynamic Resolution (aka late/dynamic binding/resolution) 48

Dynamic Resolution (aka late/dynamic binding/resolution) 48

Dynamic Resolution Static resolution is clearly not what we want to in this example

Dynamic Resolution Static resolution is clearly not what we want to in this example More desirable here is – Dynamic resolution: • Based on the type of the object • Determined at run time [Java Like] 49

Dynamic Resolution in C++ The virtual keyword states that the method can be overridden

Dynamic Resolution in C++ The virtual keyword states that the method can be overridden in a dynamic manner. class Base { public: virtual void bar(); } class Derived: public Base { public: virtual void bar(); } 50

dynamic resolution Returning to the shapes example, using virtual methods gives the desired result

dynamic resolution Returning to the shapes example, using virtual methods gives the desired result 51

dynamic resolution class Shape class Circle: public Shape { { public: virtual void draw()

dynamic resolution class Shape class Circle: public Shape { { public: virtual void draw() const {cout<<'h'; } {cout<<'c'; } virtual double area() const; }; class Square: public Shape { public: virtual void draw() const {cout<<'q'; } virtual double area() const; }; 52

dynamic resolution Returning to the shapes example, using virtual methods gives the desired result:

dynamic resolution Returning to the shapes example, using virtual methods gives the desired result: Shape* s=new Circle; s->draw(); Will print 'c' 53

Virtual Methods Class Base defines a virtual method foo() The resolution of foo() is

Virtual Methods Class Base defines a virtual method foo() The resolution of foo() is dynamic in all subclasses of Base �If the subclass Derived overrides foo(), then Derived: : foo() is called �If not, Base: : foo() is called 54

With references struct B { virtual void f() { cout << "B" << endl;

With references struct B { virtual void f() { cout << "B" << endl; } }; struct D : public B { virtual void f() { cout << "D" << endl; } }; 55 int main() { D d; B b= d; b. f(); //B B& bref= d; bref. f(); //D }

Base function that calls virtual function struct B{ virtual void f() { cout<< "Base

Base function that calls virtual function struct B{ virtual void f() { cout<< "Base f()" <<endl; } void g() { f(); } }; struct D : public B { virtual void f() { cout<< “Derived f()" <<endl; } }; int main(){ D d; d. g() will print “Derived f()”. Why? ? 56

Base function that calls virtual function struct B{ virtual void f() { cout<< "Base

Base function that calls virtual function struct B{ virtual void f() { cout<< "Base f()" <<endl; } void g(B* this) {this->f(); } }; struct D : public B { virtual void f() { cout<< “Derived f()" <<endl; } }; int main(){ D d; B: : g(&d) will print “Derived f()”. Why? ? 57

Calling virtual function from a constructor struct B { B() { f(); } virtual

Calling virtual function from a constructor struct B { B() { f(); } virtual void f(){ cout<<“Base f()”<<endl; } }; struct D : public B { virtual void f(){ cout<<“Derived f()”<<endl; } }; int main(){ D d; // would print “Base f()” Conclusion: Do not call virtual functions from a constructor! 58

Calling virtual function from a destructor struct B { ~B() { f(); } virtual

Calling virtual function from a destructor struct B { ~B() { f(); } virtual void f(){ cout<<“Base f()”<<endl; } }; struct D : public B { virtual void f(){ cout<<“Derived f()”<<endl; } }; int main(){ D d; // would print “Base f()” Conclusion: Do not call virtual functions from a destructor! 59

Polymorphism rules: When calling a method, polymorphism will take place if: � We call

Polymorphism rules: When calling a method, polymorphism will take place if: � We call a method through pointer or reference to a base class that actually points to a derived object. � The method must be virtual. � The derived class must override the base method with exactly the same signature (C++11 use override to check that the method really overrides in compile time) 60

Implementation of Virtual Methods (under the hood) 61

Implementation of Virtual Methods (under the hood) 61

Implementation of Virtual Methods Possible approach: �If foo() is a virtual method, then each

Implementation of Virtual Methods Possible approach: �If foo() is a virtual method, then each object has a pointer to the implementation of foo() that it uses �Can be implemented by using array of pointers to functions Cost: �Each virtual method requires a pointer �Large number of virtual methods � waste of memory 62

Implementation of Virtual Methods Alternative solution: �Each object has a single pointer to an

Implementation of Virtual Methods Alternative solution: �Each object has a single pointer to an array of function pointers �This array points to the appropriate functions Cost: �For each class, we store one table �Each object contains one field that points to the right table 63

class A { public: virtual void f 1(); virtual void f 2(); int _a;

class A { public: virtual void f 1(); virtual void f 2(); int _a; }; class B: public A *a 1: { public: virtual void f 1(); virtual void f 3(); *a 2: void f 4(); int _b; }; *a 3: A* a 1= new A; A* a 2= new A; A* a 3= new B; 64 void A: : f 1 { //. . . }; <vptr> _a _b VTBLs A f 1 f 2 B f 1 f 2 f 3 void A: : f 2 { //. . . }; void B: : f 1 { //. . . }; void B: : f 3 { //. . . };

Through *a 3 everything below the class A { red dashed line will be

Through *a 3 everything below the class A { red dashed line will be hidden public: virtual void f 1(); (you can downcast to a different virtual void f 2(); name, later) int _a; }; class B: public A *a 1: { public: virtual void f 1(); virtual void f 3(); *a 2: void f 4(); int _b; }; *a 3: A* a 1= new A; A* a 2= new A; A* a 3= new B; 65 <vptr> _a _b VTBLs A f 1 f 2 B f 1 f 2 f 3 void A: : f 1 { //. . . }; void A: : f 2 { //. . . }; void B: : f 1 { //. . . }; void B: : f 3 { //. . . };

Calling virtual function from a ctor/dtor explained �When the code to the ctor/dtor is

Calling virtual function from a ctor/dtor explained �When the code to the ctor/dtor is generated its generated to its class and not for a different class �Thus, the vptr will be to the vtable of the same class 66

Virtual - Recap �Virtual controls whether to use static or dynamic resolution �Once a

Virtual - Recap �Virtual controls whether to use static or dynamic resolution �Once a method is virtual, it must remain so throughout the hierarchy �Works only with pointers or references �Calling a virtual method is more expensive than standard calls �Two pointers are “chased” to get to the address of the function �No inlining 67

Destructors & Inheritance 68

Destructors & Inheritance 68

Destructors & Inheritance class Base { public: ~Base(); }; class Derived : public Base

Destructors & Inheritance class Base { public: ~Base(); }; class Derived : public Base { public: ~Derived(); }; Base *p = new Derived; delete p; Which destructor is called? (Base: : ~Base()) 69

Virtual Destructor • Destructor is like any other method • The example uses static

Virtual Destructor • Destructor is like any other method • The example uses static resolution, and hence the wrong destructor is called • To fix that, we need to declare virtual destructor at the base class! Once you declare virtual destructor, derived class must declare a destructor 70

Polymorphism & Inheritance - recap 71

Polymorphism & Inheritance - recap 71

Polymorphism & Inheritance - recap • C++ allows to use class hierarchy to implement

Polymorphism & Inheritance - recap • C++ allows to use class hierarchy to implement polymorphic code Points of care: • Choice of virtual methods �Run time considerations • Use of virtual destructor for base class 72

Example Revisiting our example, we write: class Shape { public: virtual ~Shape(); virtual void

Example Revisiting our example, we write: class Shape { public: virtual ~Shape(); virtual void draw() const; virtual double area() const; }; 73 How do we implement Shape: : draw() ?

Inheritance & Interfaces • In this example, we never want to deal with objects

Inheritance & Interfaces • In this example, we never want to deal with objects of type Shape �Shape serves the role of an interface • All shapes need to be specific shapes that are instances of derived classes of Shape • How do we enforce this? Simple runtime mechanism: void Shape: : draw() const { assert(false); // we should never //call this method } 74

Pure Virtual We can specify that if we want the derived class to be

Pure Virtual We can specify that if we want the derived class to be non-abstract, draw() must be implemented in it, class Shape { public: virtual ~Shape() {}; // pure virtuals virtual void draw() const = 0; virtual double area() const = 0; }; 75

Pure Virtual We cannot create objects of a Pure Virtual class – that is

Pure Virtual We cannot create objects of a Pure Virtual class – that is an object that contains at least one Pure Virtual method Shape* p; // legal Shape s; // illegal p = new Shape; // illegal Circle c; // legal p = &c; // legal 76

Pure Virtual Dtor We can specify that if we want the derived class to

Pure Virtual Dtor We can specify that if we want the derived class to be non-abstract, dtor must be implemented in it, class Shape { public: // pure virtuals virtual ~Shape()= 0; virtual void draw() const = 0; virtual double area() const = 0; }; inline Shape: : ~Shape() { } // What? ! 77

Pure Virtual Dtor We can specify that if we want the derived class to

Pure Virtual Dtor We can specify that if we want the derived class to be non-abstract, dtor must be implemented in it, however, it can be implemented in base also, destructor will be called here so we must define it! class Shape { public: virtual ~Shape()= 0; virtual void draw() const = 0; virtual double area() const = 0; }; inline Shape: : ~Shape() { } // What? ! 78

Pure Virtual Dtor Having a pure virtual destructor is not very useful as all

Pure Virtual Dtor Having a pure virtual destructor is not very useful as all that it says is that the class is abstract and derived will be also abstract unless it will override the destructor and enforcing derived objects to implement a destructor does not make much sense since it is an implementation issue and not an interface one 79

Private Pure Virtual Legal and often used, derived objects must implement but cannot use:

Private Pure Virtual Legal and often used, derived objects must implement but cannot use: class Shape { virtual void draw. Impl()= 0; static unsigned int g_num. Draws; public: void draw() const { ++g_num. Draws; draw. Impl(); } . . . 80

Virtual Methods - Tips 1. If you have virtual methods in a class, always

Virtual Methods - Tips 1. If you have virtual methods in a class, always declare its destructor virtual 2. Never call virtual methods during construction and destruction 3. Use pure virtual classes without any fields to define interfaces 4. Use inheritance with polymorphism with care: Be sure that this is the appropriate solution ("is a" relation) 81

Interfaces • To create an equivalent to java interface – declare a base class

Interfaces • To create an equivalent to java interface – declare a base class with all methods pure virtual and no fields • Inheritance can be used to hide implementation. But, you will need a factory, and with templates also pimpl pattern (like we did in C’s List) 82

C++ pimpl In List. hpp file: In List. cpp file: struct Ilist: public List{

C++ pimpl In List. hpp file: In List. cpp file: struct Ilist: public List{ struct List { virtual void add() { virtual void add()=0; cout << "Add"; virtual ~List(){}; } static List* create(); virtual ~IList(){}; }; }; List* List: : create() { In main. cpp: return new IList; List* lp= List: : create(); } lp->add(); delete lp; 83

C++ pimpl See: http: //en. cppreference. com/w/cpp/language/pimpl for advanced material on pimpl in C++

C++ pimpl See: http: //en. cppreference. com/w/cpp/language/pimpl for advanced material on pimpl in C++ 84

Method hiding

Method hiding

Method hiding (1) struct B { (virtual) void f(bool i) {cout << "bool" <<

Method hiding (1) struct B { (virtual) void f(bool i) {cout << "bool" << endl; } }; struct D : public B { void f(int b) {cout << "int" << endl; } }; int main(){ D d; d. f(3); // prints ‘int’ d. f(true); // prints ‘int’ } 86

Method hiding (2) struct B { (virtual) void f(bool i) {cout << "bool" <<

Method hiding (2) struct B { (virtual) void f(bool i) {cout << "bool" << endl; } }; struct D : public B { void f(int b) {cout << "int" << endl; } }; int main(){ D d; d. B: : f(true); // prints ‘bool’ d. B: : f(3); // prints ‘bool’ } 87

Method hiding (3) struct B { (virtual) void f(bool i) {cout << "bool" <<

Method hiding (3) struct B { (virtual) void f(bool i) {cout << "bool" << endl; } }; struct D : public B { using B: : f; void f(int b) {cout << "int" << endl; } }; int main(){ D d; d. f(3); // prints ‘int’ d. f(true); // prints ‘bool’ } 88

Multiple inheritance and virtual base class 89

Multiple inheritance and virtual base class 89

Multiple inheritance • A class can inherit from multiple classes: struct input. File{ void

Multiple inheritance • A class can inherit from multiple classes: struct input. File{ void read(); }; struct output. File{ void write(); }; struct io. File : public input. File, public output. File{ … }; // in main io. File f; f. read(); f. write(); 90

Multiple inheritance order • Construction and destruction order are according to the inheritance list:

Multiple inheritance order • Construction and destruction order are according to the inheritance list: struct input. File{ input. File(){cout<<“input. File ctor ”; } }; struct output. File{ output. File(){cout<<“output. File ctor ”; } }; struct io. File: public input. File, public output. File{ io. File(){cout<<“io. File ctor ”; } }; // in main io. File f; //prints: input. File ctor output. File ctor io. File ctor 91

Multiple inheritance • Name ambiguities will generate compile error. • In the following example

Multiple inheritance • Name ambiguities will generate compile error. • In the following example io. File has two instances of open() struct input. File{ void open(); }; struct output. File{ void open(); }; struct io. File: public input. File, public output. File{ … }; // in main io. File f; f. open(); //error! f. input. File: : open(); //Ok! 92

Diamond Multiple Inheritance struct file{ file char* name; void open(); input. File } struct

Diamond Multiple Inheritance struct file{ file char* name; void open(); input. File } struct input. File : public file{ void read(); io. File }; struct output. File : public file{ void write(); }; struct io. File: public input. File, public output. File{}; // in main io. File f; f. open(); //error! f. input. File: : open(); //Ok! 93 ** io. File still has two instances of open() output. File

Diamond Multiple Inheritance struct file{ char* name; void open(); } struct input. File :

Diamond Multiple Inheritance struct file{ char* name; void open(); } struct input. File : public file{ void read(); }; struct output. File : public file{ void write(); }; struct io. File: public input. File, public output. File{}; // in main io. File f; f. name= “file. A. txt”; // error! f. input. File: : name=“file. A. txt”; // Ok! f. output. File: : name=“file. B. txt”; // Ok! Does not change input. File: : name 94

Virtual Inheritance struct file{ char* name; void open(); } struct input. File : virtual

Virtual Inheritance struct file{ char* name; void open(); } struct input. File : virtual public file { void read(); }; struct output. File : virtual public file { void write(); }; struct io. File: public input. File, public output. File {}; // in main io. File f; f. open(); // Ok! // io. File has one instance of open() and name 95

Virtual inheritance: The base construction problem struct file{ file(char* name){…} char* _name; struct input.

Virtual inheritance: The base construction problem struct file{ file(char* name){…} char* _name; struct input. File: virtual public file{ input. File(char* name): file(name){} }; struct output. File: virtual public file{ output. File(char* name): file(name){} }; struct io. File: public input. File, public output. File{ io. File(char* name): input. File(name), output. File(name){} }; Problem: File ctor will be initialized twice! 96

Virtual inheritance – the solution: struct file{ file(char* name){…} char* _name; struct input. File:

Virtual inheritance – the solution: struct file{ file(char* name){…} char* _name; struct input. File: virtual public file{ input. File(char* name): file(name){} }; struct output. File: virtual public file{ output. File(char* name): file(name){} }; struct io. File: public input. File, public output. File{ io. File(char* name): file(name), input. File(name), output. File(name){} }; Solution: the base class is initialized by the most derived class 97

Virtual Base Class - D has to initialize A ! A constructors for virtual

Virtual Base Class - D has to initialize A ! A constructors for virtual base classes anywhere in your class's inheritance hierarchy are called by the "most derived" class's constructor B 2 B 3 C D 98

Interim Summary • A known problem, easy to misuse. • Usually restrict yourself to

Interim Summary • A known problem, easy to misuse. • Usually restrict yourself to “interface like” multiple inheritance: • <=1 "real" base and • >=0 "interface" like (only pure virtual functions (no data members and no implementation) 99

C-tors execution order • virtual base classes anywhere in the hierarchy. • • After

C-tors execution order • virtual base classes anywhere in the hierarchy. • • After all virtual base class constructors are finished: from base class to derived class. • 100 They are executed in the order they appear in a depth -first left-to-right traversal of the graph of base classes, where left to right refer to the order of appearance of base class names The order is determined by the order that the base classes appear in the declaration of the class, not in the order that the initializer appears in the derived class's initialization list (compilers often give warnings)

Consider the following case: struct first. Generation 1{ first. Generation 1(){cout<<"first gen 1n"; }

Consider the following case: struct first. Generation 1{ first. Generation 1(){cout<<"first gen 1n"; } }; struct first. Generation 2{ first. Generation 2(){cout<<"first gen 2n"; } }; struct second. Generation 1: public first. Generation 1{ second. Generation 1(){cout<<"snd gen 1n"; } }; struct second. Generation 2: public first. Generation 2{ second. Generation 2(){cout<<"snd gen 2n"; } }; struct third. Generation 1: public second. Generation 1, virtual public second. Generation 2{ third. Generation 1(){cout<<"third. Generation 1n"; } }; struct fourth. Generation 1: public third. Generation 1{ fourth. Generation 1(){cout<<"fourth. Generation 1n"; } }; int main() { fourth. Generation 1 f; 101

The inheritance graph: first. Generation 1 first. Generation 2 second. Generation 1 second. Generation

The inheritance graph: first. Generation 1 first. Generation 2 second. Generation 1 second. Generation 2 third. Generation 1 fourth. Generation 1 • fourth. G 1 calls second. G 2 calls first. G 2 • fourth. G 1 calls third. G 1 calls second. G 1 calls first. G 1 • Output is: first. G 2, second. G 2, first. G 1, second. G 1, third. G 1, fourth. G 1 102

Polymorphism vs. Templates

Polymorphism vs. Templates

Polymorphism vs. Templates • Templates compilation time is much longer than using inheritance. •

Polymorphism vs. Templates • Templates compilation time is much longer than using inheritance. • Using templates enlarge the code size. • Compilation errors can be very confusing. • Templates running time is much faster than using inheritance. • Combined with inlining, templates can reduce runtime overhead to zero. 104

Polymorphism vs. Templates • Rule of thumb: • Templates are better for containers and

Polymorphism vs. Templates • Rule of thumb: • Templates are better for containers and standard algorithms (e. g. , stl) • Inheritance and polymorphism are better for user’s application logic description • You can actually combine them. • As always – think of your task. 105

C++ casting, RTTI

C++ casting, RTTI

C style casting: • double d = 3. 0; int i = (int) d;

C style casting: • double d = 3. 0; int i = (int) d; • const int *c. P = &i; int *nc. P = (int*)cp; • double * d. P = (double*)ncp; • Base *base. P 1 = new Derived; Base *base. P 2 = new Base Derived *der. P = (der. P*) base. P; Derived *der. P 2 = (der. P*) base. P 2; 107

C++ style casting �static_cast<type>(expression) �const_cast<type>(expression) �reinterpret_cast<type>(expression) �dynamic_cast<type>(expression) 108

C++ style casting �static_cast<type>(expression) �const_cast<type>(expression) �reinterpret_cast<type>(expression) �dynamic_cast<type>(expression) 108

The ‘static_cast’ operator static_cast<type>(expression) Could be used to perform any cast there is a

The ‘static_cast’ operator static_cast<type>(expression) Could be used to perform any cast there is a known method for Should be used for: �any cast that would have worked implicitly �standard or user-defined conversion �up-casts �when you are sure the cast is safe (e. g. , reversing an upcast) Safer that “old-style” casts �e. g. won’t cast int* to float* Failure causes a compiler error �No dynamic checking is done int i = static_cast<int>(12. 45); 109

The ‘const_cast‘ operator const_cast<type>(expression) Is used to remove const-ness: void g(C * cp); void

The ‘const_cast‘ operator const_cast<type>(expression) Is used to remove const-ness: void g(C * cp); void f(C const* cp) { g(const_cast<C *>(cp)); } • Usually, you should design your variables/methods such that you won’t have to use const_cast. • Compile time operator • Can cause serious trouble 110

‘reinterpret_cast‘ operator reinterpret_cast<type>(expression) Is used to reinterpret byte patterns. double d(10. 2); char* d.

‘reinterpret_cast‘ operator reinterpret_cast<type>(expression) Is used to reinterpret byte patterns. double d(10. 2); char* d. Bytes = reinterpret_cast<char *>(&d); • Circumvents the type checking of c++. • Very implementation-dependent. • (Should be) used rarely. • Very dangerous! 111

‘reinterpret_cast‘ operator reinterpret_cast<type>(expression) Reminder: binary_output in stl_code 112

‘reinterpret_cast‘ operator reinterpret_cast<type>(expression) Reminder: binary_output in stl_code 112

Run Time Type Information (RTTI) 113

Run Time Type Information (RTTI) 113

Run Time Type Information (RTTI) – why? Problem: Up-casting works fine. �Treating sub-class as

Run Time Type Information (RTTI) – why? Problem: Up-casting works fine. �Treating sub-class as base class: Shape * s = new Circle(); Line What about down-casting? �might not be safe! �correctness cannot be determined by the compiler. Circle * c = (Circle*) s; 114 Circle

RTTI – Run Time Type Information Mechanisms for RTTI: 1. dynamic_cast operator 2. typeid

RTTI – Run Time Type Information Mechanisms for RTTI: 1. dynamic_cast operator 2. typeid operator (and type_info class) 115

The ‘dynamic_cast‘ operator dynamic_cast<T>(expression) Enables run-time type checking: When expression is a pointer: �Returns

The ‘dynamic_cast‘ operator dynamic_cast<T>(expression) Enables run-time type checking: When expression is a pointer: �Returns a valid pointer if expression really points to type T �null pointer value otherwise 116

The ‘dynamic_cast‘ operator dynamic_cast<T>(expression) Enables run-time type checking: When expression is a reference: �Returns

The ‘dynamic_cast‘ operator dynamic_cast<T>(expression) Enables run-time type checking: When expression is a reference: �Returns a valid reference if expression is really of type T �Throws an exception when it fails (“bad_cast”) 117

Dynamic_cast : example Shape* s = container. pop(); Circle* c = dynamic_cast<Circle*>(s); if (c

Dynamic_cast : example Shape* s = container. pop(); Circle* c = dynamic_cast<Circle*>(s); if (c != nullptr) {// c is a circle c->set. Radius(42); } else { . . . } 118

dynamic_cast - more dynamic_cast<T>(expression) Note: • Used only on pointer or reference types. •

dynamic_cast - more dynamic_cast<T>(expression) Note: • Used only on pointer or reference types. • Can be used for up-cast, down-cast, cross-cast • Only for types with virtual-functions (“Polymorphic types”) • These object have a space for the information about the type: the virtual function table 119

dynamic_cast: only for polymorphics class Circle : public Shape { virtual void draw(); }

dynamic_cast: only for polymorphics class Circle : public Shape { virtual void draw(); } class Date : public Time { // Time has no virtual functions } void foo(Shape * s, Time * t) { Circle * c = dynamic_cast<Circle*>( s ); //ok Date * date = dynamic_cast<Date*>( t ); //compilation error } 120

RTTI : typeid operator 121

RTTI : typeid operator 121

RTTI : typeid operator • Obtains info about an object/expression usage: typeid( obj )

RTTI : typeid operator • Obtains info about an object/expression usage: typeid( obj ) (like “sizeof”) Example: Dog d; Cat c; cout << "d is a " << typeid(d). name() << ", c is a " << typeid(c). name() <<endl; Output (might be): d is a Dog, c is a Cat 122

RTTI : typeid operator • Obtains info about an object/expression usage: typeid( obj )

RTTI : typeid operator • Obtains info about an object/expression usage: typeid( obj ) (like “sizeof”) Example: Dog d; Cat c; cout << "d is a " << typeid(d). name() << ", c is a " << typeid(c). name() <<endl; Output (becase it does not have to be the name of the type that the programmer used, it might also be): d is a 3 Dog, c is a 3 Cat 123

RTTI misuse void rotate( shape const& s ) { if (typeid(s) == typeid(Circle) )

RTTI misuse void rotate( shape const& s ) { if (typeid(s) == typeid(Circle) ) //do nothing else if (typeid(s) == typeid(Triangle) ) //rotate Triangle else if (typeid(s) == typeid(Rectangle) ) //rotate Rectangle } • Use virtual functions (i. e. polymorphism) when you can ! • Misuse of RTTI 124