【问题标题】:C++: Vector of objects vs. vector of pointers to new objects?C ++:对象向量与指向新对象的指针向量?
【发布时间】:2011-02-11 05:27:08
【问题描述】:

我正在寻求通过编写示例软件渲染器来提高我的 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());

或者...

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 方法。假设我希望派生类能够拥有自己的 draw() 版本。那么列表是否需要指针才能使用方法选择器(->)?

【问题讨论】:

  • thnx 已编辑以免分散注意力

标签: c++ pointers vector


【解决方案1】:

由于您明确表示要改进 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_ptrs 存在,它们可能比容器寿命更长。

使用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 中标准化,现在可以在没有 Boost 的情况下用作 std::reference_wrapper

正如Maciej Hs 答案中所指出的,您的第一种方法导致object slicing。一般来说,您在使用容器时可能需要查看iterators

【讨论】:

  • shared_ptrvector 中的性能可能非常糟糕,具体取决于您执行的操作。对于通常的目的,我推荐ptr_vector
  • 在您的 ptr_vector 示例中,首先使用“boost:ptr_vector”,然后在 typedef 中使用“std::ptr_vector”。我试图编辑帖子,但显然编辑的长度必须至少为 6 个字符,而“boost”只有 5 个字符。
  • @bsegraves:谢谢,我修好了。
【解决方案2】:

你不会用这段代码得到你想要的

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

即将发生的事情称为对象切片。你会得到一个ObjectBaseClass的向量。

要使多态起作用,您必须使用某种指针。在 boost 或其他库中可能有一些智能指针或引用可以使用,并使代码比第二种建议的解决方案更安全。

【讨论】:

    【解决方案3】:

    至于你的第一个问题,通常更喜欢使用自动分配的对象而不是动态分配的对象(换句话说,不是来存储指针)只要对于​​有问题的类型,复制- 建造和分配是可能的,而且不会过于昂贵。

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

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

    【讨论】:

    • 上例中动态创建对象和自动分配对象有时间差吗?哪种解决方案更适合优化?
    【解决方案4】:

    嗯,这取决于你想用你的向量做什么。

    如果你不使用指针,那么它是你传入的对象的副本,它被放在向量上。如果它是一个简单的对象,并且/或者您不想为它们跟踪存储而烦恼,那么这可能正是您想要的。如果它是复杂的,或者构造和销毁非常耗时,您可能更愿意每次只执行一次并将指针传递给向量。

    【讨论】:

      猜你喜欢
      • 2011-10-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-19
      • 1970-01-01
      • 2010-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多