将unique_ptr移动到自己的析构函数中是否可以?

时间:2016-07-08 11:10:30

标签: c++ c++11

昨天我遇到了一个类,它会在析构函数中调用一个方法,最终将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抱怨它,但我想得到某种确认,这是一件好事。

1 个答案:

答案 0 :(得分:3)

你在这里发生了一些非常纠结的事情。首先,您有class Aunique_ptrB。这表明所有权(或父/子)关系:A拥有BB反过来有一个指向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的移动构造函数。如果这是未定义的行为,我不会感到惊讶。