What is meant by polymorphism in C ++

Introduction to C ++ Inheritance and Polymorphism

Transcript

1 Introduction to C ++ Inheritance and Polymorphism Process Basic properties of inheritance in C ++ Redefinition and name resolution A simple class hierarchy in Java and C ++ Redefinition of virtual elements and polymorphism Dynamic and static type conversion (casts) Virtual destructors Overloading operators and inheritance Multiple inheritance 1

2 Objectives You know what inheritance is in principle, you know the difference between public, protected and private inheritance, and you understand the principle of constructive structure. You know what redefinition of elements means. You understand how polymorphism works in C ++ and what influence virtual ones have on it You have methods You understand the concept of dynamic and static type conversion (casts) and can apply these casts You understand the concept of virtual destructors and the relationship between overloaded operators and inheritance You understand the basic idea of ​​multiple inheritance, the potential problems that arise in doing so and how to deal with these problems Introduction (1) In C ++ (as in Java) classes can be derived Inheritance Superclass Subclass Subclass ... Subclass Subclass Superclass: class from which is inherited Subclass: the class which inherits the methods From a Subclasses can in turn be derived from subclasses w ground the terms superclass and subclass are relative No interfaces in C ++ Multiple inheritance in C ++: a class can be derived from more than one class 2

3 Introduction (2) A subclass inherits all public and protected (but not private) methods and all data elements of all its superclasses (including the "indirect" superclasses). Subclasses can overwrite a method of a superclass and thus cover the method of the superclass in the subclass Redefinition of elements Also applies to data elements and static variables Introduction (2) Like Java, C ++ supports polymorphism: - a variable of the type of a superclass can contain any variable of the type of a subclass Generally: Inheritance and polymorphism are handled more flexibly than in Java, but are Also error-prone example: in Java an object variable is always "polymorphic", in C ++ this is controlled by the programmer 3

4 Inheritance (1) Definition of a derived class with colon: and the access specifier (specification of the access rights): Java: class B extends A {... C ++: class B: public A {... All public and protected methods and Variables are inherited and are therefore also available in the subclass.This visibility can be further restricted by specifying the access rights after the colon: Variable / method in superclass public protected subclass inherited as public protected private (Def.) Variable / method in subclass public protected private protected protected private private No access No access No access Inheritance (2) Note that the default access specifier is private and not public, even if you need public in the vast majority of cases. Java does not recognize this restriction by means of access specifiers during inheritance - it is always inherited publicly. A protected element b is protected from external access like a private element. This b is not addressable for objects of the superclass and subclass. In contrast to a private element, methods of derived classes can access it. 4th

5 Inheritance (3) Example of private / protected / public inheritance: a.priv ++; a.prot ++; a.publ ++; b.priv ++; b.prot ++; b.publ ++; class A {private: int priv; protected: int prot; int publ; Access from outside (f ()) pxxx = private no pxxx = protected no no yes no no no pxxx = public yes class B: pxxx A {void m () {... void f () {A a; B b; Access within B (m ()) priv ++; prot ++; publ ++; independent of pxxx no yes yes inheritance (4) private / protected inheritance: all public methods of the superclass are no longer public often too strong a restriction in the subclass Remedy: explicit reimplementation of the methods in the subclass, whereby these methods explicitly call the methods of the superclass class A {void func1 () {void func2 () {class B: protected A {void func2 () {A :: func2 (); int main () {B b; b.func1 (); // compiler error, protected b.func2 (); // OK, public The range operator :: is generally used when a class is to be accessed explicitly private / protected inheritance is rarely used, possibly an indication of an unclean OO design! 5

6 Inheritance (5) private / protected inheritance is rarely used, possibly an indication of an unclean OO design! Accordingly, it was left out in Java. Inheritance (6) Constructors: If B is a subclass of A, the following happens when an instance of class B is initialized: First, the default constructor of class A is called. Then the (the initialization) corresponding constructor of class B is called. To call a constructor other than the default constructor of class A, this must be specified explicitly in the constructor of class B with a base initializer (Java: super (...)) Exists If the default or explicitly specified constructor is not in class A, there is an error when compiling Destructors are called in the reverse order to the constructor (first the destructor of class B, then that of class A) 6

7 Inheritance (7) Example: Calling the constructors (without base initializers) #include // constructor1.

8 Redefinition of elements (1) Redefinition (of non-virtual methods): There are 2 possibilities for the names of data elements or methods in the subclass Name does not exist in the superclass No redefinition Name in the superclass redefinition An element redefined in the subclass hides this Corresponding element / method in the superclass Redefinition and overloading When redefining (of non-virtual methods), the signature and the return type of both methods may be different. A redefinition of a method always hides the superclass method of the same name. However, methods in the same class can be overloaded. Therefore a method of the super class can be redefined several times. Redefinition of methods in subclasses (1) Java & C ++: A method of a superclass can be overloaded in a subclass Java: An instance of the subclass can simply use the methods of the superclass and the overloaded methods in the subclass: // Overloading.java class Parent {public void test () {System.out.println ("test ()"); class Child extends Parent {public void test (int i) {System.out.println ("test (int)"); public class Overloading {public static void main (string [] args) {Child c = new Child (); c.test (1); // outputs test (int) c.test (); // prints test () 8

9 Redefinition of methods in subclasses (2) The same example in C ++: // overloading.cpp class Parent {void test () {cout << "test ()" << endl; class Child: public parent {void test (int i) {cout << "test (int)" << endl; int main () {child c; c.test (1); c.test (); When compiling with g ++ the following output results: Name.cpp: In function `int main () ': Name.cpp: 11: error: no matching function for call to` Child :: test () `Name.cpp: 113: error : candidates are: void Child :: test (int) Redefinition of methods in subclasses (3) Reason for the compilation error: C ++ proceeds as follows when resolving names: 1. Search for the first class (starting with its own class) in which the name of the called method occurs test is found in the Child class 2. Search for the method within this Child class knows test (int), but not test () Compile error Remedy: Every method overloaded in subclasses should be redefined there class Child: public Parent {void test () {Parent :: test (); void test (int i) {cout << "test (int)" << endl; 9

10 A Simple Class Hierarchy To discuss inheritance and polymorphism in detail, we use the following class hierarchy: Car Japanese Car Italian Car German Car Mazda Nissan Ferrari Fiat BMW Opel VW We limit ourselves to the "yellow" class implementation in Java // AutoProg. java abstract class Auto {abstract public void mark (); public class Italian car extends Auto {public void mark () {System.out.println ("Macchina Italiana"); public void drive250 () {System.out.println ("goes not "); public class Fiat extends ItalianCar {public void mark () {System.out.println (" Fiat "); public class Ferrari extends ItalianCar {public void mark () {System.out.println (" Ferrari "); public void drive250 () {System.out.println ("250!"); 10

11 Implementation in C ++ // autoprog.cpp class Auto {Purely virtual method virtual void mark () = 0; class ItalianCar: public Car {virtual void mark () {cout << "Macchina Italiana" << endl; void drive250 () {cout << "does not work" << endl; class Fiat: public Italian car {virtual void brand () {cout << "Fiat" << endl; class Ferrari: public Italian car {virtual void brand () {cout << "Ferrari" << endl; void drive250 () {cout << "250!" << endl; Redefining elements (1) Redefining elements: A subclass implements a method of a superclass "new". Class Auto {virtual void mark () = 0; class Italian car: public Auto {virtual void mark () {cout << "Macchina Italiana "<< endl; void drive250 () {cout <<" does not work "<< endl; class Fiat: public Italian car {virtual void brand () {cout <<" Fiat "<< endl; class Ferrari: public Italian car { virtual void mark () {cout << "Ferrari" << endl; void drive250 () {cout << "250!" << endl; 11

12 Redefining elements (2) If a method is overwritten, the method of the superclass is no longer visible in an instance of the subclass and the "new" version of the method is used: Ferrari f = new Ferrari (); // Java Italian car ia = new ItalienischesAuto (); ia.marke (); // Macchina Italiana ia.fahre250 (); // does not work for.brand (); // Ferrari drive250 (); // 250! Ferrari f; / / C ++ ItalienischesAuto ia; ia.marke (); // Macchina Italiana ia.fahre250 (); // does not work for.marke (); // Ferrari drive250 (); // 250! Redefine elements (3) If an overwritten method of the superclass is to be accessed in the subclass, the range operator :: can be used. From the Ferrari class, the drive250 method of the ItalianCar class can be used class Ferrari: public ItalianCar {... void drive250 () { cout << "250!" << endl; ItalienischesAuto :: fahre250 (); This corresponds to the functionality that you can use with the keyword super in Java e r reaching. 12th

13 Polymorphism (1) One of the most important concepts of OO programming polymorphism: a variable of the type of a superclass can contain any object of the type of a subclass In other words: the static type of a variable can be of the dynamic type (the type of value that the Variable just contains) distinguish car example: A variable of type ItalianAuto (static type) ... may contain a variable of type Ferrari (dynamic type) The relationship between polymorphism and redefining of elements is an important aspect of object-oriented programming language In Java the dynamic type method is always used. In C ++, the behavior is determined by the programmer. Polymorphism (2) Polymorphism in Java: Ferrari f = new Ferrari (); f.brand (); // Ferrari drive250 (); // 250! Italian car ia = f; ia.brand (); // Ferrari ia.fahr250 (); // 250! Java: Although the static type of ia is Italian car, the dynamic type (Ferrari) methods are used! This is always the case in Java and cannot be influenced by the programmer 13

14 Polymorphism (3) Polymorphism in C ++ with assignment of objects: Ferrari f; f.brand (); // Ferrari drive250 (); // 250! Italian car ia = f; ia.brand (); // Macchina Italiana ia. Drive250 (); // does not work If objects are assigned to each other, the methods of the static type (Italian car) are always used in this case there is no polymorphism at all. When assigning, no pointers are copied, but the objects themselves Object of the type Italian car has "no space", the extensions of the Ferrari class are omitted. The object is converted to an Italian car and behaves in exactly the same way Polymorphism (4) Polymorphism in C ++ with assignment of pointers: Ferrari f; f.brand (); // Ferrari f.fahre250 (); // 250! ItalienischesAuto * ia = & f; ia-> Marke (); // Ferrari ia-> drive250 (); // does not work

15 Polymorphism (5) Polymorphism naturally also works on the heap, where pointers are used "automatically": Ferrari * f = new Ferrari (); f-> brand (); // Ferrari f-> drive250 (); / / 250! ItalienischesAuto * ia = f; ia-> Marke (); // Ferrari ia-> fahre250 (); // does not work

16 Polymorphism (7) Polymorphism is often referred to as early binding (static binding) and late binding (dynamic binding) Early binding: you already know which method will be executed during compilation (static type): ia.marke (); Late binding: the dynamic type and thus the corresponding method are only known at runtime: ia-> mark (); A method that is not virtual in a superclass cannot be made virtual in subclasses.A virtual method always remains virtual, even if the virtual keyword is not specified in subclasses (in this case the keyword virtual is optional), as in Java There are abstract classes in C ++ (car in our hierarchy) Can not be instantiated Contain one (or more) pure virtual methods: virtual, no body, but = 0 at the end, e.g. Method mark () Redefine elements (5) Let's extend the class ItalianCar by markefahr250: class ItalianCar: public Auto {virtual void mark () {cout << "Macchina Italiana" << endl; void drive250 () {cout << "does not work" << endl; void brand drive250 () {brand (); drive250 (); Test program Ferrari f; f.brand (); // Ferrari drive250 (); // 250! f.markefahre250 () // Ferrari does not work Direct call of mark and drive250: Methods of Ferrari are executed Indirect call (via markefahre250) With mark, the method of the class Ferrari is carried out. With drive250, the method of Italian car is carried out 16

17 Redefining elements (6) Explanation: this is because of whether the methods are virtual or not: markefahr250 is a method from ItalianCar markefahr250 therefore also calls the methods mark and drive250 from ItalianCar to drive250 is not virtual it will always be the method of ItalianCar called, regardless of whether markefahre250 is executed for a variable of the type Italian car or a subclass (Ferrari) mark is virtual the method of the type of the variable (Ferrari) is executed Here, too, C ++ behaves like Java if all methods in C ++ are virtual Polymorphism (7) In C, the code could only be expanded in one direction: new code calls old code Polymorphism is an object-oriented approach and allows old code to call new code 17

18 Polymorphism (7)! "# # $ The correct function is NOT called! Output In Base :: f (int x) In Base :: f (int x) Polymorphism (8)!" # # $ Polymorphic function Derived :: f redefines Base :: f The correct function is called Output In Base :: f (int x) In Derived :: f (int x) 18

19 Polymorphism (9)! "# # $ Old code calls new code At the time # was created, the class must not have existed! Exercise 1 on inheritance and polymorphism class Monster {virtual void augen () {cout <<": " ; virtual void nose () {cout << '-'; virtual void mouth () {cout << ''; void show () {eyes (); nose (); mouth (); cout << endl; monster bite Vampire class beisser: public monster {virtual void mouth () {cout << 'x'; virtual void beiss () {cout << "beiss beisser" << endl; class vampire: public beisser {virtual void eyes () {cout << "8"; virtual void mouth () {cout << '#'; virtual void bite () {cout << "bite vampire" << endl; 19

20 Exercise 1 on inheritance and polymorphism class Monster {virtual void augen () {cout << ":"; virtual void nose () {cout << '-'; virtual void mouth () {cout << ''; void show () {eyes (); nose(); mouth(); cout << endl; Monster m; m.display (); Beisser b; b.show (); Vampire v; v.display (); m = v; m.display (); m = b; m.20

21 Downcasting Downcasting means the conversion of a polymorphic variable into its dynamic type. In Java there is a check at runtime whether an object is really converted correctly: ItalienischesAuto ia = new Fiat (); Fiat ft = (Fiat) ia; // correct Ferrari fe = (Ferrari) ia; // Generates a ClassCastException In C ++ there is no such check: ItalienischesAuto * ia = new Fiat (); Fiat * ft = (Fiat *) ia; // correct Ferrari * fe = (Ferrari *) ia; // Behavior not defined In Java you can find out the dynamic type with instanceof: ItalienischesAuto ia = new Fiat (); if (ia instanceof Fiat) {Fiat ft = (Fiat) ia; ... Dynamic type conversion C ++: no instanceof, but a special conversion type, the dynamic cast dynamic cast <> The dynamic cast checks the validity of the conversion and returns the in the event of an error Return value NULL: ItalienischesAuto * ia = new Fiat (); Fiat * ft = dynamic_cast (ia); if (ft) {... // Cast valid else {// Cast not valid (ft == NULL) A dynamic cast only works if the corresponding variable is polymorphic, that is: If a pointer to the object is used the class contains at least one virtual method 21

22 Static type conversion There is also a static_cast: the dynamic type is not checked and no pointers have to be used. Very similar to a "normal" cast such as (Ferrari *) or (int). The static cast, however, checks the static type Conversion from int * to int or different class hierarchies is prevented. Is preferable to normal cast. Example of normal casts and static type conversion void * v = ...; // We know that v points to a Ferrari. Ferrari * fe = static_cast (v); int * pi = new int; * pi = 12; int b = (int) pi; // normal cast int * -> int: OK, but f int c = static_cast ( pi); // Static Cast * int-> int compile error double d = 3.14; int e = static_cast (d); // Static Cast double -> int OK Virtual destructors (1) In connection with polymorphism and objects on For the heap, the question arises whether a destructor should be declared virtually. Extension of our hierarchy with destructor: class Auto {virtual ~ Auto () {cout << "Bye bye Auto" << endl; ... class Ferrari: public ItalienischesAuto {~ Ferrari () {cout << "Ciao Ferrari" << endl; Test program: Italian car * ia = new Ferrari (); delete ia; // Output: Ciao Ferrari, Bye bye Auto The following applies: With a virtual destructor, all destructors are executed from bottom to top starting with the dynamic type (Ferrari) 22

23x << "" << c.y << endl; return o; 23

24 Operator overload and inheritance (2) Test program: Point3D c; ++ c; // x, y (from Point2D) are incremented, but not z cout << c; // Task of x, y, but not z Point3D inherits the operators from Point2D, with the following behind it: The ++ operator is a "normal" method and is therefore inherited. The only parameter from the ++ operator (Point2D) is a super class of Point3D. That is why the method also works with Point3D The << operator is implemented as a function and therefore not part of a class: Which means that the function is not inherited. Point3D can use the function because Point3D is a subclass of Point2D operator overloading and inheritance (3) For the desired functionality for Point3D, the operators can be newly implemented as methods / functions: class Point3D: public Point2D {double z; Point3D & operator ++ () {++ x; ++ y; ++ z; return * this; friend ostream & operator << (ostream &, const Point3D &); ostream & operator << (ostream & o, const Point3D & c) {o << cx << "" << cy << << cz << endl; return o ; Test program: Point3D c; ++ c; // x, y and z are incremented cout << c; // e.g. Output:

25 Operator overloaded and inheritance (4) The << operator is now defined twice as a function (for Point2D and Point3D). But because the functions have different signatures, this is not a problem. Polymorphism also applies to overloaded operators implemented as methods. As with normal methods, these must be declared as virtual for polymorphism to work at all. Polymorphism with ++ operator in the class Point2D virtual Point2D & operator ++ () {++ x; ++ y; return * this; Polymorphism does not work with operators that are overloaded as functions outside of classes. Multiple inheritance (1) C ++: A class can be derived from several super classes Multiple inheritance If possible, multiple inheritance should be avoided (error-prone, confusing), but it can still be useful ... And you should at least understand the basic idea. The subclass inherits the properties of all superclasses. In principle, multiple inheritance is also fairly problem-free, but it becomes more difficult when the following occurs: Two or more superclasses have a method with the same signature or a method a () subclass class A class B1 class B2 subclass method a () instance variable with the same name which should be used now in the subclass? Two or more super classes in turn have the same super class, the class and its properties would now be duplicated 25

26 Multiple inheritance (2) Example with the werewolf class, which is descended from Monster and Wolf: Monster Wolf class Monster {void frighten () {cout << "buuuuhh!" << endl; class Wolf {void howl () {cout << "heuuull!" << endl; class werewolf: public monster, public wolf {int main () {werewolf ww; ww. scare (); // buuuuhh ww.howl (); // heuuull! Werewolf multiple inheritance (3) Extension: Wolf implements a scare method: class Monster {void scare () {cout << "buuuuhh!" << endl; class wolf {void scare () {cout << "fletsch!" << endl; void howl () {cout << "heuuull!" << endl; class werewolf: public monster, public wolf {int main () {werewolf ww; ww. to frighten; // compile error ww.monster::erschrecken (); // buuuuhh! ww.wolf :: scare (); // smack! ww.how (); // heuuull! If several methods (or instance variables) are inherited with the same signature, the method must be clearly specified when called! Class :: 26

27 Multiple inheritance (4) Multiple inheritance with common base class: class Monster {protected: int victim; Monster (): victim (0) {void scare () {cout << "buuuuhh!" << endl; class Beisser: public Monster {void friss () {victim ++; class Zombie: public Monster {void friss () {victim ++; class vampire: public biter, public zombie {monster biter vampire monster zombie int main () {vampire dracula; dracula.friss (); // compile error! dracula.scare (); // compile. // compile error! dracula.monster::scare (); // buuuuhh! dracula.beisser :: scare (); // buuuuhh! dracula.zombie :: scare (); dracula.beisser :: friss (); dracula.zombie :: friss (); Multiple inheritance (4) Multiple inheritance with common base class: class Monster {Monster Monster protected: int victim; Beisser Zombie Vampire Monster (): victim (0) {void scare () Also {applies here: methods int main () {cout << "buuuuhh!" << endl; (or instance variables) vampire dracula; must be clearly dracula.friss (); // compilation error! class Beisser: public specifies monsters {become! dracula.scare (); // compile. Class :: // compile error! void friss () {victim ++; dracula.monster::scare (); // buuuuhh! class Zombie: public Monster {dracula.beisser :: scare (); // buuuuhh! void friss () {victim ++; dracula.zombie :: scare (); dracula.beisser :: friss (); class Vampire: public Beisser, dracula.zombie :: friss (); public zombie {27

28 Multiple inheritance (5) Virtual multiple inheritance means that a common base class is inherited only once. The keyword virtual is used for this purpose: Has nothing to do with the virtual in virtual methods (shared would have been better here) Beisser Monster Vampire Zombie This is more accurate Designation of the methods used and instance variables of the common base class Monster is no longer necessary. However, access to methods with the same signatures or instance variables with the same name in the Beisser and Zombie classes must still be clearly specified! The keyword virtual has no influence on single inheritance or multiple inheritance without a common base class Multiple inheritance (6) Monster - Beisser - Zombie - Vampire with virtual inheritance class Monster {protected: int victim; Monster (): victim (0) {void scare () {cout << "buuuuhh!" << endl; class Beisser: public virtual monster {void friss () {victim ++; class Zombie: public virtual monster {void friss () {victim ++; class Vampire: public Beisser, public Zombie {Beisser monster Vampire Zombie A virtual super class only has an effect in the case of multiple inheritance; 28

29 Multiple inheritance (7) Monster - Biter - Zombie - Vampire with virtual inheritance class Monster {protected: int victim; Monster (): victim (0) {int main () {void scare () {cout << "buuuuhh!" << endl; Vampire dracula; dracula.friss (); // compilation error! dracula.scare (); // buuuuhh! class Beisser: public virtual monster {dracula.monster::erschrecken();//buuuuhh! void friss () {victim ++; dracula.beisser :: scare (); // buuuuhh! dracula.zombie :: scare (); // buuuuhh! class Zombie: public virtual dracula.beisser :: friss (); Monster {// bitter necessary dracula.zombie :: friss (); // Zombie necessary void friss () {victim ++; class Vampire: public Beisser, public Zombie {Summary In C ++ there is public, protected and private inheritance, with which the visibility of instance variables and methods in subclasses can be restricted. this means that the old method is no longer visible in the subclass. Polymorphism means that a variable of the type of a superclass can contain any variable of the type of a subclass.If objects (and not pointers) are assigned, the methods of the static type of the variable are always used.If pointers to objects are assigned, the methods of the dynamic type are used used if the corresponding method has been defined as virtual. A dynamic cast can be used to convert a polymorphic variable into its dynamic type. Overloaded operators are also inherited. C ++ supports multiple inheritance 29