C++: Vector of objects vs. vector of pointers to new objects?
我想通过编写一个示例软件渲染器来提高我的C++技能。它获取由三维空间中的点组成的对象,并将其映射到二维视区,并为视图中的每个点绘制不同大小的圆。哪个更好:
1 2 3 4 5 6 7 | class World{ vector<ObjectBaseClass> object_list; public: void generate(){ object_list.clear(); object_list.push_back(DerivedClass1()); object_list.push_back(DerivedClass2()); |
或者…
1 2 3 4 5 6 7 | class World{ vector<ObjectBaseClass*> object_list; public: void generate(){ object_list.clear(); object_list.push_back(new DerivedClass1()); object_list.push_back(new DerivedClass2()); |
??在第二个例子中使用指针来创建新的对象会击败使用向量的点,因为向量会在第一个例子中自动调用派生类析构函数,而不是在第二个例子中?使用向量时是否需要指向新对象的指针,因为只要使用向量的访问方法,向量本身就处理内存管理?现在假设我有另一种方法:
1 2 3 4 5 | void drawfrom(Viewport& view){ for (unsigned int i=0;i<object_list.size();++i){ object_list.at(i).draw(view); } } |
调用时,将为世界列表中的每个对象运行draw方法。假设我希望派生类能够拥有自己的draw()版本。那么为了使用方法选择器(->)列表需要指针吗?
由于您明确地声明希望改进C++,所以我建议您使用Boost启动。这可以通过三种不同的方式帮助您解决问题:
使用使用
1 | std::vector< boost::shared_ptr< ObjectBase > > object_list; |
像这样使用:
1 2 3 4 | typedef std::vector< boost::shared_ptr< ObjectBase > >::iterator ObjectIterator; for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ ) (*it)->draw(view); |
这将给您提供多态性,并像使用指针的正常向量一样使用它,但
关于C++ 11的注释:在C++ 11中,EDOCX1×2成为EDCOX1×7的标准的一部分,因此这种方法不再需要Boost。但是,除非你真的需要共享所有权,建议使用EDCOX1 OR 8,这是在C++ 11中新引入的。
使用使用
1 | boost::ptr_vector< ObjectBase > object_list; |
像这样使用:
1 2 3 4 | typedef boost::ptr_vector< ObjectBase >::iterator ObjectIterator; for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ ) (*it)->draw(view); |
这将再次被用作指针的法向量,但这次
使用EDOCX1[13]您可以这样声明:
1 | std::vector< boost::reference_wrapper< ObjectBase > > object_list; |
然后这样使用:
1 2 3 4 5 | typedef std::vector< boost::reference_wrapper< ObjectBase > >::iterator ObjectIterator; for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ ) it->draw(view); |
注意,您不必像上面的方法那样首先取消对迭代器的引用。但是,只有在其他地方管理对象的生命周期,并且保证比
关于C++ 11的注释:EDCOX1,13,也在C++ 11中被标准化,现在可以用作EDCOX1,17,没有升压。
正如Maciejhs回答中指出的,第一种方法会导致对象切片。通常,您可能希望在使用容器时查看迭代器。
你不能用这个代码得到你想要的
1 2 3 4 5 6 7 | class World{ vector<ObjectBaseClass> object_list; public: void generate(){ object_list.clear(); object_list.push_back(DerivedClass1()); object_list.push_back(DerivedClass2()); |
将要发生的事情称为对象切片。您将得到一个ObjectBaseClass的向量。
要使多态性工作,必须使用某种指针。Boost或其他库中可能有一些智能指针或引用,可以使用它们,使代码比第二个建议的解决方案更安全。
对于第一个问题,通常最好使用自动分配的对象,而不是动态分配的对象(换句话说,不存储指针),只要对于所讨论的类型,复制构造和分配是可能的,并且不会造成过高的代价。
如果对象不能被复制或分配,那么你就不能直接把它们放到
对于第二个问题,是的,这是存储指针的另一个有效原因。动态调度(虚拟方法调用)只在指针和引用上工作(并且不能在
这取决于你想用向量做什么。
如果您不使用指针,那么它是您传入的对象的一个副本,该对象被放到向量上。如果它是一个简单的对象,并且/或者您不想费心跟踪它们的存储,那么这可能正是您想要的。如果构建和销毁是一件复杂的事情,或者非常耗时,那么您可能更喜欢只执行一次这项工作,并将指针传递到向量中。