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

问题描述

以下面的例子

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();

a 中的局部变量 initB() 超出了函数完成执行的范围。但是有一个共享指针指向它。当 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_ptra。由于我们没有定义复制赋值运算符/构造函数,编译器会自动生成一个,这就是为什么我们只有一个 A's CTOR。所以即使我无法想象你会在哪个地方这样做,它也会有一个有效的 A 实例。