Home > Enterprise >  Polymorphic pointer change at run time
Polymorphic pointer change at run time

Time:04-10

I am really confused about polymorphic pointers. I have 2 classes derived from an interface as shown below code.

#include <iostream>

using namespace std;

class Base  {

public:
    virtual ~Base() { }
    virtual void addTest() = 0;
};

class B: public Base {

public:
    B(){}
    ~B(){}

    void addTest(){
        cout << "Add test B\n";
    }
};
class C: public Base {

public:
    C(){}
   ~C(){}

    void addTest(){
        cout << "Add test C\n";
    }
private:   
    void deleteTest(){
        
    }
    
};

int main()
{
    Base *base = new B();
    base->addTest();
    base = new C();
    base->addTest();
    return 0;
}

I want to change the pointer dynamically according to a condition at run time to use the same pointer with different kinds of scenarios.

Derived classes are different from each other, so what happens in memory when the polymorphic pointer object changes?

If that usage is not good practice, how can I change the polymorphic pointer object dynamically at the run time?

CodePudding user response:

It's perfectly fine to change what a pointer points to. A Base* is not an instance of Base, it is a pointer that points to an instance of a Base (or something derived from it -- in this case B or C).

Thus in your code, base = new B() sets it to point to a new instance of a B, and then base = new C() sets it to point to a new instance of a C.

Derived classes are different from each other, so what happens in memory when the polymorphic pointer object changes?

Because Base* points to an instance of a Base, all this is doing is changing which instance (or derived instance) Base* points to. In effect, it just changes the memory address of the pointer.

From that Base* pointer, you still have access to anything defined in that Base class -- which still allows polymorphic calls to functions satisfied by derived types if the function is defined as virtual.

The exact mechanism for how this is dispatched to derived types is technically an implementation-detail of the language, but generally this is done through a process called double-dispatch, which uses a "V-Table". This is additional type-information stored alongside any classes that contain virtual functions (it's conceptually just a struct of function pointers, where the function pointers are satisfied by the concrete types).

See: Why do we need a virtual table? for more information on vtables.


What is problematic, however, is the use of new here. new allocates memory that must be cleaned up with delete to avoid a memory leak. By doing the following:

Base *base = new B();
base->addTest();
base = new C(); // overwriting base without deleting the old instance
base->addTest();

The B object's destructor is never run, no resources are cleaned up, and the memory for B itself is never reclaimed. This should be:

Base *base = new B();
base->addTest();
delete base;
base = new C(); // overwriting base without deleting the old instance
base->addTest();
delete base;

Or, better yet, this should be using smart-pointers like std::unique_ptr to do this for you. In which case you don't use new and delete explicitly, you use std::make_unique for allocation, and the destructor automagically does this for you:

auto base = std::make_unique<B>();
base->addTest();
base = std::make_unique<C>(); // destroy's the old instance before reassigning
base->addTest();

This is the recommended/modern way to write dynamic allocations

  • Related