【问题标题】:std::vector of objects / pointers / smart pointers to pass objects (buss error: 10)?std::vector of objects / pointers / smart pointers to pass objects(总线错误:10)?
【发布时间】:2013-03-31 16:29:39
【问题描述】:

我想请教一个一般性的建议。下面的代码完全编译并大致代表了我处理的代码的结构。 简而言之,我想将一系列派生自基类(Class1)的对象和一些其他参数从一个地方传递到另一个地方。更准确地说,实现父类的不同子类,收集这些子类的实例并传递参数进行处理。

问题是,您建议使用对象向量还是指针向量?我不介意从C++11 (std::unique_ptr, std::shared_ptr) 中寻找一些新的东西,如果这是出于某种原因更好/更安全/更少的内存泄漏/等等。如果有人可以为这种情况提供有关容器的建议和/或使用C++11 提供示例,我将不胜感激。

p/s/hereUncleBens 说,如果/当抛出异常时,使用指针可能会导致内存泄漏。所以也许我真的应该使用 智能指针 来完成这项任务?这看起来如何?

p/p/s/ 很有趣,当我尝试使用来自 std::vector< Container<d>*> / std::vector< Container<d>> 的那些 Class2 对象时,现实生活中的例子给了我 Bus error: 10。但是,我无法在简单的情况下重现错误...

#include <string>
#include <iostream>
#include <vector>


template<int dim>
class Class1 {
   public:

     Class1() {};
    ~Class1() {}; 

};

template<int dim>
class Class2 : public Class1<dim>
{
   public:
     Class2() : 
       Class1<dim>() {}; 

 };

template <int dim>
class Container
{
  public:
     Container( Class1<dim> & f, int param1) : c1(f), param_(param1) {}

  Class1<dim>  & c1;
      int param_;
 };

 static const int d = 2; 

 int main() 
 {
    int p = 1;
    Class2<d> c2;
    std::vector< Container<d> *> p_list;
    std::vector< Container<d> > list;
    {
      p_list.push_back ( new Container<d> ( c2,p ) ); 
    }
    std::cout<<"from pointers: "<<p_list[0]->param_<<std::endl;

    {
      list.push_back( Container<d> ( c2,p ) );
    }
    std::cout<<"from objects: "<<list[0].param_<<std::endl;
 }

【问题讨论】:

    标签: c++ oop c++11


    【解决方案1】:

    首先,Class1的析构函数应该标记为virtual,否则当派生类(例如Class2)的实例被销毁时,它的析构函数将不会被正确调用。

    至于你的问题,使用对象容器的后果是:

    • 容器可能需要复制对象,因此您需要确保有一个复制构造函数(示例中的类获取编译器生成的默认构造函数)。复制对象可能会对性能产生影响,您需要正确定义副本的语义(是深还是浅,即您是创建 class1 对象的新副本,还是仅复制引用)。
    • 您不能有任何多态性,因此您不能将 Container 子类化,然后将基类和子类的实例放在同一个容器中。
    • 根据容器,您的对象将在内存中是连续的(向量就是这种情况),这可以带来性能优势。

    如果你使用原始指针的容器,那么容器只需要复制指针(更快),你可以添加包含类型的派生实例。缺点是您必须在使用后手动销毁对象,并且正如您所提到的,这很容易泄漏内存。

    shared_ptr 与原始指针有类似的优点/缺点,但关键的好处是 shared_ptr 会在不再引用它时为您销毁对象,这使得您不太可能引入内存泄漏(但它仍然当涉及异常时并非不可能这样做)。

    鉴于您将这些对象移交给进一步处理,我会说基于 shared_ptr 的方法是一个不错的选择。在原始指针之上使用共享ptr的后果是:

    • 可能存在性能开销,因为为了线程安全,大多数 shared_ptr 实现需要检查/设置锁(这可能涉及对操作系统的系统调用)。
    • 您仍然可以通过在对象之间引入循环引用来泄漏内存。
    • 您必须使用实现 C++11 的编译器或使用外部库(大多数人使用 boost)。

    使用 shared_ptrs 的示例看起来像这样(未经测试)。

    #include <string>
    #include <iostream>
    #include <vector>
    
    template<int dim>
    class Class1 {
       public:
           Class1() {};
           virtual ~Class1() {}; 
    
    };
    
    template<int dim>
    class Class2 : public Class1<dim>
    {
        public:
            Class2() : 
            Class1<dim>() {}; 
    
    };
    
    template <int dim>
    class Container
    {
        public:
           Container( boost::shared_ptr<Class1<dim>> f, int param1) : c1(f), param_(param1) {}
    
           boost::shared_ptr<Class1<dim>> c1;
           int param_;
    };
    
    static const int d = 2;
    
    int main() 
    {
        int p = 1;
        boost::shared_ptr<Class1<d>> c2 = boost::make_shared<Class2<d>>();
    
        std::vector<boost::shared_ptr<Container<d>>> list;
        list.push_back(boost::make_shared<Container<d>>(c2,p));
    
        std::cout << "from objects: " << list[0]->param_ << std::endl;
    }
    

    总而言之,如果接收容器的代码没有在任何地方存储对它们的引用,并且您不需要多态性,那么对象容器可能就可以了。如果接收容器的代码需要将它们存储在某个地方,和/或您想要多态容器,请使用共享 ptrs。

    【讨论】:

    • 非常感谢您的详细回答。将修改我的真实代码以实现共享指针...
    • 注意到您没有在答案中定义 list。我猜是std::vector,或者可能更好boost::shared_ptr&lt; std::vector&lt;....&gt;&gt;
    • 糟糕,是的,我错过了。我已经更新了添加列表声明的答案,它是共享 ptrs 的 std::vector。
    猜你喜欢
    • 1970-01-01
    • 2012-04-07
    • 1970-01-01
    • 2016-05-01
    • 2012-07-05
    • 1970-01-01
    • 1970-01-01
    • 2015-02-13
    • 2022-05-09
    相关资源
    最近更新 更多