Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding Polymorphism and Virtual Functions in C++

Tech Oct 25 1

Polymorphism allows objects of differant classes to be treated as objects of a common base class, enabling a single interface to invoke different behaviors. In C++, polymorphism is categorized into compile-time and runtime types.

Compile-time polymorphism, or static binding, includes function overloading, operator overloading, templates, and std::bind with std::function. The compiler resolves these at compile time based on function signatures.

Runtime polymorphism, or dynamic binding, is achieved through inheritance and virtual functions. It allows the selection of functon implementations at runtime based on the actual object type.

Virtual Functions

A virtual function is declared in a base class using the virtual keyword. Derived classes can override it to provide specific implementations. When a base class pointer or reference calls a virtual function, the function corresponding to the actual object type is executed.

#include <iostream>
#include <memory>
using std::cout;
using std::endl;

class Creature {
public:
    virtual void vocalize() const {
        cout << "Creature emits noise." << endl;
    }
};

class Canine : public Creature {
public:
    void vocalize() const override {
        cout << "Canine howls." << endl;
    }
};

class Feline : public Creature {
public:
    void vocalize() const override {
        cout << "Feline purrs." << endl;
    }
};

int main() {
    std::unique_ptr<Creature> ptr1 = std::make_unique<Canine>();
    std::unique_ptr<Creature> ptr2 = std::make_unique<Feline>();
    ptr1->vocalize();
    ptr2->vocalize();
    return 0;
}

Classes with virtual functions include a virtual function pointer (vptr) pointing to a virtual function table (vtable), which stores addresses of virtual functions. Each class has its own vtable, and overriding updates entries in the derived class's vtable.

Dynamic polymorphism requires:

  • A virtual functon in the base class.
  • Override in the derived class.
  • Creation of a derived class object.
  • A base class pointer or reference to the derived object.
  • Invocation of the virtual function through that pointer or reference.

Functions that cannot be virtual include constructors, static member functions, inline functions, and ordinary non-member functions.

Abstract Classes and Pure Virtual Functions

An abstract class contains at least one pure virtual function, declared with = 0. It cannot be instantiated but can have pointers to derived class objects.

class Shape {
public:
    virtual void draw() const = 0;
};

class Circle : public Shape {
public:
    void draw() const override {
        cout << "Drawing circle." << endl;
    }
};

int main() {
    Shape* shapePtr = new Circle();
    shapePtr->draw();
    delete shapePtr;
    return 0;
}

Virtual Destructors

Declaring a base class destructor as virtual ensures proper cleanup when deleting derived objects through a base pointer.

class Base {
public:
    Base() { data = new int(5); }
    virtual ~Base() {
        delete data;
        cout << "Base destructor." << endl;
    }
private:
    int* data;
};

class Derived : public Base {
public:
    Derived() { extra = new int(10); }
    ~Derived() override {
        delete extra;
        cout << "Derived destructor." << endl;
    }
private:
    int* extra;
};

int main() {
    Base* obj = new Derived();
    delete obj;
    return 0;
}

Multiple Inheritance with Virtual Functions

In multiple inheritance, each base class with virtual functions has its own vtable. Derived class virtual functions may be added to the first vtable, and overrides adjust entries accordingly.

class Primary {
public:
    virtual void func() { cout << "Primary::func" << endl; }
};

class Secondary {
public:
    virtual void func() { cout << "Secondary::func" << endl; }
};

class Combined : public Primary, public Secondary {
public:
    void func() override { cout << "Combined::func" << endl; }
};

int main() {
    Combined c;
    Primary* p1 = &c;
    Secondary* p2 = &c;
    p1->func();
    p2->func();
    return 0;
}

Virtual Inheritance

Virtual inheritance resolves ambiguity in diamond inheritance by ensuring a single instance of a shared base class. It uses a virtual base pointer (vbptr) to a virtual base table (vbtable) for offset information.

class Core {
public:
    int value = 42;
};

class Branch1 : virtual public Core {};
class Branch2 : virtual public Core {};

class Leaf : public Branch1, public Branch2 {};

int main() {
    Leaf l;
    l.value = 100;
    cout << l.value << endl;
    return 0;
}
Tags: C++

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.