Consider the following classes
class Base {
public:
virtual void do_stuff() = 0;
};
class Derived : public Base {
public
virtual void do_stuff() { std::cout << "I'm useful"; };
};
Now let's say I want to have another class responsible for owning objects of Base
's derived types and iterate through them calling their do_stuff()
method. It looks like this, but I don't know what T
should be declared as
class Owner {
public:
void do_all_stuff() {
//iterate through all items and call do_stuff() on them
}
void add_item(T item) {
items.push_back(item);
}
vector<T> items;
}
I see a few possibilities:
T
can't be Base
, since I would only be able to add objects of concrete type Base
, so that's out of the question.
T
can be Base*
or Base&
, but now I need to trust the caller of add_item()
to pass me a pointer or a reference to an object that will still exist when I retrieve it from items
. I can't delete
the elements in Owner
's destructor, since I don't know that they were dynamically allocated. However, they should be delete
'd if they were, which leaves me with ambiguous ownership.
T
can be Base*
or Base&
and I add a Base* create_item<DerivedT>() { return new DerivedT; }
method to Owner
. This way, I know the pointer will remain valid and I own it, but I'm unable to call a non-default constructor on DerivedT
. Also, Owner
becomes responsible for instantiating objects as well. I also have to delete every item in Owner
's destructor, although that's not much of an issue.
Basically, I'd like to be able to do something similar to:
Owner owner;
void add_one() {
Derived d;
owner.add_item(d);
}
void ready() {
owner.do_all_stuff();
}
void main() {
for(int i = 0; i < 10; ++i) {
add_one();
}
ready();
}
I'm sure there's something related to move semantics in there (I could move the objects passed to add_items()
to own them) but I still can't figure out how my collection would be declared.
What is the C++ idiom for this sort of polymorphic ownership (particularly with STL containers)?
The word polymorphism means having many forms. Typically, polymorphism occurs when there is a hierarchy of classes and they are related by inheritance. C++ polymorphism means that a call to a member function will cause a different function to be executed depending on the type of object that invokes the function.
Polymorphism in C++ means, the same entity (function or object) behaves differently in different scenarios. Consider this example: The “ +” operator in c++ can perform two specific functions at two different scenarios i.e when the “+” operator is used in numbers, it performs addition.
Polymorphism in C++ allows us to reuse code by creating one function that's usable for multiple uses. We can also make operators polymorphic and use them to add not only numbers but also combine strings. This saves time and allows for a more streamlined program.
Polymorphism is the ability of an object to take on many forms. The most common use of polymorphism in OOP occurs when a parent class reference is used to refer to a child class object. Any Java object that can pass more than one IS-A test is considered to be polymorphic.
Polymorphic objects have to be handled by pointer or reference. Since their lifetime is probably not bound to a particular scope they will also probably have dynamic storage duration, which means you should use a smart pointer.
Smart pointers such as std::shared_ptr
and std::unique_ptr
work just fine in the standard collection types.
std::vector<std::unique_ptr<Base>>
Using this in Owner
looks like:
class Owner {
public:
void do_all_stuff() {
//iterate through all items and call do_stuff() on them
}
void add_item(std::unique_ptr<Base> item) {
items.push_back(std::move(item));
}
vector<std::unique_ptr<Base>> items;
}
The argument type to add_item
identifies the ownership policy required for adding an item, and requires the user to go out of their way to screw it up. For example they can't accidentally pass a raw pointer with some implicit, incompatible ownership semantics because unique_ptr
has an explicit constructor.
unique_ptr
will also take care of deleting the objects owned by Owner
. Although you do need to ensure that Base
has a virtual destructor. With your current definition you will get undefined behavior. Polymorphic objects should pretty much always have a virtual destructor.
Assuming from your context that Owner
is the sole owner of the contained objects,T
should be unique_ptr<Base>
(where unique_ptr
comes from boost or std depending on your C++11 availability). This properly recognizes that it's solely owned by the container and additionally shows the ownership transferral semantics in your add_item
call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With