【问题标题】:Is it safe to return a member variable which is a shared_ptr返回一个 shared_ptr 的成员变量是否安全
【发布时间】:2016-02-09 08:32:52
【问题描述】:

假设我有以下代码,这是我的问题的简化示例:

#include <string>
#include <iostream>
#include <memory>


class A{
  public:
  std::string name() { return "class A"; }
};

class B{
  public:
  B(){
    m_a = std::make_shared<A>();
  }

  std::shared_ptr<A> get_a() { return m_a; }

  private:
  std::shared_ptr<A> m_a;
};

std::shared_ptr<A> foo()
{
  B b;
  return b.get_a();
}

int main()
{
  auto a = foo();
  auto name = a->name();
  std::cout << name;
  return 1;
}

我想知道这样做是否安全? “b”是 B 的一个实例,将在函数 foo 结束时释放。主函数中的“a”是 B::m_a 的 shared_ptr。在“b”释放后使用“a”是否安全?

非常感谢!

【问题讨论】:

  • 请发布可编译的代码。 IE。没有语法错误,带有正确的main#includes。
  • 我已经阅读了这两个问题但是我决定发布一个新问题,因为我想确保这样做是安全的(或不安全的)事件持有 shared_ptr 的“父”类为成员变量超出范围。
  • 如果这样不安全,那么我不确定我是否首先理解 shared_ptr 的意义。
  • Christian 和 decltype_auto,你是对的,我只是编辑了代码使其可编译。

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


【解决方案1】:

那个foo

shared_ptr<A> foo()
{
  B b;
  return b.get_a();
}

不返回对 b 成员的引用,而是成员的独立副本(副本,如果不是 RV 优化);和shared_ptr一个

auto a = foo();

将 A 堆实例从销毁中保存到作用域结束。

因此,我认为这是安全的,尽管 B 是创作者,但毕竟只是他们创建和发布的许多共享资产的一个用户。

换句话说 - 最后一个关灯,而不是打开它的那个。

live at Coliru

【讨论】:

    【解决方案2】:

    是的,它是安全的。如果这不安全,那么std::shared_ptr 将毫无用处。

    “b”是 B 的一个实例,将在函数 foo 结束时释放。

    是的,但它的std::shared_ptr&lt;A&gt; 是在此之前复制的。

    让我们看看函数的最后一行到底做了什么:

    return b.get_a();
    

    get_a() 产生一个std::shared_ptr&lt;A&gt;,它被复制到一个临时对象。然后再次复制这个临时的std::shared_ptr&lt;A&gt; 对象,成为main 中调用的结果。

    (由于返回值优化,实际副本甚至可能被省略,但这不会改变任何东西。如果有的话,它会使安全性更容易理解。)支持>

    只有那么b销毁了,但是里面的std::shared_ptr&lt;A&gt;到那个时候已经被复制了。

    b 中的std::shared_ptr&lt;A&gt; 实例也被销毁,但A 对象的内存没有释放。 这就是std::shared_ptr 的重点——它知道它被复制的频率,并且只有在最后一个副本被销毁时才释放动态分配的内存。

    主函数中的“a”是B::m_a的一个shared_ptr。

    没有。这是std::shared_ptr&lt;A&gt;,仅此而已。和原来的B::m_a已经没有关系了。

    “b”释放后使用“a”安全吗?

    是的,因为ab 之间没有持久的关系。


    基本上,您质疑的是 C++ 的一个基本特性,即从函数返回值的安全性。返回 std::shared_ptr 在安全方面与返回 std::stringint 没有什么不同。当您从函数中按值返回时,总是会发生这种情况:要返回的值被复制,原始值被销毁,副本继续存在。

    【讨论】:

      【解决方案3】:

      很安全。

      shared_ptr 在堆上创建一个跟踪变量。这使得对象B 更加复杂,因为即使它是在堆栈上创建的,它也会从堆中分配一个A。可能会影响性能。

      当跟踪数据的所有用户都停止访问它时,shared_ptr&lt;&gt; 将被删除。

      【讨论】:

        猜你喜欢
        • 2020-03-09
        • 2020-06-22
        • 1970-01-01
        • 2020-08-26
        • 1970-01-01
        • 2014-01-19
        • 1970-01-01
        • 2016-08-05
        • 1970-01-01
        相关资源
        最近更新 更多