拥有多态对象集合的首选C ++习惯用法是什么?

时间:2013-07-08 16:55:57

标签: c++ c++11 polymorphism

考虑以下课程

class Base {
public:
    virtual void do_stuff() = 0;
};

class Derived : public Base {
public
    virtual void do_stuff() { std::cout << "I'm useful"; };
};

现在假设我想让另一个类负责拥有Base派生类型的对象,并通过它们调用它们的do_stuff()方法进行迭代。它看起来像这样,但我不知道应该将T声明为

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;
}

我看到了一些可能性:

T不能是Base,因为我只能添加具体类型Base的对象,所以这是不可能的。

T可以是Base*Base&,但现在我需要相信add_item()的调用者向我传递一个指针或对仍然仍然存在的对象的引用当我从items检索它时存在。我不能delete Owner的析构函数中的元素,因为我不知道它们是动态分配的。但是,如果它们是delete,它们应该是T dd,这使我拥有模糊的所有权。

Base*可以是Base&Base* create_item<DerivedT>() { return new DerivedT; },我会向Owner添加DerivedT方法。这样,我知道指针将保持有效并且我拥有它,但是我无法在Owner上调用非默认构造函数。此外,Owner也负责实例化对象。我还必须删除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(); } 的析构函数中的每个项目,尽管这不是问题。

基本上,我希望能够做类似的事情:

add_items()

我确信在那里有一些与移动语义相关的东西(我可以移动传递给{{1}}的对象来拥有它们)但是我仍然无法弄清楚我的集合将如何被声明。

这种多态所有权的C ++习惯用法是什么(特别是对于STL容器)?

4 个答案:

答案 0 :(得分:18)

多态对象必须由指针或引用处理。由于它们的生命周期可能不受特定范围的限制,因此它们也可能具有动态存储持续时间,这意味着您应该使用智能指针。

std::shared_ptrstd::unique_ptr等智能指针在标准集合类型中运行良好。

std::vector<std::unique_ptr<Base>>

Owner中使用此代码如下:

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;
}

add_item的参数类型标识了添加项目所需的所有权政策,并要求用户尽力将其搞砸。例如,由于unique_ptr具有显式构造函数,因此无法通过一些隐式的,不兼容的所有权语义意外传递原始指针。

unique_ptr还会处理删除Owner拥有的对象的问题。虽然您确实需要确保Base具有虚拟析构函数。根据您当前的定义,您将获得未定义的行为。多态对象应该总是有一个虚拟析构函数。

答案 1 :(得分:2)

根据您的上下文假设Owner是所包含对象的唯一所有者,T应为unique_ptr<Base>(其中unique_ptr来自boost或std取决于您的C ++ 11可用性)。这可以正确识别它仅由容器拥有,并在add_item调用中显示所有权转移语义。

答案 2 :(得分:2)

值得考虑的其他替代方法是使用boost::ptr_container,或者甚至更好地使用像adobe::polyboost::type_erasure这样的库来处理多态类型,以利用基于值的运行时多态性 - 避免需要指针,继承等。

答案 3 :(得分:1)

实际上,您无法在STL中存储引用,只能存储指针或实际值。 所以T是基础* 尝试编译器抱怨的其他事情。