考虑以下课程
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容器)?
答案 0 :(得分:18)
多态对象必须由指针或引用处理。由于它们的生命周期可能不受特定范围的限制,因此它们也可能具有动态存储持续时间,这意味着您应该使用智能指针。
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
具有虚拟析构函数。根据您当前的定义,您将获得未定义的行为。多态对象应该总是有一个虚拟析构函数。
答案 1 :(得分:2)
根据您的上下文假设Owner
是所包含对象的唯一所有者,T
应为unique_ptr<Base>
(其中unique_ptr
来自boost或std取决于您的C ++ 11可用性)。这可以正确识别它仅由容器拥有,并在add_item
调用中显示所有权转移语义。
答案 2 :(得分:2)
值得考虑的其他替代方法是使用boost::ptr_container
,或者甚至更好地使用像adobe::poly
或boost::type_erasure
这样的库来处理多态类型,以利用基于值的运行时多态性 - 避免需要指针,继承等。
答案 3 :(得分:1)
实际上,您无法在STL中存储引用,只能存储指针或实际值。 所以T是基础* 尝试编译器抱怨的其他事情。