Presentation is loading. Please wait.

Presentation is loading. Please wait.

Inheritance, Polymorphism and the Object Memory Model

Similar presentations


Presentation on theme: "Inheritance, Polymorphism and the Object Memory Model"— Presentation transcript:

1 Inheritance, Polymorphism and the Object Memory Model
Lecture 5 Inheritance, Polymorphism and the Object Memory Model

2 Process Memory Layout Each process consists of three distinct areas:
The Heap - stores values allocated using the new operator The Stack - stores values in activation frames The Code Segment - stores the code of all the classes used in the program executed by the process The object (an instance of a class) uses memory: Variables created using new are stored on the heap This is done during run time. While the code is being executed! Variables created without using new are stored on the stack This is done during compile time. The compiler provides an offset from StackPointer (SP) for each variable The compiler provides a contiguous block of memory for each variable

3 Object Memory Model Object are instance of a class that contains:
State: data members static data members are allocated once and shared among all class instances non-static data members are not shared Methods: member functions Methods are shared amongst all instances of the same class Their code is stored once in memory for each class They can access the data members of the calling object only Object is implemented at runtime as a region of storage in memory a contiguous block of memory object is basically a struct with methods

4 Object Memory Layout C++ has no standard way to storing variables in memory! It means each compiler may store data differently It means that same struct may have different sizes using different compilers G basic storage methodology: variable type can only reside in a memory location divisible by its size Chars can be stored in any memory address Shorts can reside in memory addresses divisible by two Integers/floats can reside in memory addresses divisible by four Double can resize in memory addresses divisible by eight And so on. Memory layout example at the website uses a different compiler Stores variables at addresses divisible by word size regardless of their type

5 Memory layout example

6 Field Alignment fields c1 and c2 are "word aligned" within the block of memory of the object: fields start on a word boundary (word=4B) memory left "wasted" compiler flag not to align fields aligning fields - easy data accessing

7 sizeof() - size used by a given type.
computed at compile-time, compiler operator return size allocated for object data-types sizeof(A) = 20 (5 words of 4 bytes).

8

9 reference to a field - compiler uses offset of field within the object
The above situation is translated to: push activation frame for new block with one variable of 20 bytes (for a1) invoke constructor of A on the address of register S (top of stack) READ [S]+12, B - address [S]+12 into register B

10 Memory Layout and Inheritance
class B extends class A fields defined in A exist in B new fields for objects of type B. block memory for objects of class B is larger than that of objects of class A.

11 first 20 bytes = structure of type A.
"look at a B value“ as if an "A value": take first part of B and "cut" to sizeof(A).

12 The Code Segment It contains code of all the classes used in the program and executed by the process Method is stored in code region allocated in the process in which the class is used It is encoded as sequence of processor instructions It is known to compiler by start address in memory

13 Method Execution Executing a method: Method Invocation:
compiler maintains a table where it keeps track of the address of each method of the class compiler invokes a method of a class method has access to internal state of object, wherever it may be! Method Invocation: parameters are pushed on stack method invoked by using CALL instruction The parameter of CALL is the address of the first instruction of the method

14 The Implicit this Parameter
How method knows where are fields of object? the compiler passes a "hidden" parameter to method call The parameter contains the address of the object The parameter is called this Example: this->foo() is called like this: foo(this) Note: static functions do not implicitly send this, as a result, the programmer has no access to the object. can be invoked without an instance: C::static_method(x) 

15 Polymorphism: use an operator or function in different ways.
Different meanings to the operators or functions (poly = many / morph = shape) 6+5 “a”+”bc” Polymorphism = essential property of OO languages

16 Static and Dynamic Function Binding
They are also called Early and Late Binding Binding To convert function calls into addresses These addresses are jumped to during code execution Static Binding Done by the compiler (linker) to directly associate the function with a machine address during compile time! Since all functions have unique addresses The linker replaces a function call with a machine language instruction This instruction tells the CPU to jump to the address of the function once executed

17 Example: Early binding

18 Early binding Main: Output:

19 Early binding Main: Output:

20 Late binding Using virtual functions Output:

21 Example: virtual functions

22 Example: virtual functions

23 s->draw() Compiler does not know the address of the function to invoke Same C++ instruction will sometimes execute: "call [Circle::draw]" "call [Rectangle::draw]" How does the compiler manage to produce the right code?

24 Dynamic Binding In this case, the compiler matches the function call with the correct function definition at runtime The compiler identifies the type of object at runtime Then matches the function call with the correct function definition By default, early binding takes place Explicit commands are used to ensure applying dynamic binding

25 Virtual functions (vtable mechanism)
Late Binding: compiler delaying the decision of method to invoke to runtime, instead of compile time. This can be achieved by declaring a virtual function The actual method invoked depends on the type of the value of the object at runtime not on the type of the value at compile time The compiler creates virtual table during compilation Assists the compiler in applying dynamic binding properly

26 invoke s->draw(): Call draw() of Rectangle or Circle by value to which s is bound at time of invocation

27 Virtual table (vtable)
how can we know which code to invoke when we call a method through an object’s pointer? value of object in memory is extended by a pointer to a table with function address table is stored explicitly in process memory (code region). There’s a table for each class that contains virtual methods.

28 vtable for class foo The block of memory of a value of type foo starts with a pointer vptr to the vtable of class foo - followed by the state of the object.

29 Invoking a virtual method
foo *d; d->m(): dereferencing d's vptr, looking up the m entry in the vtable, dereferencing that pointer to call the correct method code.

30 *((*d)[2])(d);

31 *((*d)[2])(d); ?? Assume vptr is the first element in d: d is the address of the beginning of the block of memory which stores the foo value bound to d *d is the content of the first word in the block of memory: it contains the address of the vtable (*d)[2] is the address of the 3rd element in the vtable (the address of method m) *((*d)[2])(d) - invoke function located at third slot in the vtable of foo and pass to it the address of the value

32 Inheritance and vtable
When a class extends another class, how is the vtable managed? bar extends  foo. bar overrides  m, and introduces 2 new methods s and t. 

33

34 compiler generates a new distinct vtable for class bar
compiler generates a new distinct vtable for class bar. vtable elements point: to same addresses as parent when method is not overridden To overridden methods or to new methods otherwise vtable of inherited class is an extension of the vtable of the parent table: shared methods appear in the same order new methods in the child class appear at the end of the vtable.

35 Virtual Table Single Inheritance Base Class Example
class Point { public: virtual ~Point(); virtual Point& mult( float ) = 0; // ... other functions... float x() const { return _x; } virtual float y() const { return 0; } virtual float z() const { return 0; } // ... protected: Point( float x = 0.0 ); float _x; }; Slot 0 contains address of the Point class virtual table The virtual destructor is assigned slot 1 Pure virtual function mult() is assigned slot 2 There is no mult() definition, so the address of the library function pure_virtual_called() is placed within the slot If that instance should get invoked, generally, it would terminate the program! y() is assigned to slot 3 z() is assigned to slot 4 What slot is x() assigned? None because it is not declared to be virtual

36 Virtual Table Single Inheritance Derived Class Example
class Point2d : public Point { public: Point2d( float x = 0.0, float y = 0.0 ) : Point( x ), _y( y ) {} ~Point2d(); // overridden base class virtual functions Point2d& mult( float ); float y() const { return _y; } // ... other functions... protected: float _y; }; Slot 0 points at the Point2d class virtual table The Point2d destructor is assigned to slot 1 replacing Point’s destructor address The Point2d mult() instance is assigned to slot 2 replacing the pure virtual instance The Point2d y() instance is assigned to slot 3 replacing Point’s y() address Slot 4 does not change It retains Point's inherited instance of z()

37 Virtual Table Single Inheritance 2nd Derived Class Example
class Point3d: public Point2d { public: Point3d( float x = 0.0, float y = 0.0, float z = 0.0 ) : Point2d( x, y ), _z( z ) {} ~Point3d(); // overridden base class virtual functions Point3d& mult( float ); float z() const { return _z; } // ... other functions... protected: float _z; }; This is similar to first derived class mechanics Slot 0 points at the Point3d class virtual table Point3d's destructor assigned to slot 1 replacing Point2d’s destructor address Point3d's instance of mult() assigned to slot 2 replacing Point’s mult() address It places Point2d's inherited instance of y() in slot 3 Point3d's instance of z() in slot 4 So how is ptr->z()invoked? We don’t know the type of ptr We do know that z() is at slot 4 We do know that the vtable is found at 0 Thus: (ptr[0][4])(); Regardless of ptr’s type!

38 Vtable and Multiple Inheritance
multiple inheritance: a class can extend more than one base class

39 Multiple Inheritance Class student inherits both from class person and gp_list_node  vtable layout becomes more complex in such a situation.

40 object of type student has 3 types of fields
Multiple Inheritance object of type student has 3 types of fields inherited from person inherited from gp_list_node declared in class student 3 types of methods (inherited from person, gp_list_node, defined in class student)

41 Vtable vptr points to student specific vtable
vtable first contains  person methods, next methods that appear in class student The object then contains the person fields. (look at a student value as a person value - just ignore the bottom part of the block) we cannot store the gp_list_node’s fields at the beginning of the block.

42 So where do we store gp_list_node fields?
Right after the person fields! But before them we store the vptr of gp_list_node.  Finally we store the student specific data members at the end of the data block

43 Look-at

44 Passing ‘this’ to member functions
how to pass valid this pointer to a method of gp_list_node that is not overridden? code cannot know that what it receives as the this pointer is not a real gp_list_node. accesses the fields of the value it has, assuming it is a gp_list_node value.

45 pointer fixup – look down
compiler knows what type of method is invoked - either inherited from person,  gp_list_node or specific to student. If inherited from gp_list_node: pass this as "corrected address" (this+d, where d=sizeof(person)). look down from this address, block memory looks as a valid gp_list_node object

46 Casting and Addresses Student c;
when we cast an object to a different class, we may end up with a different address in memory Student c; We get that: (void*)&c != (void*)static_cast<gp_list_node*>(&c)

47 Multiple Inheritance: Code Example
class Base1 { public: Base1(); virtual ~Base1(); virtual void speakClearly(); virtual Base1 *clone() const; protected: float data_Base1; }; class Base2 { Base2(); virtual ~Base2(); virtual void mumble(); virtual Base2 *clone() const; float data_Base2; class Derived : public Base1, public Base2 { Derived(); virtual ~Derived(); virtual Derived *clone() const; float data_Derived;

48 Multiple Inheritance: Function Call Ambiguity
What happens in these cases: Derived, Base1 and Base2 classes have clone method implementation Base1 and Base2 classes have clone method implementation but Derived does not have clone method implementation Which clone implementation is executed for these three cases for each two code implementations mentioned above? #include <iostream> int main(){ Derived *d = new Derived; d->clone(); delete d; } #include <iostream> int main(){ Base1 *d = new Derived; d->clone(); delete d; } #include <iostream> int main(){ Base2 *d = new Derived; d->clone(); delete d; } Which case out of the six is problematic?

49 Code Example: Base1 Class Implementation
#include <iostream> class Base1 { public: Base1(){ std::cout <<"Base1 constructor" << std::endl; } Base1(float data_Base1): data_Base1(data_Base1){} virtual ~Base1(){ std::cout <<"Base1 destructor" << std::endl;} virtual void speakClearly(){ std::cout << "speak!" << std::endl;}; virtual Base1 *clone() const{ std::cout << "Base1 clone" << std::endl; return new Base1(data_Base1); protected: float data_Base1; };

50 Code Example: Base2 Class Implementation
#include <iostream> class Base2 { public: Base2(){ std::cout <<"Base2 constructor" << std::endl; } Base2(float data_Base2): data_Base2(data_Base2){} virtual ~Base2(){ std::cout <<"Base2 destructor" << std::endl;} virtual void mumble(){ std::cout << "mumble" << std::endl;} virtual Base2 *clone() const{ std::cout << "Base2 clone" << std::endl; return (new Base2(data_Base2)); protected: float data_Base2; };

51 Code Example: Derived Class Implementation
#include <iostream> class Derived : public Base1, public Base2 { public: Derived(){ std::cout <<"Derived constructor" << std::endl; } Derived(float data_Derived): data_Derived(data_Derived){} virtual ~Derived(){ std::cout <<"Derived destructor" << std::endl;} virtual Derived *clone() const{ std::cout << "Derived clone" << std::endl; return new Derived(data_Derived); protected: float data_Derived; };

52 Multiple Inheritance: Function Call Ambiguity
What happens in this case: Derived, Base1 and Base2 classes have clone method implementation Base1 and Base2 classes have clone method implementation but Derived does not have clone method implementation What is the output for these three cases for each two code implementations mentioned above: #include <iostream> int main(){ Derived *d = new Derived; d->clone(); delete d; } #include <iostream> int main(){ Base1 *d = new Derived; d->clone(); delete d; } #include <iostream> int main(){ Base2 *d = new Derived; d->clone(); delete d; }

53 Casting Implicit conversion: Explicit conversion
short a=2000; int b; b=a; automatically performed when a value is copied to a compatible type Explicit conversion short a=2000; int b; b = (int) a; explicit type-casting allows to convert any pointer into any other pointer type static_cast<> : casting at compile time.

54 Dynamic cast Dynamic_cast:
Can be used only with pointers and references. Used when casting from base to derived. Ensures at run-time that the result of the type conversion is a valid object. always successful when we cast a class to one of its base classes.

55

56 Dynamic cast Dynamic_cast:
Returns nullptr if the casting fails for pointers, or throws exception when casting fails for references. dynamic_cast<> requires the Run-Time Type Information (RTTI) to track the dynamic types. This imposes a runtime overhead.

57 dynamic_cast Example #include <iostream>
struct A { virtual void f() { cout << "Class A" << endl; } }; struct B : A { virtual void f() { cout << "Class B" << endl; } }; struct C : A { virtual void f() { cout << "Class C" << endl; } }; void f(A* arg) { B* bp = dynamic_cast<B*>(arg); C* cp = dynamic_cast<C*>(arg); if (bp) bp->f(); else if (cp) cp->f(); else arg->f(); }; int main() { A aobj; C cobj; A* ap = &cobj; A* ap2 = &aobj; f(ap); // line 17 f(ap2); // line 18 } Output? Line 17: Class C Line 18: Class A

58 g++ fdump -class-hierarchy option
look at the exact structure of the vtables the compiler generates for us g++ has an option that allows us to get this information in a readable manner: g++ c.cpp -fdump-class-hierarchy -o c generates a text file called c.cpp.t01.class which gives the details of the memory layout and vtable of the classes defined in the file.

59

60

61 Virtual methods: performance issues
invoking a virtual method is more expensive at runtime than invoking a regular function. 2 operations: get the location of the function's code from vtable, and invoke the function. Object values are extended by one word for each vtable to which they refer. Virtual methods cannot be compiled "inline" Inline: avoids calling a function (pushing arguments on the stack, popping them out at end). The compiler copies the code of the function at invocation point. Advantage: no parameter passing and returning protocol. Disadvantage: Executable is long…

62 3 costs combined can have a strong effect on performance of program.
in C++, methods are not virtual by default. If a class does not have virtual method, then does not include a vtable. in Java, methods are always virtual.

63 Example: pure virtual functions

64 pure virtual functions
Shape cannot be instantiated. Shape is called an abstract class (like an “interface”). If we did not implement draw() in Circle, then Circle would also be abstract. Optional feature: Pure virtual functions may have an implementation, which can only be called from derived classes.

65 Implementing Interfaces in C++
Java avoids the complexity of multiple inheritance restricts programmers to single inheritance and the mechanism of interfaces. Interfaces = restricted method of multiple inheritance. interfaces in C++ = pure virtual class

66 pure virtual abstract class
does not define any data members. All of its methods are virtual. All of its methods are abstract (marked as virtual m() = 0;)

67 "virtual inheritance“ = "implement an interface"
avoid the problem of ambiguous hierarchy composition – diamond problem inheritance=arranging classes in memory

68 Multiple Inheritance: The Diamond Problem
class Top{}; class Left : public Top{}; class Right : public Top{}; class Bottom : public Left, public Right{ }; int main(){ Left* lptr = new Bottom(); Right* rptr = new Bottom(); Top* tptr = new Bottom(); Bottom* bptr = new Bottom(); } What’s the error at line 11 during compilation? diamond_problem.cpp:11:28: error: ‘Top’ is an ambiguous base of ‘Bottom’ Why? Bottom will contains two copies of the sub-object Top! Which one to use when we access Top data members? Ambiguous!

69 The Diamond Problem Solution: Virtual Inheritance
class Top{}; class Left : virtual public Top{}; class Right : virtual public Top{}; class Bottom : public Left, public Right{ }; int main(){ Left* lptr = new Bottom(); Right* rptr = new Bottom(); Top* tptr = new Bottom(); Bottom* bptr = new Bottom(); } Virtual inheritance ensures that one copy of Top is added to Bottom Done by prefixing the inheritance syntax with the virtual keyword

70 The diamond problem Consider the following class hierarchy

71 The diamond problem A call to bat.eat() is ambiguous because there are two Animal (indirect) base classes

72 The diamond problem Bat b; Animal &a = b;
Furthermore: Bat b; Animal &a = b; error: which Animal subobject should a Bat cast into, a Mammal::Animal or a WingedAnimal::Animal? *One can use static casting, for example: static_cast<Mammal&>(bat).eat()

73 Better solution: virtual inheritance
The Animal portion of Bat::WingedAnimal is now the same Animal instance as the one used by Bat::Mammal. Bat::eat() is not ambiguous.

74 The Visitor Pattern when you want to decide at runtime which piece of logic to execute=polymorphism introduce polymorphism

75 Visitor pattern - re-organization of code
Example: We have printer objects and document objects a printer object can print document objects. code to print each document is different in printer. Problem: Using virtual tables/functions we determine the type of printer, but not document.

76 Goal each printer type has a separate method for handling each document type. We handle the printer type by vtable. We do not want each document to know about each printer – bad coupling. We don’t want to ask “instanceOf()”. Goal: also determine the document type using vtable mechanism.

77 How? “double dispatch” use the virtual table mechanism to invoke the print() method on the right printer, with a base document* as parameter. A base document will have a virtual printMe() method. Each document’s printMe() knows its type and calls back the printer’s print().

78 How? “double dispatch” a printer object receives the print message with a document object: first dispatch happens when we select the appropriate printer object using print() second dispatch is achieved by sending the printMe() message to the document.

79

80

81 Output dispatching function <print> called PDFDoc accepting a print call printing a PDF doc DocDoc accepting a print call printing a Doc doc using badPrint


Download ppt "Inheritance, Polymorphism and the Object Memory Model"

Similar presentations


Ads by Google