【问题标题】:Shared pointers not increasing the use_count共享指针不增加 use_count
【发布时间】:2018-06-21 14:05:42
【问题描述】:

我正在尝试了解如何在 C++ 中使用 std::shared_ptr。但这很令人困惑,我不明白如何创建指向同一个对象的多个共享指针。甚至文档和在线资料也不是很清楚。

以下是我编写的一小段代码,旨在尝试了解std::shared_ptr 的行为:

#include <iostream>
#include <memory>
using namespace std;

class Node
{
public:
    int key;
    Node()
    {
        key = 0;
    }
    Node(int k)
    {
        key = k;
    }
};

int main()
{
    Node node = Node(10);

    shared_ptr<Node> ptr1((shared_ptr<Node>)&node);
    cout << "Use Count: " << ptr1.use_count() << endl;

    // shared_ptr<Node> ptr2=make_shared<Node>(node);//This doesn't increase use_count
    shared_ptr<Node> ptr2((shared_ptr<Node>)&node);
    cout << "Use Count: " << ptr2.use_count() << endl;

    if (ptr1 == ptr2)
        cout << "ptr1 & ptr2 point to the same object!" << endl;

    if (ptr1.get() == ptr2.get())
        cout << "ptr1 & ptr2 point to the same address!" << endl;

    cout << "ptr1: " << ptr1 << "  "
         << "ptr2: " << ptr2 << endl;
    return 0;
}

根据我得到的输出,ptr1ptr2 都指向同一个 Node 对象,但 use_count 对它们来说都是 1。即使使用std::make_shared 也不起作用,程序在退出前崩溃。

你能告诉我我做错了什么吗?以及如何创建多个指向同一个对象的shared_ptr(s)。

【问题讨论】:

  • (shared_ptr&lt;Node&gt;)&amp;node 在多个级别上都是错误的。
  • 您通过说ptr2 = ptr1; 来增加使用次数,并且您不得获取局部变量的地址,因为它会在程序结束时尝试删除,并可怕地崩溃。
  • 除了@VTT 的注释之外,您还创建了两个不同的指针,它们是共享指针。不是共享一个指针。
  • 顺便说一句,这个参考站点有a good example(但线程可能使它更复杂一些)。
  • 虽然在创建ptr1 & ptr2 时会创建并传递共享指针,但这些都是临时的,会被销毁或得到moved

标签: c++ c++11 shared-ptr reference-counting make-shared


【解决方案1】:

单独创建shared_ptrs(包括使用make_shared)时,use_count 不会增加,它们根本不共享。创建的shared_ptr对其他shared_ptrs和被管理的指针一无所知,即使指针可能恰好相同(注意可能导致多次破坏)。

你需要告诉哪个shared_ptrs 应该共享;当您从另一个 shared_ptr 创建 shared_ptr 时,use_count 会增加。例如

shared_ptr<Node> ptr1 = make_shared<Node>(10);
cout<<"Use Count: "<<ptr1.use_count()<<endl; // 1

shared_ptr<Node> ptr2(ptr1);
cout<<"Use Count: "<<ptr2.use_count()<<endl; // 2
cout<<"Use Count: "<<ptr1.use_count()<<endl; // 2

顺便说一句:正如 cmets 所建议的那样,创建一个 shared_ptr 管理指向 &amp;node 的指针是很危险的,该指针指向在堆栈上分配的对象。以下代码可能与您的意图非常吻合。

Node* node = new Node(10);

shared_ptr<Node> ptr1(node);
cout<<"Use Count: "<<ptr1.use_count()<<endl; // 1

shared_ptr<Node> ptr2(ptr1);
cout<<"Use Count: "<<ptr2.use_count()<<endl; // 2
cout<<"Use Count: "<<ptr1.use_count()<<endl; // 2

【讨论】:

  • 但是如果我想在同一个对象上创建两个 shared_ptr(s) 怎么办?例如在代码中给定的 node 对象上?
  • @DeeJay 答案已修改。
  • ptr2 和 ptr1 是两个不同的 shared_ptrs 指向同一个 Node 对象。我认为您可能对代码在做什么有某种根本的困惑?
  • @songyuanyao 对于ptr2,如果我这样做:shared_ptrptr2(node);不是也可以吗?
  • @songyuanyao 为什么ptr1和ptr2都指向同一个地址,use_count()还是1?
【解决方案2】:

shared_ptr&lt;&gt;s 也拥有他们所指向的对象。这意味着您不能从堆栈对象创建 shared_ptr 。要创建 shared_ptr,请使用 newmake_shared

shared_ptr<Node> ptr1(new Node(42));
shared_ptr<Node> ptr2 = make_shared<Node>();
shared_ptr<Node> ptr3 = make_shared<Node>(99);

这些都是独立的对象,并且都有use_count()==1。 如果您想复制它们,只需这样做:

shared_ptr<Node> copy1 = ptr1;

现在ptr1copy1 拥有use_count()==2

【讨论】:

  • 那么如果我在堆上创建一个对象,我是否能够在它上面创建shared_ptr(s)。
    例如
    Node node=new Node(10);
    shared_ptr ptr1=make_shared(node);
    shared_ptr ptr2=make_shared(node);
    这会不会增加两个ptr1的use_count() & ptr2?
  • 如果你不自己删除它:是的。节点* temp = 新节点(1); share_ptr ptr1(temp);
  • 但是:不要为同一个堆对象创建 2 个 shared_ptrs!
  • 如果我为同一个堆对象创建两个 shared_ptr(s) 会出现什么问题?
  • @DeeJay,第一个正确删除对象,第二个将转到 SEGFAULT
【解决方案3】:

有很多误解,但我会尝试解释一下: 在 c++ 中,对象要么存储在 stack 上,要么存储在 heap 上。在 stack 上创建的对象将被销毁,并且一旦您离开声明它们的范围,它们的内存就会被释放。这会为您完全自动发生。例如:

int bla()
{
    int a = 1;
    int b = 2;
    int result = a+b;
    return result;
}

在这个例子中,3 个intobjects 将在程序进入函数bla时被创建,并在函数相应返回时被销毁。

那么你就有了。您可以通过new 在堆上创建对象。这个对象的寿命超过了它们被创建的范围,但你必须记住通过delete 销毁对象。因为这是内存泄漏的常见陷阱和来源,标准库提供了帮助类(例如您已经找到的shared_ptr)来克服这个问题。这个想法是,shared_ptrobject 将负责删除给定的对象(它必须在堆上(!)(是的,你可以通过自定义删除器绕过它,但这比这更高级一点解释))一旦 shared_ptr 本身被破坏。顾名思义,shared_ptr 的意图是共享。要共享,您只需复制 shared_ptr。 shared_ptr 实现有一个自定义复制构造函数和复制赋值运算符,它们将通过增加使用计数和复制当前处理的对象的地址来处理这个问题。这是 shared_ptr 对象了解是否有其他实例管理同一对象的唯一方法。所以要增加使用次数,您必须复制shared_ptr

现在让我解释一下出了什么问题:

  1. 您的初始 Node node = Node(10); 是在 堆栈 上创建的(如上面的整数)。因此无需手动管理生命周期。你创建了一个shared_ptr,管理这个节点的地址。这是一个错误。一旦您离开范围,shared_ptr 和节点对象本身(通过 stack 上的自动性)将删除节点对象。这很糟糕。

  2. 假设错误 1 ​​不存在。所以我们有第二个错误: 您正在创建第二个,单独的shared_ptr,而不是从第一个复制。现在两个现有的shared_ptr 都不知道彼此,他们每个人都会尝试删除节点对象。所以我们再次进行了双重删除。

假设您希望您的 Node 对象真正由 shared_ptr 管理,您必须从一个 shared_ptr 开始,然后尽可能多地复制它来共享它。要创建第一个,您有 2 种可能性:

std::shared_ptr<int> mysharedpointer (new int(ANYNUMBER));
auto mysharedpointer = std::make_shared<int>(ANYNUMBER);

通常您更喜欢第二个,因为在函数调用上下文中使用时,它具有轻微的性能优势和更高的安全性。

如果您现在想要第二个shared_ptr,与第一个共享所有权,您可以通过复制第一个指针来实现:

auto mysecondsharedpointer = mysharedpointer;

瞧,您有一个与共享所有权关联的副本。

(这有点简化,我确定我错过了一些东西。请随意完成答案)

【讨论】:

  • 如果我将 shared_ptr 传递给函数怎么办? normal 方式会起作用吗? '无效(shared_ptra,shared_ptrb){}'。 shared_ptr 的 a & b 会增加 use_count 吗?
  • 鉴于您的函数签名 (void (shared_ptr&lt;int&gt; a, shared_ptr&lt;int&gt; b)),shared_ptr&lt;int&gt;s 均按值获取。这意味着它们将从给它的参数中构造(复制或移动)。所以是的,a 和 b 都将使用计数增加一。如果你将相同的shared_ptr 传递给 a 和 b,那么当然 use_count 会增加 2(因为两者共享同一个对象的所有权)
  • 当函数返回时,use_count 会减少吗?如果我不想增加函数中的 use_count 怎么办?只使用普通指针?
  • 是的。如果函数返回,函数的作用域退出并且你的shared_ptr&lt;int&gt; ashared_ptr&lt;int&gt; b 被销毁。在 shared_ptr&lt;&gt; 的析构函数中,相应的 use_count 将递减。
  • 如果你想避免增加use_count,你可以: 1. 在调用你的函数时移动一个shared_ptr。这样调用者将失去所有权,并且该对象在函数之后被删除 2. 将函数签名更改为引用 shared_ptr。您应该避免这种情况 3. 更改您的函数签名以引用 shared_ptr 指向的对象(在本例中为 int):void (int&amp; a, int&amp; b)。在调用端,您必须取消对 shared_pointer 4. 的引用。与 3 相同,但采用指向 int (int*) 的非拥有原始指针。使用 if 参数可以为 null
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多