C++ 理解c++;共享指针

C++ 理解c++;共享指针,c++,pointers,shared-ptr,smart-pointers,C++,Pointers,Shared Ptr,Smart Pointers,嗨,我正在制作我自己的引用计数智能指针,但在我开始之前,有两个概念我不太明白 我发现,当创建一个指向对象的共享指针时,我必须为一个结构/类分配内存,该结构/类将包含诸如引用计数(最初为一个)以及用于递增和递减的互斥量等信息。当我使用=操作符使另一个共享指针也指向这个对象时,我还将把指向这个结构/类的指针传递给那个新指针,这样我就可以增加计数。我的问题是,如果我让第三个共享指针指向这个对象(不使用copy构造函数或=运算符),那么这个指针将不知道这个结构,因此引用计数将为1,如果我随后删除指针,计

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

  • 我发现,当创建一个指向对象的共享指针时,我必须为一个结构/类分配内存,该结构/类将包含诸如引用计数(最初为一个)以及用于递增和递减的互斥量等信息。当我使用=操作符使另一个共享指针也指向这个对象时,我还将把指向这个结构/类的指针传递给那个新指针,这样我就可以增加计数。我的问题是,如果我让第三个共享指针指向这个对象(不使用copy构造函数或=运算符),那么这个指针将不知道这个结构,因此引用计数将为1,如果我随后删除指针,计数将达到0,实际上,这个对象将被销毁,此对象还有两个其他指针

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

  • 答案是肯定的。如果从指向某个对象的普通指针创建共享指针,而该对象已由另一个共享指针拥有,则当一个引用计数达到0时,该对象将被销毁,其余的共享指针将悬空-它们一直指向该对象曾经存在的位置

    这就是为什么智能指针的用户决不能从本身没有所有权的代码中授予指针的所有权

  • 存在与检查和递减参考计数器有关的争用条件。如果共享指针可以在多个线程中使用,那么必须跨线程同步对引用计数器的访问

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

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

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

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

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

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

    当我使用=操作符使另一个共享指针也指向这个对象时,我还将把指向这个结构/类的指针传递给那个新指针,这样我就可以增加计数

    关于共享指针的一点是,它们拥有指针的所有权。创建共享指针后,不应存在指向同一对象的原始指针实例。因此,当您复制或分配时,您是通过共享指针来完成的

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

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

    如果我随后删除指针,计数将达到0,并且该对象将被销毁,而实际上,该对象还有两个其他指针

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

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


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

    在典型的实现中,如果您创建一个指向某个对象的
    共享\u指针
    ,并创建指向同一对象的第二个
    共享\u指针
    ,而不引用已经创建的
    共享\u指针
    ,它们的引用计数都将为1,当其中一个超出范围时删除该对象,当另一个尝试删除该对象时导致未定义的行为

    这是STL具有工厂对象(如
    std::make_shared
    std::make_unique
    )的主要原因之一,因为确保程序员从不管理原始对象指针有助于防止此类错误

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

    至于多线程问题:这取决于。如果所有线程都基于原始的
    共享指针
    对象创建
    共享指针
    ,并且您正在正确使用
    互斥锁
    来维护引用计数,那么它们都应该正确地增加引用计数,而不会出现问题,并且对象
    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));
    
    auto b = std::make_shared<int>(10);