C++:对象的向量与指向新对象的指针的向量?

met*_*ics 27 c++ pointers vector

我正在寻求通过编写示例软件渲染器来提高我的C++技能.它采用由3d空间中的点组成的对象,并将它们映射到2d视口,并为视图中的每个点绘制不同大小的圆.哪个更好:

class World{
    vector<ObjectBaseClass> object_list;
public:
    void generate(){
        object_list.clear();
        object_list.push_back(DerivedClass1());
        object_list.push_back(DerivedClass2());
Run Code Online (Sandbox Code Playgroud)

要么...

class World{
    vector<ObjectBaseClass*> object_list;
public:
    void generate(){
        object_list.clear();
        object_list.push_back(new DerivedClass1());
        object_list.push_back(new DerivedClass2());
Run Code Online (Sandbox Code Playgroud)

?? 在第二个例子中使用指针来创建新对象会破坏使用向量的点,因为向量会在第一个示例中自动调用DerivedClass析构函数而不是在第二个示例中调用DerivedClass析构函数吗?在使用向量时是否需要指向新对象的指针,因为只要您使用其访问方法,它们本身就会处理内存管理?现在让我们说我在世界上有另一种方法:

void drawfrom(Viewport& view){
    for (unsigned int i=0;i<object_list.size();++i){
        object_list.at(i).draw(view);
    }
}
Run Code Online (Sandbox Code Playgroud)

调用时,这将为世界列表中的每个对象运行draw方法.假设我希望派生类能够拥有自己的draw()版本.为了使用方法选择器( - >),列表是否需要指针?

Bjö*_*lex 27

由于您明确声明要改进C++,我建议您开始使用Boost.这可以通过三种不同的方式帮助您解决问题:

用一个 shared_ptr

使用a shared_ptr可以像这样声明你的向量:

std::vector< boost::shared_ptr< ObjectBase > > object_list;
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

typedef std::vector< boost::shared_ptr< ObjectBase > >::iterator ObjectIterator;

for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
    (*it)->draw(view);
Run Code Online (Sandbox Code Playgroud)

这会给你多态性,就像它是一个普通的指针向量一样,但它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;
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

typedef boost::ptr_vector< ObjectBase >::iterator ObjectIterator;

for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
    (*it)->draw(view);
Run Code Online (Sandbox Code Playgroud)

这将再次像普通的指针向量一样使用,但这次ptr_vector管理对象的生命周期.与第一种方法的不同之处在于,当向量被破坏时,你的对象会被破坏,而上面它们可能比容器更长寿,如果shared_ptr存在其他引用它们的话.

用一个 reference_wrapper

使用reference_wrapper你会声明它像这样:

std::vector< boost::reference_wrapper< ObjectBase > > object_list;
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

typedef std::vector< boost::reference_wrapper< ObjectBase > >::iterator 
    ObjectIterator;

for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
    it->draw(view);
Run Code Online (Sandbox Code Playgroud)

请注意,您不必像上述方法那样首先取消引用迭代器.但是,只有在对象的生命周期在其他地方进行管理并且保证长于对象的生命周期时,这才有效vector.

关于C++ 11的注意事项: reference_wrapper在C++ 11中已经标准化,现在可以在std::reference_wrapper没有Boost的情况下使用.

正如Maciej H的回答所指出的,你的第一种方法会导致对象切片.通常,您可能希望在使用容器时查看迭代器.


Mac*_*ehl 19

你不会得到你想要的代码

class World{
    vector<ObjectBaseClass> object_list;
public:
    void generate(){
        object_list.clear();
        object_list.push_back(DerivedClass1());
        object_list.push_back(DerivedClass2());
Run Code Online (Sandbox Code Playgroud)

将要发生的事情称为对象切片.您将获得ObjectBaseClass的向量.

使多态性工作你必须使用某种指针.在boost或其他库中可能有一些智能指针或引用可以使用,并使代码比第二个提出的解决方案更安全.


Tyl*_*nry 13

至于你的第一个问题,通常首选使用自动分配的对象而不是动态分配的对象(换句话说,存储指针),只要对于有问题的类型,复制构造和分配是可能的并且不会过于昂贵.

如果无法复制或分配对象,那么std::vector无论如何都不能将它们直接放入,所以这个问题没有实际意义.如果复制和/或赋值操作很昂贵(例如,对象存储大量数据),那么出于效率原因,您可能希望存储指针.否则,通常最好不要存储指针,因为你提到的原因(自动释放)

至于你的第二个问题,是的,这是存储指针的另一个有效理由.动态分派(虚方法调用)仅适用于指针和引用(并且您不能在a中存储引用std::vector).如果需要在同一向量中存储多个多态类型的对象,则必须存储指针以避免切片.