Reputation: 540
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
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_ptr
s 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
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