YouTip LogoYouTip

Cpp Polymorphism

**Polymorphism** literally means "many forms." It is used when there is a hierarchy among classes and they are related through inheritance. In C++, polymorphism is one of the key features of object-oriented programming. C++ polymorphism allows a base class pointer or reference to call a derived class's overridden method, enabling the same interface to exhibit different behaviors. Polymorphism makes code more flexible and generic. A program can operate on different types of objects through a base class pointer or reference without explicitly distinguishing the object type. This enhances code extensibility, as new shape classes can be added without modifying the main program. Here are some key points of polymorphism: **Virtual Functions**: * Declare a function as virtual in the base class using the `virtual` keyword. * Derived classes can override this virtual function. * When a virtual function is called, the version of the function to be invoked is determined based on the actual type of the object. **Dynamic Binding**: * Also known as late binding, it determines the specific implementation of a function call at runtime. * Requires using a pointer or reference to the base class to call virtual functions. The compiler determines which function to call at runtime based on the actual type of the object. **Pure Virtual Functions**: * A class containing a pure virtual function is called an abstract class and cannot be instantiated directly. * A pure virtual function has no function body and is declared using `= 0`. * It forces derived classes to provide a concrete implementation. **Implementation Mechanism of Polymorphism**: * Virtual Function Table (V-Table): C++ uses a virtual function table at runtime to implement polymorphism. Each class containing virtual functions has a virtual function table, which stores pointers to all virtual functions in the class. * Virtual Function Pointer (V-Ptr): An object contains a pointer to its class's virtual function table. **Advantages of Using Polymorphism**: * **Code Reuse**: Through base class pointers or references, different types of derived class objects can be operated on, achieving code reuse. * **Extensibility**: When adding new derived classes, there is no need to modify code that depends on the base class; only ensure the new class correctly overrides the virtual functions. * **Decoupling**: Polymorphism allows for more modular program design, reducing the coupling between classes. **Notes**: * Polymorphism only occurs when virtual functions are called through a base class pointer or reference. * If a function is called directly using a derived class object, the version in the derived class is invoked, not the version in the base class. * Polymorphism requires Runtime Type Information (RTTI), which may increase program overhead. * * * ## Example 1 We can understand the application of polymorphism through a simple example: ## Example 1 #include using namespace std; // Base class Animal class Animal { public: // Virtual function sound, providing an interface for different animals to make sounds virtual void sound()const{ cout<<"Animal makes a sound"<< endl; } // Virtual destructor ensures derived class objects are properly destructed virtual ~Animal(){ cout<<"Animal destroyed"<< endl; } }; // Derived class Dog, inheriting from Animal class Dog :public Animal { public: // Override the sound method void sound()const override { cout<<"Dog barks"<< endl; } ~Dog(){ cout<<"Dog destroyed"<< endl; } }; // Derived class Cat, inheriting from Animal class Cat :public Animal { public: // Override the sound method void sound()const override { cout<<"Cat meows"<< endl; } ~Cat(){ cout<<"Cat destroyed"<sound();// Call Dog's sound method delete animalPtr;// Free memory, calling Dog and Animal destructors // Create a Cat object and assign it to the Animal pointer animalPtr =new Cat(); animalPtr->sound();// Call Cat's sound method delete animalPtr;// Free memory, calling Cat and Animal destructors return 0; } The program output is: Dog barks Dog destroyed Animal destroyed Cat meows Cat destroyed Animal destroyed ### Code Explanation **Base class `Animal`**: * The `Animal` class defines a virtual function `sound()`, which is a virtual function (`virtual`) representing the behavior of an animal making a sound. * `~Animal()` is a virtual destructor, ensuring that when a derived class object pointed to by a base class pointer is released, the derived class destructor is correctly called to prevent memory leaks. **Derived classes `Dog` and `Cat`**: * Both `Dog` and `Cat` classes are derived from the `Animal` class and each implements the `sound()` method. * `Dog`'s `sound()` outputs "Dog barks"; `Cat`'s `sound()` outputs "Cat meows". This allows the same method (`sound()`) to exhibit different behaviors in different classes. **Main function `main()`**: * Creates a base class pointer `animalPtr`. * Uses `new Dog()` to create a `Dog` object and assigns its address to `animalPtr`. At this point, calling `animalPtr->sound()` outputs "Dog barks" because `animalPtr` actually points to a `Dog` object. * When releasing the `Dog` object, the `Dog` destructor is called first, followed by the `Animal` destructor. * Uses `new Cat()` to create a `Cat` object and assigns it to `animalPtr`, then calls `animalPtr->sound()`, outputting "Cat meows", demonstrating polymorphic behavior. ### Key Concepts * **Virtual Functions**: By declaring a virtual function in the base class using the `virtual` keyword, derived classes can override this function, allowing the correct function to be called at runtime based on the object type. * **Dynamic Binding**: C++ polymorphism is implemented through dynamic binding. At runtime, the base class pointer `animalPtr` calls the corresponding `sound()` method based on the actual type of the object it points to (`Dog` or `Cat`). * **Virtual Destructor**: In a base class with polymorphic behavior, the destructor should be declared as `virtual` to ensure that when a derived class object is deleted, the derived class destructor is called, preventing resource leaks. * * * ## Example 2 In the following example, we implement a generic Shape base class and two derived classes, Rectangle and Triangle, using polymorphism. By calling different derived class methods through a base class pointer, we demonstrate the dynamic binding characteristic of polymorphism. ## Example 2 #includeusing namespace std; class Shape{protected: int width, height; public: Shape(int a = 0, int b = 0) : width(a), height(b){}virtual int area(){cout<<"Shape class area: "<<endl; return 0; }}; class Rectangle : public Shape{public: Rectangle(int a = 0, int b = 0) : Shape(a, b){}int area()override{cout<<"Rectangle class area: "<<endl; return width * height; }}; class Triangle : public Shape{public: Triangle(int a = 0, int b = 0) : Shape(a, b){}int area()override{cout<<"Triangle class area: "<<endl; return(width * height / 2); }}; int main(){Shape *shape; Rectangle rec(10, 7); Triangle tri(10, 5); shape = &rec; cout<<"Rectangle Area: "<area()<<endl; shape = &tri; cout<<"Triangle Area: "<area()<<endl; return 0; } When the above code is compiled and executed, it produces the following output: Rectangle Area: Rectangle class area: 70Triangle Area: Triangle class area: 25 ### Code Analysis **Definition of the Shape class:** * `Shape` is an abstract base class that defines a virtual function `area()`. `area()` is a virtual function used to calculate area and uses the `virtual` keyword, allowing it to be overridden in derived classes to implement polymorphism. * `width` and `height` are `protected` attributes, accessible only within the `Shape` class and its derived classes. // Base class Shape, representing a shapeclass Shape { protected: int width, height; // Width and height public: // Constructor with default parameters Shape(int a = 0, int b = 0) : width(a), height(b) { } // Virtual function area, used to calculate area virtual int area() { cout << "Shape class area: " << endl; return 0; }}; **Definition of the Rectangle class:** * `Rectangle` inherits from the `Shape` class and overrides the `area()` method to calculate the area of a rectangle. * The `area()` method uses the `override` keyword, indicating that it overrides the `area()` method of the base class `Shape`. * `Rectangle::area()` returns `width * height`, which is the area of the rectangle. // Derived class Rectangle, representing a rectangleclass Rectangle : public Shape { public: // Constructor, initializing width and height using the base class constructor Rectangle(int a = 0, int b = 0) : Shape(a, b) { } // Override the area function to calculate the rectangle's area int area() override { cout << "Rectangle class area: " << endl; return width * height; }}; **Definition of the Triangle class:** * The `Triangle` class also inherits from `Shape` and overrides the `area()` method to calculate the area of a triangle. * `Triangle::area()` returns `width * height / 2`, which is the formula for the area of a triangle. // Derived class Triangle, representing a triangleclass Triangle : public Shape { public: // Constructor, initializing width and height using the base class constructor Triangle(int a = 0, int b = 0) : Shape(a, b) { } // Override the area function to calculate the triangle's area int area() override { cout << "Triangle class area: " <area()` is called. Since `area()` is a virtual function, it dynamically binds to `Rectangle::area()`, outputting the area of the rectangle. * Next, the `shape` pointer is pointed to the `Triangle` object `tri`. When `shape->area()` is called, it dynamically binds to `Triangle::area()`, outputting the area of the triangle. // Main functionint main() { Shape *shape; // Base class pointer Rectangle rec(10, 7); // Rectangle object Triangle tri(10, 5); // Triangle object // Point the base class pointer to the rectangle object and call the area function shape = &rec; cout << "Rectangle Area: " <area() << endl; // Point the base class pointer to the triangle object and call the area function shape = &tri; cout << "Triangle Area: " <area() <area()` is called, it dynamically binds to the corresponding `area()` implementation at runtime based on the actual type of the object `shape` points to (`Rectangle` or `Triangle`). This mechanism of determining which function to call at runtime is called dynamic binding and is the core of polymorphism. * **Polymorphism of Base Class Pointers**: The base class pointer `shape` can point to any object derived from `Shape`. When `shape` points to different derived class objects, calling `shape->area()` produces different behaviors, demonstrating the characteristic of polymorphism. **Virtual functions** are functions declared with the keyword **virtual** in the base class. **Virtual functions** allow subclasses to override them, enabling dynamic binding by calling the overridden version in the derived class through a base class pointer or reference at runtime. What we want is to be able to select the function to call based on the type of the object being called at any point in the program. This operation is called **dynamic linking** or **late binding**. **Characteristics:** * **Can have an implementation in the base class**. Typically, virtual functions provide a default implementation in the base class, but subclasses can choose to override them. * **Dynamic Binding**: The appropriate function version is called at runtime based on the actual type of the object. * **Optional Override**: Derived classes can optionally override virtual functions, but it is not mandatory. ## Example #include using namespace std; class Animal { public: virtual void sound(){// Virtual function cout<<"Animal makes a sound"<< endl; } }; class Dog :public Animal { public: void sound() override {// Override virtual function cout<<"Dog barks"<sound();// Output: Dog barks delete animal; } In the above code, `sound` is a virtual function of the `Animal` class. When `sound()` is called through the `Animal*` pointer `animal`, the program selects to call `Dog::sound()` based on the actual object type (`Dog`). You might want to define a virtual function in the base class so that it can be redefined in derived classes to better suit the object, but you cannot provide a meaningful implementation for the virtual function in the base class. In this case, a pure virtual function is used. A pure virtual function is a virtual function without an implementation, declared in the base class using `= 0`. A pure virtual function indicates that the base class defines an interface, but the specific implementation is the responsibility of the derived class. Pure virtual functions make the base class an abstract class, which cannot be instantiated. **Characteristics:** * **Must be declared as `= 0` in the base class**, indicating no implementation, and subclasses must override it. * **Abstract Class**: A class containing pure virtual functions cannot be instantiated directly; all pure virtual functions must be implemented through derived classes to create objects. * **Interface Definition**: Pure virtual functions are typically used to define interfaces, allowing derived classes to implement specific behaviors. We can rewrite the virtual function `area()` in the base class as follows: #includeusing namespace std; class Shape{public: virtual int area() = 0; }; class Rectangle : public Shape{private: int width, height; public: Rectangle(int w, int h) : width(w), height(h){}int area()override{return width * height; }}; int main(){Shape *shape = new Rectangle(10, 5); cout<<"Rectangle Area: "<area()<<endl; delete shape; } `= 0` tells the compiler that the function has no body. The above virtual function is a **pure virtual function**. ### Comparison Between Virtual Functions and Pure Virtual Functions | Feature | Virtual Function | Pure Virtual Function | | --- | --- | --- | | Definition | Declared with `virtual` in the base class, has an implementation | Declared with `= 0` in the base class, no implementation | | Subclass Override | Subclasses can choose to override | Subclasses must implement | | Abstractness | Class can be instantiated | Makes the class an abstract class, cannot be instantiated | | Purpose | Provides default behavior, allows subclasses to override | Defines an interface, forces subclasses to implement specific behavior |
← Collection RotateCollection Sublist β†’