19

考虑以下类

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*or Base&,但现在我需要相信 的调用者会add_item()向我传递一个指针或对一个对象的引用,当我从items. 我不能deleteOwner的析构函数中使用元素,因为我不知道它们是动态分配的。然而,如果他们是的话,他们应该是delete'd',这让我有模糊的所有权。

T可以是Base*orBase&和我添加一个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()以拥有它们),但我仍然无法弄清楚我的集合将如何声明。

这种多态所有权(尤其是 STL 容器)的 C++ 习语是什么?

4

4 回答 4

20

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

诸如std::shared_ptr和之类的智能指针std::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具有虚拟析构函数。使用您当前的定义,您将获得未定义的行为。多态对象应该几乎总是有一个虚拟析构函数。

于 2013-07-08T17:43:00.667 回答
2

假设您的上下文Owner是所包含对象的唯一所有者,T应该是unique_ptr<Base>unique_ptr取决于您的 C++11 可用性,来自 boost 或 std 的位置)。这正确地识别出它完全由容器拥有,并在您的add_item调用中另外显示所有权转移语义。

于 2013-07-08T17:47:50.053 回答
2

其他值得考虑的替代方案是使用boost::ptr_container,甚至更好的是,为多态类型使用类似adobe::poly或的库boost::type_erasure,以利用基于值的运行时多态性——避免对指针、继承等的需要。

于 2013-07-10T21:19:18.313 回答
1

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

于 2013-07-08T17:05:09.557 回答