C ++:对象的矢量与指向新对象的向量的矢量?
我正在寻求通过编写示例软件渲染器来提高我的C ++技能。 它将对象组成一个三维空间中的点,并将它们映射到一个二维视口,并为每个视点绘制不同大小的圆。 哪个更好:
class World{
vector<ObjectBaseClass> object_list;
public:
void generate(){
object_list.clear();
object_list.push_back(DerivedClass1());
object_list.push_back(DerivedClass2());
要么...
class World{
vector<ObjectBaseClass*> object_list;
public:
void generate(){
object_list.clear();
object_list.push_back(new DerivedClass1());
object_list.push_back(new DerivedClass2());
?? 在第二个例子中使用指针来创建新的对象会影响使用向量的点,因为向量在第一个例子中会自动调用DerivedClass析构函数,但不会在第二个例子中调用? 指向使用向量时必需的新对象的指针,因为只要您使用访问方法,它们就自己处理内存管理? 现在让我们说我在世界上有另一种方法:
void drawfrom(Viewport& view){
for (unsigned int i=0;i<object_list.size();++i){
object_list.at(i).draw(view);
}
}
当被调用时,这将为世界列表中的每个对象运行绘制方法。 比方说,我希望派生类能够拥有自己的draw()版本。 为了使用方法选择器( - >),列表是否需要指针?
你不会得到你想要的代码
class World{
vector<ObjectBaseClass> object_list;
public:
void generate(){
object_list.clear();
object_list.push_back(DerivedClass1());
object_list.push_back(DerivedClass2());
将要发生的事情称为对象切片。 你将得到一个ObjectBaseClass的向量。
为了使多态性工作你必须使用某种类型的指针。 在boost或其他库中可能会有一些智能指针或引用可用,并使代码比第二个建议的解决方案更安全。
既然你明确表示你想改进你的C ++,我会建议你开始使用Boost。 这可以以三种不同的方式帮助你解决问题:
使用shared_ptr
使用shared_ptr
可以像这样声明你的向量:
std::vector< boost::shared_ptr< ObjectBase > > object_list;
并像这样使用它:
typedef std::vector< boost::shared_ptr< ObjectBase > >::iterator ObjectIterator;
for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
(*it)->draw(view);
这会给你多态性,并且会像使用普通的指针向量一样使用它,但是shared_ptr
会为你做内存管理,当最后一个shared_ptr
引用它时被销毁。
关于C ++ 11的注意事项:在C ++ 11中, shared_ptr
成为std::shared_ptr
标准的一部分,因此此方法不再需要Boost。 但是,除非您真的需要共享所有权,否则建议您使用std::unique_ptr
,它是C ++ 11中新引入的。
使用ptr_vector
使用ptr_vector
你可以这样做:
boost::ptr_vector< ObjectBase > object_list;
并像这样使用它:
typedef boost::ptr_vector< ObjectBase >::iterator ObjectIterator;
for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
(*it)->draw(view);
这将再次像普通的指针向量一样使用,但这次ptr_vector
管理对象的生命周期。 与第一种方法的不同之处在于,这里的对象在矢量被销毁时会被销毁,而在它们上面,如果其他shared_ptr
引用它们,它们可能会比容器活得更长。
使用reference_wrapper
使用reference_wrapper
你可以这样声明它:
std::vector< boost::reference_wrapper< ObjectBase > > object_list;
然后像这样使用它:
typedef std::vector< boost::reference_wrapper< ObjectBase > >::iterator
ObjectIterator;
for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
it->draw(view);
请注意,您不必像上述方法那样先取消迭代器的引用。 然而,这只有在你的对象的生命周期在其他地方被管理并且保证比vector
长度更长时才有效。
关于C ++ 11的注意事项: reference_wrapper
也已在C ++ 11中标准化,现在可用作std::reference_wrapper
而不使用Boost。
正如Maciej Hs回答指出的,你的第一种方法会导致对象切片。 一般来说,您可能想在使用容器时查看迭代器。
至于你的第一个问题,通常最好是使用自动分配的对象而不是动态分配的对象(换句话说,不要存储指针),只要对于所讨论的类型来说,复制构造和分配是可能的并且不是非常昂贵。
如果不能复制或分配对象,那么无论如何都不能直接将它们放到std::vector
,所以问题是没有意义的。 如果复制和/或分配操作昂贵(例如对象存储大量数据),那么出于效率原因,您可能需要存储指针。 否则,通常最好不要因为你提到的原因而存储指针(自动释放)
至于你的第二个问题,是的,这是存储指针的另一个有效理由。 动态分派(虚拟方法调用)仅适用于指针和引用(并且不能将引用存储在std::vector
)。 如果您需要将多个多态类型的对象存储在同一个向量中,则必须存储指针以避免切片。
上一篇: C++: Vector of objects vs. vector of pointers to new objects?
下一篇: Why use pointers?