Search code examples
c++referencewrapperpure-virtualtemplate-classes

c++ store a reference to a pure virtual class as member of class


I have a templated wrapper containing an instance of a class which inherits of a pure virtual class.
My problem is how to store the data inside the wrapper.
- I can't use a copy because pure virtual classes can't be instanciated (or sliced if I use a simple virtual class).
- I didn't managed to keep a reference. This ref becomes invalid because I don't manage the allocation of the object I get (out of scope).
- My only solution is to use pointers, even if I wanted to avoid that because that's not really safe and I need my code to be robust.
What can I do?

Here's a little example that simulate my problem:

#include <iostream>
#include <string>
#include <ctime>
#include <cmath>

using namespace std;

class Module
{
public:
    Module() : m(rand())
    {
        cout << "m = " << m << endl;
    }

    virtual void f() = 0;

    int m;
};

class ModuleA : public Module
{
public:
    ModuleA() : ma(rand())
    {
        cout << "ma = " << ma << endl;
    }

    void f() {}

    int ma;
};

template<typename T>
class Container
{
public:
    Container(T e) : element(e) {}

    T element;
};

// Objects are created outside of main
ModuleA createModule()
{
    return ModuleA();
}

Container<Module&> createContainer()
{
    return Container<Module&>(createModule());
}

int main()
{
    srand((unsigned int)time(NULL));

    Container<Module&> conta = createContainer();

    ModuleA& ca1 = dynamic_cast<ModuleA&>(conta.element); // wrong !

    system("pause");

    return 0;
}

Solution

  • You can use a std::shared_ptr in your container, i.e.

    template<typename T>
    class Container
    {
    public:
        // The pointer you get must be managed as well
        Container(std::shared_ptr<T> e) : element(e) {}
    
        std::shared_ptr<T> element;
    };
    

    and that would be perfectly safe. Indeed if the object goes out of scope in the code that created it, you still have a valid pointer until the container goes itself out of scope. You can tweak a little further the memory ownership relations with std::weak_ptr or std::unique_ptr if the semantics of the std::shared_ptr don't fit exactly your case.

    You should definitely look into std::weak_ptr, as it allows you to forbid some code to take ownership of a pointer, but still permits access if the pointer is valid at the location where you need to access it. It also prevents memory retain cycles because an std::weak_ptr doesn't own the memory.