Presentation is loading. Please wait.

Presentation is loading. Please wait.

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

Similar presentations


Presentation on theme: ". 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."— Presentation transcript:

1 . Virtual Classes & Polymorphism

2 Example (revisited) u We want to implement a graphics system u We plan to have lists of shape. Each shape should be able to draw it self, compute its size, etc.

3 Solution #1 class Shape { public: enum Type { Square, Circle, Triangle }; Shape( Type t, Point Center, double width, double height); void Draw(); double Area(); … private: Type m_type; Point m_center; … };

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

5 Solution #2 – different classes class Square { public: … void Draw(); double Area(); … }; class Circle { public: … void Draw(); double Area(); … };

6 Solution #2 – Discussion u Each shape has its own implementation u We can easily add new shapes However, u Shapes are different types u One cannot view them as part of the same class

7 Solution #3 - hierarchy class Shape { public: … void Draw(); double Area(); … }; class Square: public Shape { public: … void Draw(); double Area(); … }; class Circle: public Shape { public: … void Draw(); double Area(); … }; …

8 Solution #3 Now we can write code such as: Shape* MyShapes[2]; MyShapes[0] = new Circle(); MyShapes[1] = new Square(); … What will happen when we call MyShapes[0]->Draw(); Lets test … see Shapes.cpp.

9 What is going on? 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

10 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() u Two implementations, the more specific is used

11 Inheritance & Overloading  Derived inherits the method foo () from Base  The inherited class to use methods of the base class  Derived overloads the implementation of bar ()  This mechanism allows an inherited class to specialize the implementation

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

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

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

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

16 Solution #4: dynamic resolution u Returning to the shapes example, using virtual methods gives the desired result u See Shapes-virtual.cpp

17 Virtual Methods  Class Base defines a virtual method foo()  The resolution of foo() is dynamic in all subclasses of Base  If the subclass Derived overloads foo(), then Derived::foo() is called  If not, Base::foo() is called [See nested.cpp]

18 Underneath the Hood: Inheritance class Base { … private: double m_x; int m_a; }; class Derived : public Base { … private: double m_z; }; class Base a; class Derived b; m_x m_a m_z m_x m_a a: b: Base

19 Pointing to an Inherited Class class Derived b; class Base* p = &b; u 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. m_x m_a m_z b: p:

20 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 u Essentially the solution we used in our C implementation Cost: u Each virtual method requires a pointer Large number of virtual methods  waste of memory

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

22 Example class A { public: virtual void f1(); virtual void f2(); int m_a; }; class B: public A { public: virtual void f1(); virtual void f3(); void f4(); int m_b; }; … A a1, a2; B b; class A { public: virtual void f1(); virtual void f2(); int m_a; }; class B: public A { public: virtual void f1(); virtual void f3(); void f4(); int m_b; }; … A a1, a2; B b; void A::f1 { //... }; void A::f1 { //... }; void A::f2 { //... }; void A::f2 { //... }; void B::f1 { //... }; void B::f1 { //... }; VTBLs A B m_a a1: m_a m_b b: m_a a1: void B::f3 { //... }; void B::f3 { //... }; f1 f2 f3 f1 f2

23 Virtual - Recap u Virtual controls whether to use static or dynamic resolution u Once a method is virtual, it must remain so throughout the hierarchy u Calling a virtual method is more expensive than standard calls  Two pointers are “chased” to get to the address of the function

24 Destructors & Inheritance class Base { public: ~Base(); }; class Derived : public Base { public: ~Derived(); }; Base *p = new Derived; … delete p; Which destructor is called? [see destruct.cpp]

25 Virtual Destructor u Destructor is like any other method u The example uses static resolution, and hence the wrong destructor is called u To fix that, we need to declare virtual destructor at the base class! u See destruct-virt.cpp u Once you declare virtual destructor, derived class must declare a destructor

26 Polymorphism & Inheritance u C++ allows to use class hierarchy to implement polymorphic code Points of care: u Choice of virtual methods  Run time considerations u Use of virtual destructor for base class

27 Shapes & Sizes Revisiting our example, we write class Shape { public: … virtual ~Shape(); // Virtual destructor virtual void Draw(); // Virtual method virtual double Area(); // Also … }; How do we implement Shape::Draw() ?

28 Inheritance & Interfaces  In this example, we never want to deal with objects of type Shape  Shape serves the role of an interace  All shapes need to be specific shapes that are instances of derived classes of Shape u How do we enforce this? Simple mechanism: void Shape::Draw() { assert(false); // we should never call this method }

29 Pure Virtual Classes  We can specify that Shape::Draw() does not exist class Shape { public: … virtual ~Shape(); // Virtual destructor virtual void Draw() = 0; // pure virtual virtual double Area() = 0; // Also … };

30 Pure Virtual u We cannot create objects of a Pure Virtual class Shape* p; // legal Shape s; // illegal Circle c; // legal p = &c; // legal p = new Shape; // illegal

31 Virtual Methods - Tips u If you have virtual methods in a class, always declare its destructor virtual u Never call virtual methods during construction and destruction u Use virtual methods Duplicate() and Create() for implementing “virtual constructors” u Use pure virtual classes to define interfaces u Use inheritance with care: Be sure that this is the appropriate solution

32 Post Quiz issues…. Things to brush up on: u Preprocessor vs. Compiler u #define and how it works u Compiler vs. Linkage

33 Example void main() { (1) int *y = (int*)malloc( 3*sizeof(int) ); (2) int x[] = {1,2,3}; (3) int ** pp = NULL; (4) int *z = y; } A space on the memory heap was allocated in the following lines: a. 1 b. 2 c. 3 d. 4

34 Another int *ip = (int*)malloc(100*sizeof(int)); (*ip) += 30; free(ip); 1This code will generate a compilation error. 2This code can generate unpredictable run-time behavior. 3No error would occur if we only check if ip equals NULL before calling the "free" function 4The code contains no error.

35 Something else…. char s[] = {'p','l','a','b'}; What will be returned by calling strcmp(s,"plab")? 1. a negative number 2. 0 3. a positive number 4. We cannot know for sure

36 Almost Last Example struct MyStruct { int x[3]; }; void f(MyStruct s) { s.x[0] = 15; s.x[1] = 16; s.x[2] = 17; } int main() { MyStruct s; f(s); printf("%d %d %d\n", s.x[0], s.x[1], s.x[2]); }

37 Similar but not quite… struct MyStruct { int x[3]; }; void f(MyStruct* s) { s->x[0] = 15; s->x[1] = 16; s->x[2] = 17; } int main() { MyStruct s; f(&s); printf("%d %d %d\n", s.x[0], s.x[1], s.x[2]); }

38 Final Example typedef void (*myfunc)(int*); void foo(int * x) { x[0] += 2; } void bar( int * x ) { x[1] += 2; } main() { myfunc funcs[2] = { foo, bar }; int arr[] = {3,4,5}; (funcs[0])(arr); (funcs[1])(arr+1); printf("%d %d %d\n",arr[0],arr[1],arr[2]) ; }


Download ppt ". 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."

Similar presentations


Ads by Google