理解c ++共享指针

时间:2016-05-09 15:38:16

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

嗨我正在制作自己的引用计数智能指针,但在我开始之前有两个我不太了解的概念。

  1. 我知道当创建共享指针指向一个对象时,我必须为一个结构/类分配内存,该结构/类将包含诸如引用计数(最初为一个)之类的信息,也可能是一个互斥量用于递增和减少。当我使用时,假设=运算符使另一个共享指针也指向该对象,我也将指向此结构/类的指针传递给该新指针,这样我就可以增加计数。我的问题是,如果我使第三个共享指针指向此对象(不使用复制构造函数或=运算符),那么此指针不会知道结构,因此如果我有引用计数为1然后删除指针,计数将达到0并且对象将被销毁,实际上,此对象还有另外两个指针?

  2. 如果共享指针的引用计数为1,然后创建多个线程,如果一个线程结束/销毁它,那么可能仍在运行的其他线程会发生什么?

5 个答案:

答案 0 :(得分:4)

  

当我创建一个共享指针指向一个对象时,我必须为一个结构/类分配内存,该结构/类将包含诸如引用计数(最初为一个)之类的信息,也可能是一个互斥量以及递增和递减。

是的,你需要一个柜台 只有在计划多线程时才需要互斥锁。我会集中精力让计数工作首先担心锁定后记。

  

当我使用时,假设=运算符使另一个共享指针也指向此对象,我也将指向此结构/类的指针传递给该新指针,这样我就可以增加计数。

关于共享指针的观点是它们拥有指针的所有权。一旦创建了共享指针,就不应该有指向同一对象的RAW指针的实例。因此,当您复制或分配时,您通过共享指针完成所有操作。

  

我的问题是,如果我将第三个共享指针指向此对象(不使用复制构造函数或=运算符),则此指针将不知道结构,因此引用计数为1。 / p>

你的假设是正确的。这就是为什么当你创建共享指针时,你不应该保留指针的副本。这是引入std::make_shared()的另一个原因,它分配内存并立即将其包装在智能指针中,因此没有RAW指针返回给用户代码。

  

如果我然后删除指针,计数将达到0并且对象将被销毁,实际上,此对象还有另外两个指针?

这正是问题所在。这就是为什么你不应该创建或传递RAW指针到已经被管理的对象。

  

如果共享指针的引用计数为1,然后创建多个线程,如果一个线程结束/销毁它,那么可能仍在运行的其他线程会发生什么?

如果您的引用计数正在运行并且您只有管理RAW指针的共享指针,它应该按预期工作。但如果你在一个线程中删除它,它将在所有线程中被销毁。

答案 1 :(得分:1)

  1. 答案是肯定的。如果从指向对象的普通指针创建共享指针,当对象已经被另一个共享指针拥有时,当一个引用计数达到0并且剩余的共享指针将悬空时,该对象将被销毁 - 它们会一直指向对象曾经存在的地方。

    这就是智能指针的用户必须永远不会从本身没有所有权的代码授予指针所有权的原因。

  2. 有一个竞争条件,检查和减少参考计数器。如果共享指针可以在多个线程中使用,则必须跨线程同步对引用计数器的访问。

答案 2 :(得分:1)

  

我的问题是,如果我将第三个共享指针指向此对象(不使用复制构造函数或=运算符)

由于您绕过了复制共享指针的机制,因此这将是未定义的行为。使用复制构造函数或赋值运算符。

  

如果共享指针的引用计数为1,然后创建多个线程,如果一个线程结束/销毁它,那么可能仍在运行的其他线程会发生什么?

这意味着在析构函数完成后使用共享指针,这是未定义的行为。确保这些线程拥有自己的共享指针副本以避免这种情况,或确保指针在其他线程使用时不会被销毁。

答案 3 :(得分:1)

  

我的问题是,如果我将第三个共享指针指向此对象(不使用复制构造函数或=运算符),则此指针将不知道结构,因此引用计数为1,如果然后我删除指针,计数将达到0并且对象将被销毁,实际上,此对象还有另外两个指针?

你真的无能为力。如果你有一个指针并将它分配给两个单独的共享指针,那么你会得到一个双重删除。

int * foo = new int(10); // set value to 10
{
    std::shared_ptr<int> a(foo);
    std::shared_ptr<int> b(foo)
} // uh-oh.  we call delete twice

无法将指针标记为拥有。编写代码的人不应该这样做。我们解决这类问题的方法是直接在构造函数中创建指针,这样就无法将其提供给另一个实例

std::shared_ptr<int> a(new int(10));

或者您可以使用std::make_shared

auto b = std::make_shared<int>(10);

现在我们不再需要担心将原始指针指向另一个共享指针并将其从我们下面删除。

  

如果共享指针的引用计数为1,然后创建多个线程,如果一个线程结束/销毁它,那么可能仍在运行的其他线程会发生什么?

如果将共享指针传递给每个线程,则所有线程共享所有权。在所有线程结束且原始共享指针超出范围之前,不会删除指针。

答案 4 :(得分:-1)

在典型的实现中,如果您为对象创建shared_pointer,并在不引用已创建的shared_pointer的情况下为同一对象创建第二个shared_pointer,则它们都会有引用计数为1,当超出范围时删除对象,当另一个尝试删除对象时导致未定义的行为。

这是STL具有std::make_sharedstd::make_unique等工厂对象的主要原因之一,因为确保原始对象指针永远不会由程序员管理,有助于防止这类错误

当然,更智能的实现可能会尝试在静态级别上跟踪已创建的所有指针。维护,并可能尝试记住对象。但这有其自身的风险和问题,所以我不会建议。

关于多线程问题:它取决于。如果所有线程都基于原始shared_pointer对象创建shared_pointer,并且您正确使用mutex来维护引用计数,那么它们都应该正确递增引用计数而不问题,并且该对象不应仅因为单个线程停止使用而被删除。