共享指针指向的对象的生命周期

Lifetime of object pointed to by shared pointer

举个例子

struct A {
   int x = 0;
};

struct B {
   std::shared_ptr<A> mA;
   
   void setA(std::shared_ptr<A> a) {
     mA = a;
   }
};

struct C {

  B initB() {
    A a;
    
    A *aPtr = &a;
    B b;
    b.setA(std::make_shared<A>(aPtr));
    return b;
 } 
};

现在在 main() 方法中

C c;
B b = c.initB();

initB() 中的局部变量 a 超出范围一个函数完成执行。但是有一个指向它的共享指针。当 a 超出范围时,共享指针指向的对象是否会被删除? 最后 *(b.mA) 会给出 A 的有效实例吗?

首先,这无法编译。

    B initB() {
        A a;

        A* aPtr = &a;
        B b;
        b.setA(std::make_shared<A>(/*aPtr*/a););
        return b;
    }

您必须传递要共享的实际对象,而不是指向它的指针。现在,为了找出这个问题的答案,我们可以为每个函数编写通知程序,并添加构造函数和析构函数,这样我们就可以看到发生了什么。

#include <memory>

#include <iostream>

struct A {
    int x = 0;
    A() {
        std::cout << "A's CTOR" << std::endl;
    }
    ~A() {
        std::cout << "A's DTOR" << std::endl;
    }
};

struct B {

    B() {
        std::cout << "B's CTOR" << std::endl;
    }

    ~B() {
        std::cout << "B's DTOR" << std::endl;
    }

    std::shared_ptr<A> mA;

    void setA(std::shared_ptr<A> a) {
        std::cout << "Entering setA()" << std::endl;
        mA = a;
        std::cout << "Exiting setA()" << std::endl;
    }
};

struct C {

    C() {
        std::cout << "C's CTOR" << std::endl;
    }

    ~C() {
        std::cout << "C's DTOR" << std::endl;
    }

    B initB() {
        std::cout << "Entering initB()" << std::endl;
        A a;

        A* aPtr = &a;
        B b;
        b.setA(std::make_shared<A>(/*aPtr*/a));
        std::cout << "Exiting initB()" << std::endl;
        return b;
    }
};

int main() {
    std::cout << "Entering Main" << std::endl;
    C c;
    B b = c.initB();
    std::cout << "Exiting Main" << std::endl;
    return 0;
}

输出:

Entering Main
C's CTOR
Entering initB()
A's CTOR
B's CTOR
Entering setA()
Exiting setA()
Exiting initB()
B's DTOR
A's DTOR
Exiting Main
B's DTOR
A's DTOR
C's DTOR

有意思,你明白发生了什么事吗?有 2 个 A's DTORstd::make_share<A>(a) 实际上复制了 a,然后将 shared_ptr 复制到 a。由于我们没有定义复制赋值 operator/constructor,编译器会自动生成一个,这就是为什么我们只有一个 A's CTOR。因此,即使我无法想象您会在哪个地方执行此操作,它也会有一个有效的 A 实例。