1

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;
}
Antoine
  • 910
  • 1
  • 9
  • 26
  • This shouldn't compile. You are binding non-const references to temporary objects. – Neil Kirk Mar 19 '15 at 11:24
  • This code compile in Visual Studio 2013 without any warning. – Antoine Mar 19 '15 at 11:24
  • @AntoineLafarge then you're using non-standard extensions (they may be enabled by default in your compiler). You should state the compiler in the question. – eerorika Mar 19 '15 at 11:29
  • You **can** copy by value, because your `T` is `ModuleA`, which is a concrete class. – Giulio Franco Mar 19 '15 at 11:30
  • @user2079303 I didn't modified the vc++ compiler or visual studio (community). – Antoine Mar 19 '15 at 11:31
  • It compiles and links OK for me (VS 2013) too. – Robinson Mar 19 '15 at 11:32
  • @GiulioFranco No because I store a Module&, and not a ModuleA&. Compuler don't want to copy by value here. – Antoine Mar 19 '15 at 11:33
  • @AntoineLafarge I'm not suggesting that you did. I'm saying that you're using language features that are not part of c++ and only available in your compiler. That's why you should mention the compiler in the question. Also, regarding your answer to Giulio, you don't have any `Module&` in your code. – eerorika Mar 19 '15 at 11:34
  • 1
    @AntoineLafarge I don't see any `Module&` in your code. The only ref I see is `T& Container::element`, and the only instantiation I see is `Container`. Just replace `T& element` with `T element` and it works. – Giulio Franco Mar 19 '15 at 11:36
  • 2
    It seems to me this is a question of ownership; something I've also struggled with in the past with C++. Use unique_ptr for internal ownership, only handing out raw pointers from it to other things you own (encapsulated entities). Use shared_ptr for things you return to callers, but return them a weak_ptr, the contract being "if I return you a weak_ptr, under no circumstances do you construct a shared_ptr with it and store that instead - I'm responsible for this object". These are a few things I've learned over the years. – Robinson Mar 19 '15 at 11:36
  • 1
    Increase your warning level: `warning C4239: nonstandard extension used : 'argument' : conversion from 'ModuleA' to 'ModuleA &'` – Neil Kirk Mar 19 '15 at 11:38
  • 1
    @GiulioFranco My mistake, I just updated the code to show what I want to do. – Antoine Mar 19 '15 at 11:39
  • 1
    @AntoineLafarge now I see the problem, and you're no longer using non-standard + bogus C++ extension. – Giulio Franco Mar 19 '15 at 11:41
  • @GiulioFranco Okay JBL suggested using shared_ptr which is promising. – Antoine Mar 19 '15 at 11:43
  • 2
    @AntoineLafarge I strongly suggest you look into `std::weak_ptr` as well. – JBL Mar 19 '15 at 11:46

1 Answers1

2

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.

JBL
  • 12,588
  • 4
  • 53
  • 84
  • 2
    @GiulioFranco OP stated that "This ref becomes invalid because I don't manage the allocation of the object I get (out of scope)." so I fairly assumed the wrapper shouldn't be the only owner. That wouldn't be pretty if the container goes out of scope, but the object is still referenced somewhere else through e.g. a raw pointer. – JBL Mar 19 '15 at 11:34
  • 2
    I would make element private and create a public member function that returns weak_ptr. I wouldn't return raw pointers from a unique_ptr. – Robinson Mar 19 '15 at 11:39
  • 1
    @Robinson Well then it depends on what OP really needs. Still agree for the last sentence. – JBL Mar 19 '15 at 11:42
  • 2
    I would recommend shared_ptr, return a weak_ptr from a public method. Another reason to only return weak_ptr is that it'll help avoid cycles that may result in objects staying alive even though you think they've been destroyed. – Robinson Mar 19 '15 at 12:17