来自std :: unique_ptr管理的对象的C ++回调

时间:2014-12-01 21:17:24

标签: c++ unique-ptr

我有一个C ++项目,我将对象存储在网格容器的单元格中。每个单元格可以包含存储在std :: unique_ptr中的一个或零个对象。当然,将这些对象作为参数的所有方法都应该使用const std :: unique_ptr引用来保持"唯一性"。

其次,当物体发生某些事情时,它们会发出信号,并将自己作为参数之一;这些信号由网格容器在单个处理程序中捕获(因此我们需要对象引用)。处理程序可以对对象执行某些操作或发出自己的信号,进一步传递对象引用。

问题是对象本身显然不能将std :: unique_ptr引用返回给自己,而对它们进行操作的所有其他方法都需要一个。有没有办法解决这个问题,还是我必须放弃独特的指针并使用原始指针?

这是使用sigc ++库的代码示例(因为我还没有测试过,所以请允许出现小错误):

class Inhabitant
{
public:
  void sos()
  {
    signal_distress.emit (*this);
  }

  // Signals
  sigc::signal<void, Inhabitant &> signal_distress;
};

class Cell
{
public:
  std::unique_ptr<Inhabitant> set_inhabitant (std::unique_ptr<Inhabitant> inhabitant)
  {
    // Set new inhabitant, return previous one...
  }

private:
  std::unique_ptr<Inhabitant> m_inhabitant;
};

class Grid
{
public:
  void add_inhabitant_at (std::unique_ptr<Inhabitant> inhabitant,
                          unsigned int x, unsigned int y)
  {
    // Connect the inhabitant to our rescue team
    inhabitant->signal_distress.connect (sigc::mem_fun (*this,
      &Grid::on_inhabitant_distress));

    // Place in cell
    m_cells[x][y].set_inhabitant (std::move (inhabitant));
  }

private:
  // Data
  Cell m_cells[100][100];

  // Helpers
  void help_inhabitant (const std::unique_ptr<Inhabitant> &inhabitant)
  {
    // Do something helpful
  }

  // Signal handlers
  void on_inhabitant_distress (Inhabitant &inhabitant)
  {
    // Now, how do I call help_inhabitant(), or any other function that expects
    // a unique_ptr reference?
  }
};

3 个答案:

答案 0 :(得分:2)

最佳做法是不将诸如unique_ptr之类的智能指针传递给不需要(或共享)unique_ptr管理的资源的所有权的函数。换句话说,我认为你不想通过const引用传递unique_ptr。相反,采用这些unique_ptr引用的所有函数实际上只需要const Inhabitant &。例如:

void help_inhabitant (const Inhabitant &inhabitant) {
    // do stuff with the inhabitant directly
}

答案 1 :(得分:0)

好的,当我意识到细胞居民没有理由向信号发送自己的参考时解决了这个问题。相反,当使用信号注册时,侦听器(网格)可以绑定对居民的引用(即,unique_ptr):

inhabitant->signal_distress().connect (std::bind (&Grid::on_inhabitant_distress,
                                                 this,
                                                 std::cref (inhabitant));

这样信号处理程序可以采用unique_ptr:

void on_inhabitant_distress (const std::unique_ptr<Inhabitant> &inhabitant)
{
  // Now everything is fine!
  help_inhabitant (inhabitant);
}

并且“唯一性”链保持不变。

答案 2 :(得分:0)

  

当然,所有具有这些对象作为参数的方法都应该使用const std :: unique_ptr引用来保持&#34;唯一性&#34;。

不,该对象仍然拥有一个唯一的所有者,无论有多少其他代码可以通过非拥有指针或引用访问它。你认为传递const unique_ptr<T>&维持任何形式的不变量或强制执行政策都是一种错觉。

  

有没有办法解决这个问题,还是我必须放弃独特的指针并使用原始指针?

你不必在竞争中放弃它,只是在它不合适的地方。使用unique_ptr来管理所有权和生命周期,使用原始指针简单地引用由其他一些代码管理的对象。

  

关于unique_ptr的关键是你传递对它的引用而不是原始指针吗?

不,绝对不是。

unique_ptr管理对象的所有权,但不能用于访问对象。

如果你想在不拥有对象的情况下引用该对象,那么将引用或指针传递给该对象就可以了(只要接收这些指针或引用的代码不认为它正在取得所有权并尝试删除宾语)。只想使用Inhabitant的代码并不需要关心它由unique_ptr拥有,它只是想使用该对象。如何管理其生命周期是其他人关注的问题,并且不拥有该对象的代码不应该依赖于所有权策略。避免这种依赖性将允许您将所有者更改为使用shared_ptr或其他一些机制,并且信号处理程序将不受影响,因为他们不必更改访问对象的方式。

通过unique_ptr按值(或右值参考)传递所有权。不要通过const-reference传递unique_ptr,因为它完全没用,调用者无法使用原始指针完成任何操作。

使用对unique_ptr的引用实际上引入了一类新的错误,否则将无法存在:

void register_callback(func_type f, const unique_ptr<T>& obj);

unique_ptr<T> p(new T);
register_callback(func, p);   // stores reference to p
unique_ptr<T> p2 = std::move(p);

现在信号处理程序仍然引用p,当回调发生时它将变为空。 unique_ptr身份完全无关紧要,重要的是只有一个unique_ptr对象拥有指针,拥有它的对象并不重要。但是你通过绑定对它的引用使得回调取决于unique_ptr的确切实例,所以你不能移动它(所以你永远不能移动Cell,这意味着你不能将其存储在可能重新分配和移动其元素的vector容器中

如果你这样做,而不是回调是指对象,它并不精确地存储在哪里:

void register_callback(func_type f, T* obj);

unique_ptr<T> p(new T);
register_callback(func, p.get());   // stores p.get()
unique_ptr<T> p2 = std::move(p);

p.get()指针的回调副本仍然有效,即使它的所有权从一个对象转移到另一个对象。