Shared_ptr 如果涉及“this”,则无法在构造函数中初始化

Shared_ptr cannot be initialized in constructor if `this` involved

我有一些代码结构如下:

#include <memory>
#include <vector>

using namespace std;
struct demo {
    vector<shared_ptr<demo>> foo{shared_ptr<demo>(this)};
    void append(){
        foo.push_back(make_shared<demo>());
    }
};

demo dummy(/* accept some arguments */){
    demo a{};
    a.append();
    return a;
}

int main()
{
    demo bar=dummy();
    return 0;
}

虚拟函数只是根据它在新构造的演示实例上的参数和 return 它做一些处理。 (我很确定上面的内容足以暴露问题)

它编译得很好没有错误,但是它不会自行终止。 (我在 Win7 上使用 MinGW 的 g++)。

然后我尝试变通。我添加了一个复制构造函数(以及一个显式默认构造函数),然后使用 new 只在主函数中保留指向演示的指针。所以代码将是这样的:

...
struct demo {
    vector<shared_ptr<demo>> foo {shared_ptr<demo>(this)};
    void append() {
        foo.push_back(make_shared<demo>());
    }
    demo(){
    };
    demo(const demo& instance): foo(instance.foo) {
    }
};
...
int main()
{
//    demo bar=dummy();
    demo* bar=new demo(dummy());
    delete bar;
    return 0;
}

然后就成功了。但我仍然想知道这些线背后的机制。您能否解释一下 new 构造的变量和通常声明的变量之间的区别?

编辑: 所以最后有没有 push_back this 作为构造函数中的结构成员进入向量? (之所以如此,是因为我的vector的其余部分存储了除第一个之外的所有shared_ptr,换句话说,即使vector被pop_back编辑为空,它仍然可以return this)

要么在堆栈上创建 a,要么让 shared_ptr 管理它的生命周期,但你不能两者都做。堆栈消失时,堆栈上的对象也随之消失。

这是一个更简单的示例,它重现了您的问题:

struct demo {
    shared_ptr<demo> foo{this};
};

int main()
{
    demo bar;
}

问题是您在成员中存储了指向 this 的共享指针。因此,当一个 demo 实例被销毁时,共享指针会递减,当共享指针递减为零时,它将再次删除该对象,从而导致未定义的行为。

即使那不是问题,仍然存在 bar 是一个自动对象的问题。而且您可能没有共享指向自动对象的指针。但是 bar 确实创建了一个指向自身的共享指针。

demo* bar=new demo(dummy());
delete bar;

这样更好,因为现在对象(由 bar 指向)具有动态存储,所以您可能有一个指向它的共享指针。您当然必须修复 dummy 以不创建自动 demo 实例。

此外,您可能不会显式 delete 一个 demo 的实例,因为内部有一个共享指向它自己。 "correct" 处理 demo 实例的方法是删除共享指针,然后由它负责删除:

demo* bar=new demo(dummy());
bar->foo.clear();      // this is for your vector version
//bar->foo = nullptr;  // this is for my minimized example above
// object pointed by bar no longer exists
// (unless the shared pointer was copied and exists elsewhere)

不过,我觉得这个设计很混乱,不推荐。您应该重新考虑 为什么 将共享指针存储到 this.