Inheritance.

Slides:



Advertisements
Similar presentations
The C ++ Language BY Shery khan. The C++ Language Bjarne Stroupstrup, the language’s creator C++ was designed to provide Simula’s facilities for program.
Advertisements

CPA: C++ Polymorphism Copyright © 2007 Mohamed Iqbal Pallipurath Overview of C++ Polymorphism Two main kinds of types in C++: native and user-defined –User-defined.
. Virtual Classes & Polymorphism. Example (revisited) u We want to implement a graphics system u We plan to have lists of shape. Each shape should be.
Polymorphism From now on we will use g++!. Example (revisited) Goal: Graphics package Handle drawing of different shapes Maintain list of shapes.
Rossella Lau Lecture 8, DCO10105, Semester B, DCO10105 Object-Oriented Programming and Design  Lecture 8: Polymorphism & C++ pointer  Inheritance.
. Plab – Tirgul 11 RTTI, Binary files. RTTI – why? Problem: u Up-casting works fine.  Treating sub-class as base class Shape * s = new Circle(); u What.
Run-time type information (RTTI) and casts Consider classes for components and windows: class Component {... virtual void draw() {} }; class Window: public.
Run Time Type Information, Binary files. RTTI – why? Problem: Up-casting works fine. –Treating sub-class as base class Shape * s = new Circle(); What.
1 CSE 303 Lecture 24 Inheritance in C++, continued slides created by Marty Stepp
Virtual Functions Junaed Sattar November 10, 2008 Lecture 10.
(c) University of Washington03-1 CSC 143 Java Inheritance Reading: Ch. 10.
Computer Science and Software Engineering University of Wisconsin - Platteville 7. Inheritance and Polymorphism Yan Shi CS/SE 2630 Lecture Notes.
1 Data Structures - CSCI 102 CS102 C++ Polymorphism Prof Tejada.
Inheritance in C++ CS-1030 Dr. Mark L. Hornick.
CSE 425: Object-Oriented Programming II Implementation of OO Languages Efficient use of instructions and program storage –E.g., a C++ object is stored.
Copyright © 2011 Pearson Education, Inc. Publishing as Pearson Addison-Wesley Taken from slides of Starting Out with C++ Early Objects Seventh Edition.
Polymorphism &Virtual Functions
Polymorphism &Virtual Functions 1. Polymorphism in C++ 2 types ▫Compile time polymorphism  Uses static or early binding  Example: Function and operator.
“is a”  Define a new class DerivedClass which extends BaseClass class BaseClass { // class contents } class DerivedClass : BaseClass { // class.
Programming in Java Unit 2. Class and variable declaration A class is best thought of as a template from which objects are created. You can create many.
Object Oriented Programming with C++/ Session 6 / 1 of 44 Multiple Inheritance and Polymorphism Session 6.
1 Inheritance. 2 Why use inheritance?  The most important aspect of inheritance is that it expresses a relationship between the new class and the base.
Polymorphism and Virtual Functions. Topics Polymorphism Virtual Functions Pure Virtual Functions Abstract Base Classes Virtual Destructors V-Tables Run.
C# Classes and Inheritance CNS 3260 C#.NET Software Development.
CS-1030 Dr. Mark L. Hornick 1 Basic C++ State the difference between a function/class declaration and a function/class definition. Explain the purpose.
Inheritance Initialization & Destruction of Derived Objects Protected Members Non-public Inheritance Virtual Function Implementation Virtual Destructors.
Inheritance and Composition Reusing the code and functionality Unit - 04.
Overview of C++ Polymorphism
Object Oriented Programming Elhanan Borenstein Lecture #7.
Computer Science Department Inheritance & Polymorphism.
CS 342: C++ Overloading Copyright © 2004 Dept. of Computer Science and Engineering, Washington University Overview of C++ Overloading Overloading occurs.
Polymorphism & Virtual Functions 1. Objectives 2  Polymorphism in C++  Pointers to derived classes  Important point on inheritance  Introduction to.
 Virtual Function Concepts: Abstract Classes & Pure Virtual Functions, Virtual Base classes, Friend functions, Static Functions, Assignment & copy initialization,
CSCI-383 Object-Oriented Programming & Design Lecture 17.
CSE 332: C++ Overloading Overview of C++ Overloading Overloading occurs when the same operator or function name is used with different signatures Both.
Design issues for Object-Oriented Languages
Chapter 2 Objects and Classes
Inheritance Modern object-oriented (OO) programming languages provide 3 capabilities: encapsulation inheritance polymorphism which can improve the design,
Modern Programming Tools And Techniques-I
Inheritance.
CMSC 202 Polymorphism.
Advanced Programming in Java
c++ style casting At the beginning c++ supported two styles of casts:
Chapter 13: Pointers, Classes, Virtual Functions, and Abstract Classes
CS 3370 – C++ Object-oriented Programming
Advanced Program Design with C++
7. Inheritance and Polymorphism
Andy Wang Object Oriented Programming in C++ COP 3330
Inheritance and Polymorphism
Object-Oriented Programming & Design Lecture 18 Martin van Bommel
Object-Oriented Programming
Polymorphism.
Inheritance & Polymorphism
Review: Two Programming Paradigms
Polymorphism & Virtual Functions
Inheritance, Polymorphism and the Object Memory Model
Chapter 12: Pointers, Classes, Virtual Functions, and Abstract Classes
Programming with ANSI C ++
Introduction to Classes
Virtual Functions Department of CSE, BUET Chapter 10.
9: POLYMORPHISM Programming Technique II (SCSJ1023) Jumail Bin Taliba
CISC/CMPE320 - Prof. McLeod
Overview of C++ Polymorphism
Inheritance, Polymorphism and the Object Memory Model
Polymorphism 2019/4/29.
Advanced Programming in Java
四時讀書樂 (春) ~ 翁森 山光照檻水繞廊,舞雩歸詠春風香。 好鳥枝頭亦朋友,落花水面皆文章。 蹉跎莫遣韶光老,人生唯有讀書好。
Lecture 6: Polymorphism
Static Binding Static binding chooses the function in the class of the base class pointer, ignoring any versions in the class of the object actually.
Computer Science II for Majors
Presentation transcript:

Inheritance

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 C Class B Class D

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…

constexpr Both keywords can be used in the declaration of objects as well as functions. The basic difference when applied to objects is this: const declares an object as constant. This implies a guarantee that, once initialized, the value of that object won't change, and the compiler can make use of this fact for optimizations. It also helps prevent the programmer from writing code that modifies objects that were not meant to be modified after initialization. constexpr tells the compiler that this expression results in a compile time constant value, so it can be used in places like array lengths, assigning to const variables, etc. A constant-expression object behaves as if it was declared const, except that it requires initialization before use and its initializer must be a constant expression. Consequently, a constant-expression object can always be used as part of another constant expression.

Consider: const int mx = numeric_limits<int>::max(); // OK: runtime initialization The function max() merely returns a literal value. However, because the initializer is a function call, mx undergoes runtime initialization. Therefore, you cannot use it as a constant expression: int arr[mx]; // error: “constant expression required”

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 changeName(const string& name);    void changeId(int id);    std::string getName() const;    int getId() const; } ;

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

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:    ...

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

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 }

Objects of Programmer can use Person’s methods int main() { Programmer yoram("Yoram",1226611,"N.G.C ltd."); cout << yoram.getCompany() << endl; yoram.changeCompany("Microsoft"); cout << yoram.getName() << " " << yoram.getId() << endl; yoram.changeName("Yori"); yoram.changeId(2266110); ... }

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

public, protected and private inheritance

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 Default inheritance for class is private and for struct is public Shall such inheritance be public, everything which is aware of Base and Child, is also aware that Child inherits from Base. Shall such inheritance be protected, only Child (and its children), is aware that itself inherits from Base. Shall such inheritance be private, no one other than Child is aware of such inheritance.

public, protected and private inheritance Default for structs 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 Default for classes Default inheritance for class is private and for struct is public Shall such inheritance be public, everything which is aware of Base and Child, is also aware that Child inherits from Base. Shall such inheritance be protected, only Child (and its children), is aware that itself inherits from Base. Shall such inheritance be private, no one other than Child is aware of such inheritance.

public, protected and private inheritance class Base {…}; class PublicDerived : public Base{…}; class ProtectedDerived : protected Base{…}; class PrivateDerived : private Base{…}; Access rules: (1) No access to Base private section from derived classes (2) PublicDerived inherits public Base members as public, and protected members as protected (3) ProtectedDerived inherits public and protected Base members as protected (4) PrivateDerived inherits public and protected Base members as private

public inheritance – regular Is-A

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

private/protected are in class level and not in instance level class A { private: int _i; public: A(int i) : _i(i) {} void setIfromA(A a) { _i = a._i; } }; int main () { A a(5); A b(2); b.setIfromA(a); } ok אם מתודה מקבלת כפרמטר אובייקט של המחלקה, היא יכולה לגשת ל-private שלו.

C-tor & D-tor order of execution

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

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

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

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 class A { int _a; public: A(int a) : _a(a) { cout << "A ctor\n"; } ~A() { cout << "A dtor\n"; } }; class B : public A { int _b; B(int a, int b) : A(a), _b(b) { cout << “B ctor\n"; } ~B() { cout << “B dtor\n“; } B b(1,2); A, B, B, A

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

Overriding

Person class Person { ... void outputDetails(std::ostream& os) const; ... } ;

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

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

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

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

Virtual Classes & Polymorphism

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.

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; };

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

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

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

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

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

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

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

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

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

Class Hierarchy class Base { public:    void foo();    void bar(); }; class Derived: public Base { public:    void bar(); }; Derived obj; obj.foo(); Calls Base::foo() Derived does not implement this method obj.bar(); Calls Derived::bar() Two implementations, the more specific is used

Inheritance & Overloading Derived inherits the method foo() from Base The inherited class uses methods of the base class Derived overrides the implementation of bar() This mechanism allows an inherited class to specialize the implementation

Static resolution (aka early/static binding/resolution)

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

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

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

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 Base

Pointing to an Inherited Class Derived b; Base* p = &b; 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 _x _a _z b: p:

Dynamic Resolution (aka late/dynamic binding/resolution)

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]

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(); }

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

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

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

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

With references struct B { virtual void f() { cout << "B" << endl; } }; struct D : public B { virtual void f() { cout << "D" << endl; } }; 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 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??

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??

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!

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!

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)

Implementation of Virtual Methods (under the hood)

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

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

class A { public:    virtual void f1();    virtual void f2();    int _a; }; class B: public A { public:    virtual void f1();    virtual void f3();    void f4();    int _b; }; A* a1= new A; A* a2= new A; A* a3= new B; void A::f1 { //... }; VTBLs <vptr> _a *a1: void A::f2 { //... }; A f1 f2 <vptr> _a *a2: void B::f1 { //... }; B <vptr> _a _b *a3: f1 f2 f3 void B::f3 { //... };

Through *a3 everything below the red dashed line will be hidden (you can downcast to a different name, later) class A { public:    virtual void f1();    virtual void f2();    int _a; }; class B: public A { public:    virtual void f1();    virtual void f3();    void f4();    int _b; }; A* a1= new A; A* a2= new A; A* a3= new B; void A::f1 { //... }; VTBLs <vptr> _a *a1: void A::f2 { //... }; A f1 f2 <vptr> _a *a2: void B::f1 { //... }; B <vptr> _a _b *a3: f1 f2 f3 void B::f3 { //... };

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

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

Destructors & Inheritance

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

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

Polymorphism & Inheritance - recap

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

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

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 }

Pure Virtual We can specify that Shape::draw() must be implemented in derived class class Shape { public: virtual ~Shape() {}; // pure virtuals virtual void draw() const = 0; virtual double area() const = 0; };

Pure Virtual inline Shape::~Shape() { } // What?! We can specify that Shape::draw() must be implemented in derived class class Shape { public: // pure virtuals virtual ~Shape()= 0; virtual void draw() const = 0; virtual double area() const = 0; }; inline Shape::~Shape() { } // What?!

Pure Virtual inline Shape::~Shape() { } // What?! We can specify that Shape::draw() must be implemented in derived class, but 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?!

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 '}';

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

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

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)

C++ pimpl In Ilist.h file: class IList { public:    virtual void Add()=0;    virtual ~IList(){};    static Ilist* makeList(); }; In main.cpp: IList* L = IList::makeList(); In list.cpp file: class List:public IList { public:    virtual ~IList(){}; void Add()    {       cout << "Add";    } }; IList* IList::makeList() {    return new List; }

Method hiding

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’ }

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’ }

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’ }

Multiple inheritance and virtual base class

Multiple inheritance A class can inherit from multiple classes: struct inputFile{ void read(); }; struct outputFile{ void write(); struct ioFile : public inputFile, public outputFile{ … // in main ioFile f; f.read(); f.write();

Multiple inheritance order Construction and destruction order are according to the inheritance list: struct inputFile{ inputFile(){cout<<“inputFile ctor ”;} }; struct outputFile{ outputFile(){cout<<“outputFile ctor ”;} struct ioFile: public inputFile, public outputFile{ ioFile(){cout<<“ioFile ctor ”;} // in main ioFile f;//prints: inputFile ctor outputFile ctor ioFile ctor

Multiple inheritance Name ambiguities will generate compile error. In the following example ioFile has two instances of open() struct inputFile{ void open(); }; struct outputFile{ struct ioFile: public inputFile, public outputFile{ … // in main ioFile f; f.open(); //error! f.inputFile::open(); //Ok!

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

Diamond Multiple Inheritance struct file{ char* name; void open(); } struct inputFile : public file{ void read(); }; struct outputFile : public file{ void write(); struct ioFile: public inputFile, public outputFile{}; // in main ioFile f; f.name= “fileA.txt”; // error! f.inputFile::name=“fileA.txt”; // Ok! f.outputFile::name=“fileB.txt”; // Ok! Does not change inputFile::name

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

Virtual inheritance: The base construction problem struct file{ file(char* name){…} char* _name; struct inputFile: virtual public file{ inputFile(char* name):file(name){} }; struct outputFile: virtual public file{ outputFile(char* name):file(name){} struct ioFile: public inputFile, public outputFile{ ioFile(char* name):inputFile(name),outputFile(name){} Problem: File ctor will be initialized twice!

Virtual inheritance – the solution: struct file{ file(char* name){…} char* _name; struct inputFile: virtual public file{ inputFile(char* name):file(name){} }; struct outputFile: virtual public file{ outputFile(char* name):file(name){} struct ioFile: public inputFile, public outputFile{ ioFile(char* name):file(name), inputFile(name),outputFile(name){} Solution: the base class is initialized by the most derived class

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

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)

C-tors execution order virtual base classes anywhere in the hierarchy. 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 After all virtual base class constructors are finished: from base class to derived class. 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 firstGeneration1{ firstGeneration1(){cout<<"first gen1\n";} }; struct firstGeneration2{ firstGeneration2(){cout<<"first gen2\n";} struct secondGeneration1:public firstGeneration1{ secondGeneration1(){cout<<"snd gen 1\n";} struct secondGeneration2:public firstGeneration2{ secondGeneration2(){cout<<"snd gen 2\n";} struct thirdGeneration1: public secondGeneration1, virtual public secondGeneration2{ thirdGeneration1(){cout<<"thirdGeneration1\n";} struct fourthGeneration1: public thirdGeneration1{ fourthGeneration1(){cout<<"fourthGeneration1\n";} int main() { fourthGeneration1 f;

The inheritance graph: fourthGeneration1 thirdGeneration1 secondGeneration2 secondGeneration1 firstGeneration2 firstGeneration1 fourthG1calls secondG2calls firstG2 fourthG1calls thirdG1calls secondG1calls firstG1 Output is: firstG2, secondG2, firstG1, secondG1, thirdG1, fourthG1

Polymorphism vs. Templates

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.

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.

C++ casting, RTTI

C style casting: double d = 3.0; int i = (int) d; const int *cP = &i; int *ncP = (int*)cp; double * dP = (double*)ncp; Base *baseP1 = new Derived; Base *baseP2 = new Base Derived *derP = (derP*) baseP; Derived *derP2 = (derP*) baseP2;

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

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);

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

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

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

Run Time Type Information (RTTI)

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

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

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

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”)

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

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

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 }

RTTI : typeid operator

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

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 3Dog, c is a 3Cat

RTTI misuse Use virtual functions (i.e. polymorphism) when you can ! 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