Skip to content

Polymorphic object container

Exception safety 问题 以及 解决方案

需要遵循 : CppCoreGuidelines R.11: Avoid calling new and delete explicitly ,在下面文章中对此进行了详细说明:

1、sean-parent Value Semantics and Concept-based Polymorphism

在其中进行了非常深入的探讨

2、Can I have polymorphic containers with value semantics in C++? # A

One reasonable solution is to store container safe smart pointers. I normally use boost::shared_ptr which is safe to store in a container. Note that std::auto_ptr is not.

vector<shared_ptr<Parent>> vec;
vec.push_back(shared_ptr<Parent>(new Child()));

shared_ptr uses reference counting so it will not delete the underlying instance until all references are removed.

3、What's the preferred C++ idiom to own a collection of polymorphic objects? # A

能否使用value semantic来进行解决?

这在 Polymorphic-containers-with-value-semantic 中进行了讨论。

Smart pointer + container的方式

1、能够解决exception safety问题

2、需要注意的是,Smart pointer + container在实现上是依赖于move semantic的:

a、performance principle: prefer move over copy

b、如何评价 C++11 的右值引用(Rvalue reference)特性? - Tinro的回答 - 知乎

在这篇文章中,有着非常好的解答

stackoverflow What's the preferred C++ idiom to own a collection of polymorphic objects?

A

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.

A

Other alternatives worth considering are to use boost::ptr_container, or even better, use a library like adobe::poly or boost::type_erasure for your polymorphic types, to exploit value-based run-time polymorphism—avoids the need for pointers, inheritance, etc.