【问题标题】:Why instantiating a shared_ptr calling destructor?为什么要实例化一个 shared_ptr 调用析构函数?
【发布时间】:2020-05-06 01:28:32
【问题描述】:

有人能解释一下为什么类bar的析构函数在同类型对象被初始化的那一行被调用吗?

    #include <memory>
    #include <iostream>

    using namespace std;

    class bar
    {
      public:
        bar() {}

        ~bar() { std::cout << "destructor called " << std::endl; }
    };

    class foo
    {
      public:
        foo(std::shared_ptr<bar> barP) {}
    };


    int main() {

        std::shared_ptr<foo> f;
        std::cout << "before init " << std::endl;
        f = std::shared_ptr<foo>(new foo(std::shared_ptr<bar>(new bar())));
        std::cout << "after init"  << std::endl;
    }

输出:

before init 
destructor called 
after init

【问题讨论】:

  • foo 的构造函数获得了barP 的所有权,但不对其做任何事情。在f=... 行结束后,您希望哪个shared_ptr&lt;&gt;bar 上保持引用计数?

标签: c++ c++11 shared-ptr smart-pointers


【解决方案1】:

此声明:

f = std::shared_ptr<foo>(new foo(std::shared_ptr<bar>(new bar())));
                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

构造一个临时的shared_ptr,它在语句末尾超出范围。到那时,shared_ptr 消失,bar 也随之消失(因为没有 shared_ptr 的副本仍然存在)。

但是如果你把foo改成这样:

class foo
{
  public:
    foo(std::shared_ptr<bar> barP) { m_bar = barP; }
    std::shared_ptr<bar> m_bar;
};

那么你得到的输出可能就是你所期望的,因为foo 维护了shared_ptr 的副本,直到它(foo)超出范围并且该副本保持bar 活着:

before init 
after init
destructor called

Live demo

【讨论】:

    【解决方案2】:

    这是因为 bar 实例的生命周期仅与 foo 构造函数的持续时间一样长。所以它被构造出来,传递给shared_ptr,然后传递给foo构造函数。一旦该构造函数完成(即使在同一行),表达式本身也完成了,因此 shared_ptr 完成并销毁。

    main 的末尾,就在cout 之前,您仍然有一个shared_ptrf 中的foo,但是未命名的shared_ptr 到您的bar 对象已经“超出范围” ”。

    【讨论】:

      【解决方案3】:

      首先,你不应该用这种方式实例化shared_ptr,使用make_shared来实例化shared_ptr。请检查修改后的代码 -

      #include <memory>
      #include <iostream>
      
      using namespace std;
      
      class bar
      {
        public:
          bar() {}
      
          ~bar() { std::cout << "destructor called " << std::endl; }
      };
      
      class foo
      {
        public:
          foo(std::shared_ptr<bar> barP) {}
      };
      
      
      int main() {
      
          std::shared_ptr<foo> f;
          std::cout << "before init " << std::endl;
          std::shared_ptr<bar> b = std::make_shared<bar>();
          f = std::make_shared<foo>(b);
          std::cout << "after init"  << std::endl;
      }
      

      上述程序的输出-

      before init 
      after init
      destructor called 
      

      通常,您的代码发生的情况是您将new bar 传递给foo 的构造函数。并且没有人持有bar 共享指针的所有权。因此,引用计数变为 0 并被删除,因此正在调用析构函数。

      进一步阅读make_shared vs new

      【讨论】:

        猜你喜欢
        • 2018-05-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-06
        • 1970-01-01
        • 2013-02-14
        • 1970-01-01
        • 2019-08-20
        相关资源
        最近更新 更多