John
John

Reputation: 540

Container class with vector of smart pointers to abstract base class

I am having some problems creating my container class.

This is the behaviour that I want:

class AbstractBase
{
    public:
        AbstractBase() { }
        virtual std::string toString()=0;
};


class Derived : public AbstractBase
{
    public:
        Derived() { }
        std::string toString() { return "Just an example " + std::to_string( _value ); }
    private:
        int _value;
};

void Container::print()
{
    for( auto i : bunch_of_different_derived_objects_that_i_own )
        std::cout << i.toString() << std::endl;
}

What is the best way to create a container class that "owns" a bunch of different derived objects by only keeping pointers to the AbstractBase class?

Is it possible to make the container class own different derived objects without knowing their definition?

Using

std::vector<std::shared_ptr<AbstractBase>> 

caused weird behaviour and

std::vector<std::unique_ptr<AbstractBase>> 

was impossible to iterate.

Is it possible to achieve what I want?

Upvotes: 2

Views: 4119

Answers (2)

David G
David G

Reputation: 96810

The problem seems to be the construction of your for() loop:

for (auto i : bunch_of_different_derived_objects_that_i_own)
{
    i.toString();
}

auto deduces the type to be std::shared_ptr. You can't use the . operator to call a member function because that will look for member functions that belong specifically to the std::shared_ptr class. Use the indirection operator -> so you can access member functions of the type pointed to:

i->toString();

Using std::unique_ptr won't work either because auto type deduction rules are equivalent to a function template that deduces its type by value. Binding to a parameter by value will cause either a move or copy depending on the value-category of the argument. Since the elements of the vector are lvalues, a copy will be attempted which can't be done because std::unique_ptrs can only be moved (hence the name unique).

Use a reference to prevent a copy:

for (auto& i : bunch_of_different_derived_objects_that_i_own)

You also need a virtual destructor for your base class. If you don't provide one only the base class subobject of your elements will be called, not the derived part too.

Upvotes: 0

Piotr Skotnicki
Piotr Skotnicki

Reputation: 48447

1. Declare virtual destructor

class AbstractBase
{
public:
    AbstractBase() { }
    virtual ~AbstractBase() = default; // this is (defaulted) virtual destructor
    virtual std::string toString() = 0;
};

class Derived : public AbstractBase
{
public:
    Derived() { }
    virtual std::string toString() override { return "Just an example " + std::to_string( _value ); }
private:
    int _value;
};

2. Store shared_ptr's in vector

std::vector<std::shared_ptr<AbstractBase>> v = { std::make_shared<Derived>() };

3. Do your stuff and not care about destructors any more.

for (auto i : v)
{
    std::cout << i->toString() << std::endl;
}

Upvotes: 5

Related Questions