Presentation is loading. Please wait.

Presentation is loading. Please wait.

Advanced Program Design with C++

Similar presentations


Presentation on theme: "Advanced Program Design with C++"— Presentation transcript:

1 Advanced Program Design with C++
COMP Advanced Program Design with C++ Advanced Program Design with C++ Polymorphism Virtual functions Abstract classes Slicing problem Value and reference semantics Function pointers Virtual functions tables Diamond problem Virtual inheritance Joey Paquet,

2 Polymorphism Virtual Polymorphism
COMP Advanced Program Design with C++ Polymorphism Polymorphism Associating many meanings to one function. Virtual functions provide this capability. Fundamental principle of object-oriented programming. Virtual Existing in "essence" though not in fact. Virtual functions. Virtual (abstract) classes. Joey Paquet,

3 Best explained by example: Classes for several kinds of figures
COMP Advanced Program Design with C++ Polymorphism Best explained by example: Classes for several kinds of figures Rectangles, circles, etc. Each figure is an object of a different class Each have different data elements and formulas to compute them Rectangle data: name, area, perimeter, height, width Circle data: name, area, perimeter, radius All derive from one parent-class: CShape Requires a method getArea() Different formula to compute different figures’ area, requires different implementation for each kind of figure Joey Paquet,

4 Each class needs a different getArea() function
COMP Advanced Program Design with C++ Polymorphism Each class needs a different getArea() function Can define a member getArea() in each class, so: Nothing new here yet… CRectangle r; CCircle c; r.getArea(); //Calls Rectangle’s getArea c.getArea(); //Calls Circle’s getArea Joey Paquet,

5 COMP 345 - Advanced Program Design with C++
Polymorphism Problem! Parent class CShape may contain functions that applies to all kinds of figures. Consider: print() : prints out all characteristics common to all kinds of figures Fine for getName(), as it has common behavior. However, getArea()has subclass-specific behavior. Complications! Which getArea() function to call? From which class? Here, a function that exposes common behavior uses another function that exposes subclass-specific behavior. May hardcode a function that calls the right getArea() depending on which subclass the figure in question is. Need a mechanism to call subclass-specific behavior automatically Joey Paquet,

6 COMP 345 - Advanced Program Design with C++
Polymorphism Problem! Consider a new kind of figure later coming along: CTriangle class derived from CShape class Function print() inherited from CShape Will it work for triangles? It uses getArea(), which is different for each subclass of figure It will use CShape::getArea(), which was not made to work for triangles We want the inherited function print() to use the function Triangle::getArea() and not the function CShape::getArea() But class CTriangle wasn’t even written when CShape::print() was written. It does not “know” about triangles. If print() function is hard-coded to call the right getArea() depending on the type of CShape, we need to change the implementation of print()every time we add a new subclass of Cshape. Again, we need a mechanism to automatically call subtype-specific behavior. Joey Paquet,

7 Polymorphism void displayCShapeDynamicCastDemo(CShape& shape) {
COMP Advanced Program Design with C++ Polymorphism void displayCShapeDynamicCastDemo(CShape& shape) { CShape* p = &shape; CCircle* p1 = dynamic_cast<CCircle*>(p); CRectangle* p2 = dynamic_cast<CRectangle*>(p); CTriangle* p3 = dynamic_cast<CTriangle*>(p); if (p1 != NULL){ cout << "Circle's area is " << p1->getArea() << endl; cout << "Circle's radius is " << p1->getRadius() << endl; } if (p2 != NULL){ cout << "Rectangle's area is " << p2->getArea() << endl; cout << "Rectangle's width is " << p2->getWidth() << endl; cout << "Rectangle's height is " << p2->getLength() << endl; if (p3 != NULL){ cout << "Triangle's area is " << p3->getArea() << endl; cout << "Triangle's width is " << p3->getHeight() << endl; cout << "Triangle's height is " << p3->getBase() << endl; Joey Paquet,

8 Virtual methods COMP 345 - Advanced Program Design with C++
Joey Paquet,

9 Polymorphism: virtual methods
COMP Advanced Program Design with C++ Polymorphism: virtual methods Virtual methods allow to declare such a subtype-specific behavior. If an object of type CShape is declared and instantiated with an object of type e.g. CRectangle, calling any of its virtual methods will result in using CRectangle’s behavior, even though its type is declared as CShape. This requires a dynamic run-time binding mechanism. In C++ implementation, it is called “dynamic binding” or “late binding”. class CShape{ protected: double area; string name; public: CShape() : name("anonymous"){ } CShape(string newName) : name(newName){ string getName(){ return name; virtual double getArea()= 0; virtual void print(){ cout << "Shape:" << name << endl; cout << "Area: " << getArea() << endl; }; bool operator < (CShape &s1, CShape &s2){ return (s1.getArea() < s2.getArea()); Joey Paquet,

10 Polymorphism: virtual methods
COMP Advanced Program Design with C++ Polymorphism: virtual methods Polymorphic behavior comes when you declare an object of a base class type and then instantiate it with an instance of a derived type. In this case, the type of the object can only be determined dynamically at run-time. If a method is declared as virtual, its subtype behavior is branched upon. If a method is not declared as virtual, the base type behavior is branched upon. void main() { CRectangle *shapeRect = new CRectangle("shapeRect", 16.8, 8.8); CCircle *shapeCircle = new CCircle("shapeCircle", 13.8); CTriangle *shapeTriangle = new CTriangle("shapeTriangle", 2.5, 3.8); CShape *shapes[] = { shapeRect, shapeCircle, shapeTriangle }; for (int i = 0; i < sizeof(shapes) / sizeof(shapes[0]); i++){ shapes[i]->getArea(); shapes[i]->print(); if (i>0) cout << "Shape[" << i << "] area (" << shapes[i]->getArea() << ") less than Shape[" << i - 1 << "] area (" << shapes[i-1]->getArea() << ") : " << (*shapes[i] < *shapes[i-1]) << endl; } delete shapeRect; delete shapeCircle; delete shapeTriangle; Joey Paquet,

11 Polymorphism: virtual methods
COMP Advanced Program Design with C++ Polymorphism: virtual methods class CCircle : public CShape{ double radius; public: CCircle() : CShape(), radius(1){ }; CCircle(string n, double r) : CShape(n), radius(r) { void setRadius(double r) { radius = r; } double getRadius() const { return radius; } double getArea(){ area = * radius * radius; return area; } void print(){ CShape::print(); cout << "Circle:" << endl; cout << "Radius: " << radius << endl; class CRectangle : public CShape{ double length; double width; public: CRectangle() : CShape(), length(1), width(1){ } CRectangle(string n, double l, double w) : CShape(n), length(l), width(w){ void setLength(double l) { length = l; } void setWidth(double w) { width = w; } double getLength() const { return length; } double getWidth() const { return width; } double getArea(){ area = length * width; return area; void print(){ CShape::print(); cout << "Rectangle:" << endl; cout << "Height: " << length << " Width: " << width << endl; }; Joey Paquet,

12 Polymorphism: virtual destructors
COMP Advanced Program Design with C++ Polymorphism: virtual destructors If you implement a class to be used polymorphically, you probably need to declare its destructor as virtual. If an object of a derived class is assigned to a pointer to a base class, and then later deleted using the base class pointer, the base class destructor will be used to delete the object. The derived class’ destructor will thus never be called, possibly leading to a resource leak. Rule of thumb: always declare the destructor as virtual if a class is to be used polymorphically. class Base { public: virtual ~Base(){ cout << "Base::~Base()" << endl; } }; class Derived : public Base { ~Derived(){ cout << "Derived::~Derived()" << endl; int main(){ Base *b = new Derived(); delete b; int i; cin >> i; Joey Paquet,

13 Pure virtual functions Abstract classes
COMP Advanced Program Design with C++ Pure virtual functions Abstract classes Joey Paquet,

14 Polymorphism: pure virtual methods and abstract classes
COMP Advanced Program Design with C++ Polymorphism: pure virtual methods and abstract classes In C++, some virtual methods can be declared as “pure virtual”. Any class with at least one pure virtual method is an abstract class. No instance of an abstract class can be created. However, an abstract class still represents a data type. To be used, abstract classes need to be derived by subclasses that provide an implementation to all their pure virtual methods. A class that derives an abstract class and does not provide an implementation for all the pure virtual functions it inherits is itself still an abstract class. Joey Paquet,

15 Polymorphism: pure virtual methods and abstract classes
COMP Advanced Program Design with C++ Polymorphism: pure virtual methods and abstract classes There are two main uses for pure virtual functions/abstract classes: Classes that are to be used polymorphically with different subclass behaviors. Interfaces, i.e. classes that contain only pure virtual functions. This is equivalent to Java’s interfaces. Contrary to Java interfaces, C++ abstract classes can provide a definition for some of their methods, and can have non-constant data members. class CShape{ protected: double area; string name; public: CShape() : name("anonymous"){ } CShape(string newName) : name(newName){ string getName(){ return name; virtual double getArea()= 0; virtual void print(){ cout << "Shape:" << name << endl; cout << "Area: " << getArea() << endl; }; class Iserialisable { public: virtual void load(istream& in) = 0; virtual void save(ostream& out) = 0; virtual ~serialisable(); } Joey Paquet,

16 Object slicing: value semantics and reference semantics
COMP Advanced Program Design with C++ Object slicing: value semantics and reference semantics Joey Paquet,

17 Object slicing COMP 345 - Advanced Program Design with C++
class Pet { public: string name; Pet() {} Pet(const Pet& aPet) : name(aPet.name) { cout << "In Pet's copy constructor" << endl; } Pet(string newName) : name(newName){} virtual ~Pet(){} virtual void print() const { cout << "In Pet::print()" << endl; cout << "name: " << name << endl << endl; Pet & operator= (const Pet & otherPet){ cout << "In Pet's assignment operator" << endl; name = otherPet.name; return *this; }; class Dog : public Pet { public: string breed; Dog() : Pet() {} Dog(const Dog& aDog) : Pet(aDog), breed(aDog.breed) { cout << "In Dog's copy constructor" << endl; } Dog(string newName, string newBreed) : Pet(newName), breed(newBreed){} virtual void print() const { cout << "In Dog::print(): " << endl; cout << "name: " << name << endl; cout << "breed: " << breed << endl << endl; Dog & operator= (const Dog & otherDog){ cout << "In Dog's assignment operator" << endl; Pet::operator=(otherDog); breed = otherDog.breed; return *this; }; Joey Paquet,

18 Anything that is a Dog is also a Pet:
COMP Advanced Program Design with C++ Object slicing Anything that is a Dog is also a Pet: Can assign values to parent-types, but not inversely A Pet is not necessarily a Dog However, the values assigned to vpet1 and vpet2 lose their breed field Called the slicing problem This is due to the fact that value semantics was used in the operations to assign/copy the object in the operation. The resulting copy is a Pet that has lost its identity as a Dog. When using pointers or references, reference semantics is used, which does not result in slicing and enables polymorphism. Dog vdog1("Tiny", "GreatDane"); Pet vpet1; cout << "Use of Pet's copy constructor:"; Pet vpet2(vdog1); cout << "Use of Pet's assignment operator:"; vpet1 = vdog1; Joey Paquet,

19 Value semantics: object slicing
COMP Advanced Program Design with C++ Value semantics: object slicing cout << "\nUsing value semantics results in the slicing problem\n"; cout << "and is not very interesting as it does not enable polymorphism.\n\n"; Dog vdog1("Tiny", "GreatDane"); Pet vpet1; cout << "Use of Pet's copy constructor:"; Pet vpet2(vdog1); cout << "Use of Pet's assignment operator:"; vpet1 = vdog1; cout << "The Dog has been sliced into a Pet\n"; cout << "due to the value semantics used in\n"; cout << "the assignment operation.\n"; cout << "vdog1.print() uses Dog::print():\n"; vdog1.print(); cout << "vpet1.print() uses Pet::print():\n"; vpet1.print(); cout << "vpet2.print() uses Pet::print():\n"; vpet2.print(); cout << endl << "Passing a Dog received as a Pet "; cout << "using value semantics:" << endl; valueSemantics(vdog1); void valueSemantics(const Pet aPet) { // As this function receives its parameter as value, // it is copied into a Pet value, and then always // calls Pet::print(). aPet.print(); } Joey Paquet,

20 Reference semantics: polymorphism
COMP Advanced Program Design with C++ Reference semantics: polymorphism cout << "\nUsing reference semantics does not result in the slicing problem\n"; cout << "and it allows to use polymorphism through late binding.\n\n"; Dog *pdog1 = new Dog("Tiny", "GreatDane"); Pet *ppet1 = new Pet; cout << "Use of Dog and Pet's copy constructors:\n"; Pet *ppet2 = new Dog(*pdog1); cout << "Use of Pet's copy constructor:\n"; Pet *ppet3 = new Pet(*pdog1); cout << "ppet1 = pdog1 does not use "; cout << "Pet`s assignment operator\n"; ppet1 = pdog1; cout << "pdog1.print() uses Dog::print():\n"; pdog1->print(); cout << "ppet1.print() uses Dog::print():\n"; ppet1->print(); cout << "ppet2.print() uses Dog::print():\n"; ppet2->print(); cout << "ppet3.print() uses Pet::print():\n"; ppet3->print(); cout << "Passing a Dog received as a Pet using"; cout << " reference semantics" << endl; referenceSemantics(vdog1); void referenceSemantics(const Pet& aPet) { // As this function receives its parameter // as a reference, its original type is kept // in the value the reference parameter // refers to and thus can polymorphically // call print(). aPet.print(); } Joey Paquet,

21 Function pointers COMP 345 - Advanced Program Design with C++
Joey Paquet,

22 COMP 345 - Advanced Program Design with C++
Function pointers Function pointers are pointers, i.e. variables, which point to the starting address of a function’s code. Enables the call to different functions, depending on the function pointer value, which can be pointing to different functions’ code in time. In C++, a function pointer can only point to different functions with the same signature (single dispatch mechanism). Single dispatch assumes that the same number/types of values are passed/returned during a function call. multiple dispatch – dispatch to any function (independent of signature), with a an elaborated mechanism for mapping parameters/return value. Joey Paquet,

23 Function pointers COMP 345 - Advanced Program Design with C++
float Plus(float a, float b) {return a + b;} float Minus(float a, float b) {return a - b;} float Multiply(float a, float b) {return a * b;} float Divide(float a, float b) {return a / b;} float Switch(float a, float b, char opCode) { float result; // execute operation switch (opCode) { case '+': result = Plus(a, b); break; case '-': result = Minus(a, b); break; case '*': result = Multiply(a, b); break; case '/': result = Divide(a, b); break; } return result; float Switch_With_Function_Pointer(float a, float b, float(*ptFunc)(float, float)) { // call using function pointer float result = ptFunc(a, b); int main(){ //function pointer declaration float(*op)(float, float); //assign the address of a particular function op = &Plus; cout << "Switch: 1+2="; cout << Switch(1, 2, '+') << endl; cout << "Switch_With_Function_Pointer: 1+2="; cout << Switch_With_Function_Pointer(1, 2, op) << endl; int i; cin >> i; } Joey Paquet,

24 Virtual function tables
COMP Advanced Program Design with C++ Virtual function tables Joey Paquet,

25 Polymorphism: virtual function tables
COMP Advanced Program Design with C++ Polymorphism: virtual function tables C++ implements virtual functions through a form of late binding using virtual function tables, also known as virtual method tables, or vtables. Every class that includes or inherits at least one virtual function is assigned a virtual function table. A vtable is a static array of function pointers. There is one function pointer for each virtual function declared or inherited in the class. Each pointer points to the function belonging to the most derived class that implements this function. For pure virtual functions, one can assume that the vtable contains a NULL pointer. Some implementations implement a pointer to a special function that may throw an exception upon call. Every object created that belongs to a class hierarchy having virtual functions keeps its own vtable that is used to resolve its function calls at run time. When this object is assigned to a variable of one of its supertypes, its own vtable is used to resolve the function calls. Joey Paquet,

26 Polymorphism: virtual function tables
COMP Advanced Program Design with C++ Polymorphism: virtual function tables class Base { public: virtual void function1(); virtual void function2(); virtual void function3() = 0; void function4(); }; class Derived1 : public Base { void function1(); virtual void function5(); void function6(); class Derived2 : public Base { void function2(); void function3(); void function5(); class Derived3 : public Derived1 { Joey Paquet,

27 Polymorphism: virtual function tables
COMP Advanced Program Design with C++ Polymorphism: virtual function tables int main(){ Base *baseArray[] = { new Derived2, new Derived3 }; cout << "baseArray[0]->function1(): " << endl; baseArray[0]->function1(); cout << "baseArray[0]->function2(): " << endl; baseArray[0]->function2(); cout << "baseArray[0]->function3(): " << endl; baseArray[0]->function3(); cout << "baseArray[0]->function4(): " << endl; baseArray[0]->function4(); //baseArray[0]->function5(); //no function5 in Base cout << "dynamic_cast<Derived2*>(baseArray[0])"; cout << "->function5(): " << endl; dynamic_cast<Derived2*>(baseArray[0])->function5(); cout << "baseArray[1]->function1(): " << endl; baseArray[1]->function1(); cout << "baseArray[1]->function2(): " << endl; baseArray[1]->function2(); cout << "baseArray[1]->function3(): " << endl; baseArray[1]->function3(); cout << "baseArray[1]->function4(): " << endl; baseArray[1]->function4(); //baseArray[1]->function5(); cout << "dynamic_cast<Derived3*>(baseArray[1])->function5(): " << endl; dynamic_cast<Derived3*>(baseArray[1])->function5(); //baseArray[1]->function6(); cout << "dynamic_cast<Derived3*>(baseArray[1])->function6(): " << endl; dynamic_cast<Derived3*>(baseArray[1])->function6(); } Joey Paquet,

28 Virtual inheritance and the diamond problem
COMP Advanced Program Design with C++ Virtual inheritance and the diamond problem Joey Paquet,

29 Multiple use of a class in a hierarchy
COMP Advanced Program Design with C++ Multiple use of a class in a hierarchy Multiple inheritance allows you to use more than once the same abstract class in a class hierarchy, similarly to Java’s Interfaces. Here, IComm is an abstract class having only pure virtual methods and no data members and will not be instantiated. You may also want to use the same non-abstract class more than once in a hierarchy. Here, Recorder is a class that is used to record all the communications emitted or received. There is one store for emission, and another store for reception. Joey Paquet,

30 COMP 345 - Advanced Program Design with C++
Diamond problem But what if what you really want is a single Store that records all received/emitted messages? In C++, if you set both Transmitter and Receiver to inherit from Recorder, both of them will by default have their own copy of a different Recorder object in memory. This is going to also lead to ambiguities when, e.g. a WalkieTalkie object refers to a member of Recorder. As there are in fact two separate stores. This is called the diamond problem. In order to solve both of these problems, C++ offers what is called virtual inheritance. Joey Paquet,

31 COMP 345 - Advanced Program Design with C++
Virtual inheritance #include <iostream> using namespace std; class A1 { int i; public: A1() { cout << "in A1::A1()\n"; }; A1(int k) : i(k){ cout << "in A1::A1(int)\n"; } }; class A : virtual public A1 { A() {cout<<"in A::A()\n";}; A(int k) : A1(k){ cout << "in A::A(int)\n";} class B : public A { B(){cout<<"in B::B()\n";}; B(int i) : A1(), A(i) { cout << "in B::B(int)\n"; } class C : public A { C(){ cout << "in C::C()\n"; }; C(int i) : A(i) { cout << "in C::C(int)\n"; } class D : public B, C{ D() { cout << "in D::D()\n"; } D(int i) : A1(i), B(i), C(i) { cout << "in D::D(int)\n"; } int main() { D d1(2); D d2; int i; cin >> i; return 0; } To signify virtual inheritance, the classes that are to share a same instance of a class in a hierarchy have to use the virtual keyword in their inheritance specification. This changes how the objects of the most derived class are created, and how their constructors call each other implicitly or explicitly. The class responsible to call the constructor on the virtually inherited class is the first derived class that includes all instances of a virtually inherited class. In the example here, it is the class D. Any other call to a constructor of a virtually inherited class up the hierarchy is ignored. Joey Paquet,

32 Virtual inheritance COMP 345 - Advanced Program Design with C++
#include <iostream> using namespace std; class V { int i; public: V() { cout << "in V::V()\n"; }; V(int i) : i(i){ cout << "in V::V(int)\n"; } }; class A : virtual public V { A() { cout << "in A::A()\n"; }; A(int i) : V(i) { cout << "in A::A(int)\n"; } class B : virtual public V { B(){ cout << "in B::B()\n"; }; B(int i) : V(i) { cout << "in B::B(int)\n"; } class C : public A, B { C(){ cout << "in C::C()\n"; }; C(int i) : A(i), B(i) { cout << "in C::C(int)\n"; } class D : virtual public V { D() { cout << "in D::D()\n"; } D(int i) : V(i) { cout << "in D::D(int)\n"; } class E : public C, D { E() { cout << "in E::E()\n"; } E(int i) : V(i), C(i), D(i) { cout << "in E::E(int)\n"; } int main() { E e1(2); E e2; int i; cin >> i; return 0; } Joey Paquet,

33 COMP 345 - Advanced Program Design with C++
References Mark Radford. C++ Interface Classes - An Introduction. Overload Journal #62 - Aug 2004. LearnCPP.com The virtual table. Andrei Milea. Solving the Diamond Problem with Virtual Inheritance. Cprogramming.com. Walter Savitch. Polymorphic and Virtual Functions in C++. Informit, Pearson. March 2002. Robert C. Martin. Java and C++, A critical comparison. March 1997. Andrzej's C++ blog. Value semantics. February 2013. Joey Paquet,


Download ppt "Advanced Program Design with C++"

Similar presentations


Ads by Google