Presentation is loading. Please wait.

Presentation is loading. Please wait.

Inheritance.

Similar presentations


Presentation on theme: "Inheritance."— Presentation transcript:

1 Inheritance

2 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

3 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…

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

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

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

7 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

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

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

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

11 public, protected and private inheritance

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

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

14 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

15 public inheritance – regular Is-A

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

17 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

18 C-tor & D-tor order of execution

19 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

20 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

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

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

23 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

24 Overriding

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

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

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

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

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

30 Virtual Classes & Polymorphism

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

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

33 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

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

35 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

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

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

38 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

39 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(); ?

40 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!

41 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

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

43 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

44 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()

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

46 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

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

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

49 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]

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

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

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

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

54 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

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

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

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

58 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!

59 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!

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

61 Implementation of Virtual Methods (under the hood)

62 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

63 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

64 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 { //... };

65 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 { //... };

66 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

67 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

68 Destructors & Inheritance

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

70 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

71 Polymorphism & Inheritance - recap

72 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

73 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() ?

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

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

76 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

77 Pure Virtual Dtor inline Shape::~Shape() { } // What?!
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?!

78 Pure Virtual Dtor inline Shape::~Shape() { } // What?!
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?!

79 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

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

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

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

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

84 C++ pimpl See: for advanced material on pimpl in C++

85 Method hiding

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

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

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

89 Multiple inheritance and virtual base class

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

91 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

92 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!

93 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

94 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

95 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

96 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!

97 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

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

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

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

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

102 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

103 Polymorphism vs. Templates

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

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

106 C++ casting, RTTI

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

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

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

110 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

111 ‘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!

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

113 Run Time Type Information (RTTI)

114 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

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

116 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

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

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

119 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

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

121 RTTI : typeid operator

122 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

123 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

124 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


Download ppt "Inheritance."

Similar presentations


Ads by Google