昨天我遇到了一个类,它会在析构函数中调用一个方法,最终将unique_ptr返回给this
。这显然是一个问题,因为这会导致双重删除,所以我将.release()
添加到该方法调用中,一切都很好。然而,这是一个奇怪的情况,我想要有一些关闭,这是一件好事。
举个例子,我把这种情况简化为其本质:
#include <memory>
class B;
class A {
public:
A(std::unique_ptr<B> b);
std::unique_ptr<B> removeB();
private:
std::unique_ptr<B> b_;
};
class B {
public:
B();
~B();
void setA(A *a);
private:
A *a_;
};
A::A(std::unique_ptr<B> b)
: b_(std::move(b))
{
b_->setA(this);
}
B::B()
: a_(nullptr)
{
}
B::~B()
{
if (a_)
a_->removeB().release();
}
void B::setA(A *a)
{
a_ = a;
}
std::unique_ptr<B> A::removeB()
{
return std::move(b_);
}
int main(int argc, char *argv[])
{
A a(std::make_unique<B>());
}
我认为这基本上就像这样做:
class B;
class A {
public:
A(B* b);
~A();
B *removeB();
private:
B *b_;
};
class B {
public:
B();
~B();
void setA(A *a);
private:
A *a_;
};
A::A(B *b)
: b_(b)
{
b_->setA(this);
}
A::~A()
{
delete b_;
}
B::B()
: a_(nullptr)
{
}
B::~B()
{
if (a_)
a_->removeB();
}
void B::setA(A *a)
{
a_ = a;
}
B *A::removeB()
{
B *result = b_;
b_ = nullptr;
return result;
}
int main(int argc, char *argv[])
{
A a(new B());
}
它工作正常,我没有任何静态分析仪或valgrind抱怨它,但我想得到某种确认,这是一件好事。
答案 0 :(得分:3)
你在这里发生了一些非常纠结的事情。首先,您有class A
,unique_ptr
到B
。这表明所有权(或父/子)关系:A
拥有B
。 B
反过来有一个指向A
的简单指针,而这个指针未在构造函数中设置,而是通过setA
调用,这似乎证实了这一点:A
得到一个孩子B
,然后使用一个简单的指针调用setA
,告诉B
其父级。
那么B
何时被摧毁?答案:在A
破坏期间,通过unique_ptr
的析构函数。当B
调用发生这种情况时,为什么A
会尝试将自己拉出removeB
?这似乎是混乱的代码,只是不需要在那里。可能正确的事情就是在B
的析构函数中什么都不做,然后一切都会被正确销毁。
所以我的建议是为B
设置一个空的析构函数。
回答你的具体问题:removeB().release()
是否有任何问题:我有一种感觉,是的。 B
析构函数在A
销毁期间被调用,因此在unique_ptr
成员A
被销毁期间发生。因此,removeB
调用将部分销毁的对象传递给unique_ptr
的移动构造函数。如果这是未定义的行为,我不会感到惊讶。