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
.
我有一些代码结构如下:
#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
.