C++ 在移动唯一的ptr时,是否可以移动它';它在自己的析构函数中?

C++ 在移动唯一的ptr时,是否可以移动它';它在自己的析构函数中?,c++,c++11,C++,C++11,昨天我遇到一个类,该类在其析构函数中调用一个方法,该方法最终返回一个唯一的\u ptr到this。这显然是一个问题,因为这会导致双重删除,所以我在该方法调用中添加了.release(),一切又恢复正常。这是一个奇怪的情况,虽然,我想有一些结束,这是一个好的事情做 举个例子,我将这种情况归结为其本质: #include <memory> class B; class A { public: A(std::unique_ptr<B> b); std::uniqu

昨天我遇到一个类,该类在其析构函数中调用一个方法,该方法最终返回一个唯一的\u 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>());
}

它工作正常,我没有任何静态分析仪或valgrind对此抱怨,但我想确认一下,这是一件可以做的事情。

这里发生了一些非常复杂的事情。首先,您有
A类
,它有一个与A
B
唯一的
ptr
。这表明了所有权(或父/子)关系:
A
拥有A
B
B
又有一个指向
a
的简单指针,该指针不是在构造函数中设置的,而是通过
setA
调用来设置的,这一事实似乎证实了这一点:
a
获取一个子
B
,然后用一个简单的指针对其调用
setA
,告诉
B

那么
B
什么时候被销毁?答:在
A
的销毁过程中,通过
unique\ptr
的析构函数。当
removeB
调用发生这种情况时,为什么
B
会尝试将自己从
A
上拉下来?这看起来像是混乱的代码,根本不需要存在。正确的方法可能是在
B
的析构函数中什么都不做,然后所有的东西都会被正确地销毁

因此,我的建议是为
B
使用一个空的析构函数


回答您的具体问题:这个
removeB()。在
A
的销毁过程中调用
B
析构函数,因此在
A
唯一\u ptr
成员的销毁过程中发生。因此,您的
removeB
调用将部分销毁的对象传递给
unique\u ptr
的移动构造函数。如果这是未定义的行为,我不会感到惊讶。

是否“这是一件可以做的事情”取决于应用程序的其余部分如何使用这些类。所以,恐怕你是唯一一个能确定“这是一件可以做的事情”的人。我的意思是,这更像是一个基本的问题:这能保证在任何编译器、任何平台、任何stdlib实现(不包括bug)上工作吗?这是一个非常棘手的问题。我认为这实际上取决于编译器的实现。不是100%确定,但是如果编译器支持c++11,我看不出有什么问题。因为这些类是紧密耦合的,你不能简单地合并它们吗?为什么你的析构函数需要调用一个返回
唯一的\u ptr
,引用
这个
?我询问的原因是,创建(或返回)一个
唯一\u ptr
与对象的初始化(
*此
不能在其析构函数中初始化)或防止其销毁(如果调用析构函数,则对象的销毁已不可撤销地开始)相关。强制
unique_ptr
释放(因此不销毁对象)将起作用,但在析构函数中执行此操作的需要表明设计严重受损。在我们的特定情况下,如果在析构函数中调用
removeB
,则应始终返回
nullptr
,但是有一个bug导致它返回一个实际的有效指针。
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());
}