1

我认为这是一个简单的问题,但我一直在盯着一些复杂的遗留代码,我再也看不到森林的树木了。该应用程序将运行数天,然后在退出时失败(当然对于较短的工作它不会失败!)我怀疑是 SEGV。

我用下面的一些伪代码简化了这个案例(希望我做对了)。

用人类的话来说:我有一个类 XYZ,它有很多东西,包括一个指向简单类 ABC 的指针向量(假设它很简单)。这些指针在 XYZ 的析构函数中被删除;这就是析构函数所做的一切。

然后我有一个简单的基类 TheBase,它只有两个简单的虚拟方法,没有析构函数。

最后,我有三个类,Tom 和 Dick(从 TheBase 派生)和 Harry(不是从 TheBase 派生的)。这三个类都是从对 XYZ 对象的 const 引用构造的;所以他们有一个对 XYZ 对象的 const 引用。它们也没有析构函数。

总的来说, boost::shared_ptr 是为 Tom、Dick 和 Harry 对象中的每一个定义的。然后创建一个 XYZ 对象。接下来,XYZ 对象作为对 Tom、Dick 和 Harry 对象的 const 引用传递。在那之后,发生了一大堆事情并且主要退出。

那么当所有这些事情都超出范围时会发生什么?特别是 XYZ 对象?这会被正确处理吗?似乎某些内容将被多次删除。

// A simple class (let's assume it is!)
class ABC
{
  // unimportant stuff.
}

// class XYZ has an array of ABC objects. All the destructor does is delete     those objects.
class XZY 
{
  public:
    XYZ(vector<string> v1,
        vector<string> v2,
        vector<string> v3 );
  virtual ~XYZ(){
          for ( i = 0; i < n, i++ ){
              delete my_abcs[i];
          }
  } 
private:
    vector <ABC*> my_abcs
  // lots of other methods & members
}

// Simple base class with only 2 simple virtual methods
class TheBase
{   
  public:
        virtual void minor_func1();
        virtual void minor_func2();
}

// A class derived from base class. Constructs with a const reference to an XYZ class.
class Tom:TheBase
{
    public:
        Tom( const XYZ & xyz )

    private:
        const XYZ & my_xyz; 
  // lots of other methods & members
}
Tom::Tom(const XYZ & xyz):my_xyz(xyz){
  ...
}

// Another class derived from base class. Constructs with a const reference to an XYZ class.
class Dick:TheBase
{
    public:
        Dick( const XYZ & xyz )

    private:
        const XYZ & my_xyz; 
    // lots of other methods & members
}
Dick::Dick(const XYZ & xyz):my_xyz(xyz){
...
}

// A class NOT derived from base class but still constructs with a const reference to an XYZ class.
class Harry:TheBase
{
  public:
        Harry( const XYZ & xyz )

    private:
        const XYZ & my_xyz; 
    // lots of other methods & members
}
Harry::Harry(const XYZ & xyz):my_xyz(xyz){
...
}

main (...){
  ...

  boost::shared_ptr <Tom> a_tom;
  boost::shared_ptr <Dick> a_dick;
  boost::shared_ptr <Harry> a_harry;
  ...

  XYZ a_xyz( ... );

  a_tom.reset( new Tom( a_xyz) );
  a_dick.reset( new Dick( a_xyz) );
  a_harry.reset( new harry( a_xyz) );

  ...
}
4

2 回答 2

3

shared_ptr当最后一个shared_ptr指向它们的对象被破坏时,由 a 管理的对象将被破坏。局部变量在超出范围时被破坏,其创建顺序与它们的创建顺序相反。在你的情况下,如果没有shared_ptr静态生命周期(或伪静态生命周期;即在一个动态分配的对象中,直到离开 main 之后才会被破坏,如果有的话),那么a_xyz 将被破坏,然后指向的三个对象shared_ptr. _ 如果这些对象没有 a_xyz在它们的析构函数中使用对的引用(并且shared_ptr没有被复制到它们将超过 main 的地方),那么应该没有问题。

于 2013-03-26T17:32:11.143 回答
1

我有一个类 XYZ,它有很多东西,包括一个指向简单类 ABC 的指针向量(假设它很简单)。这些指针在 XYZ 的析构函数中被删除;这就是析构函数所做的一切。

顺便说一句,这个问题的答案是std::vector<std::unique_ptr<ABC>>,它封装了在类型中拥有相关指针的事实std::vector,并且无需您手动销毁它们。它还会阻止意外复制:如果您曾经实现过非平凡的析构函数,则需要实现或阻止复制构造和复制分配(规则 3)。

使用 a std::vector<std::unique_ptr<ABC>>,它只能被移动,所以移动赋值和移动构造是不阻塞的,而复制构造和复制赋值是阻塞的。

std::unique_ptr<T>有少量的开销。

.get()唯一的成本是当您需要访问底层时的一堆调用T*,这基本上是零运行时成本。

总的来说, boost::shared_ptr 是为 Tom、Dick 和 Harry 对象中的每一个定义的。然后创建一个 XYZ 对象。接下来,XYZ 对象作为对 Tom、Dick 和 Harry 对象的 const 引用传递。在那之后,发生了一大堆事情并且主要退出。

C++ 中相同范围内的对象按照声明它们的相反顺序被销毁。所以汤姆、迪克和哈利的寿命将比 XYZ 对象更长。

最后我有三个类,Tom 和 Dick(从 TheBase 派生)和 Harry(不是从 TheBase 派生的)。这三个类都是从对 XYZ 对象的 const 引用构造的。所以他们有一个对 XYZ 对象的 const 引用。它们也没有析构函数。

引用(在此上下文中)对所引用事物的生命周期没有影响。引用不是智能指针,它们是不安全且未经检查的别名。通常,当您创建对某事物的引用时,您的工作是确保该对象的持续时间比对该对象的引用长。

如果 afterXYZ超出任何范围TomDickHarry访问它们的引用- XYZ,那么您调用了未定义的行为。如果你没有,你就没有。

即使你没有,依赖它也可能是一个坏习惯,因为你的代码将非常脆弱。

(要清楚:当我说“在此上下文中”时,我的意思是。有一个上下文,其中引用生命周期会改变相关对象的生命周期:当引用直接从临时(匿名)对象构造时,该匿名对象的生命周期将延长到引用的生命周期。但是请注意,以这种方式间接构造的引用不具有此属性 - 因此A& a = A();将延长匿名对象的生命周期A,而B& b = a;不会,并且A& get_A() { return A(); }; A& a = get_A();确实不起作用,但A get_A() { return A(); }; A& a = get_A();确实会延长寿命(最后一个不确定)。)

于 2013-03-26T17:59:44.420 回答