如何传递std :: unique_ptr?

lve*_*lla 72 c++ unique-ptr c++11

我第一次尝试使用C++ 11 unique_ptr; 我正在替换我的一个项目中的多态原始指针,该指针由一个类拥有,但经常传递.

我以前的功能如下:

bool func(BaseClass* ptr, int other_arg) {
  bool val;
  // plain ordinary function that does something...
  return val;
}
Run Code Online (Sandbox Code Playgroud)

但我很快意识到我无法切换到:

bool func(std::unique_ptr<BaseClass> ptr, int other_arg);
Run Code Online (Sandbox Code Playgroud)

因为调用者必须处理函数的指针所有权,所以我不想这样做.那么,什么是我的问题的最佳解决方案?

我虽然将指针作为参考传递,如下所示:

bool func(const std::unique_ptr<BaseClass>& ptr, int other_arg);
Run Code Online (Sandbox Code Playgroud)

但是我觉得这样做非常不舒服,首先是因为传递已经输入的内容似乎并非本能,_ptr作为参考的参考.其次,因为功能签名变得更大.第三,因为在生成的代码中,需要两个连续的指针间接来达到我的变量.

R. *_*des 86

如果希望函数使用指针对象,请传递对它的引用.没有理由将该函数与仅使用某种智能指针相关联:

bool func(BaseClass& base, int other_arg);
Run Code Online (Sandbox Code Playgroud)

并在通话现场使用operator*:

func(*some_unique_ptr, 42);
Run Code Online (Sandbox Code Playgroud)

或者,如果base允许参数为null,则保持签名不变,并使用get()成员函数:

bool func(BaseClass* base, int other_arg);
func(some_unique_ptr.get(), 42);
Run Code Online (Sandbox Code Playgroud)

  • 这很好,但你可以摆脱`std :: vector <std :: unique_ptr>`参数的`std :: unique_ptr`吗? (3认同)

Vic*_*avu 27

使用std::unique_ptr<T>(除了不必记住调用deletedelete[]显式)之外的优点是它保证指针是或者nullptr指向(基础)对象的有效实例.在我回答你的问题后,我会回到这个问题,但第一个信息是DO使用智能指针来管理动态分配对象的生命周期.

现在,您的问题实际上是如何在旧代码中使用它.

我的建议是,如果您不想转移或共享所有权,则应始终传递对该对象的引用.像这样声明你的函数(const根据需要使用或不使用限定符):

bool func(BaseClass& ref, int other_arg) { ... }
Run Code Online (Sandbox Code Playgroud)

然后调用者有一个std::shared_ptr<BaseClass> ptr将处理nullptr案件或将要求bool func(...)计算结果:

if (ptr) {
  result = func(*ptr, some_int);
} else {
  /* the object was, for some reason, either not created or destroyed */
}
Run Code Online (Sandbox Code Playgroud)

这意味着任何调用者都必须承诺引用是有效的,并且它将在函数体的执行过程中继续有效.


这是为什么我坚信你应该之所以没有通过原始指针或引用智能指针.

原始指针只是一个内存地址.可以有(至少)4个含义之一:

  1. 所需对象所在的内存块的地址.(好的)
  2. 您可以确定的地址0x0不是可解除引用的,并且可能具有"无"或"无对象"的语义.(坏的)
  3. 存储块的地址位于进程的可寻址空间之外(取消引用它将导致程序崩溃).(丑陋的)
  4. 可以取消引用但不包含您期望的内存块的地址.也许指针被意外修改,现在它指向另一个可写地址(在您的进程中完全是其他变量).写入这个内存位置会导致很多乐趣在执行期间发生,因为只要你被允许在那里写,操作系统就不会抱怨.(Zoinks!)

正确使用智能指针缓解了相当可怕的情况下,3和4,它们通常不会在编译时和你一般只检测经验在运行时,当你的程序崩溃或做意想不到的事情.

将智能指针作为参数传递有两个缺点:您无法在不进行复制的情况下更改指向对象的const-ness (这会增加开销并且不可能),并且您仍然保留第二个()含义.shared_ptrunique_ptrnullptr

我从设计角度将第二种情况标记为().这是一个关于责任的更微妙的论点.

想象一下当函数接收a nullptr作为其参数时它意味着什么.它首先必须决定如何处理它:使用"神奇"值代替丢失的对象?完全改变行为并计算其他东西(不需要对象)?恐慌并抛出异常?此外,当函数通过原始指针获取2个,3个甚至更多个参数时会发生什么?它必须检查每一个并相应地调整其行为.除了真正的原因之外,这在输入验证之上增加了一个全新的水平.

呼叫者应该是具有足够上下文信息的人来做出这些决定,或者换句话说,坏事不会越少,你知道的越多.另一方面,该函数应该只是接受调用者的承诺,即它所指向的内存可以安全地按预期使用.(引用仍然是内存地址,但在概念上代表了有效性的承诺.)


Mik*_*son 22

我同意Martinho,但我认为指出pass-by-reference的所有权语义非常重要.我认为正确的解决方案是在这里使用简单的pass-by-reference:

bool func(BaseClass& base, int other_arg);
Run Code Online (Sandbox Code Playgroud)

C++中传递引用的普遍接受的含义就像函数的调用者告诉函数"在这里,你可以借用这个对象,使用它,并修改它(如果不是const),但仅限于功能体的持续时间." 这绝不会与所有权规则相冲突,unique_ptr因为对象只是在短时间内被借用,没有实际的所有权转移(如果你把车借给某人,你是否签了标题)对他来说?).

因此,即使将引用(甚至是原始指针)拉出来也可能看起来很糟糕(设计方面,编码实践等)unique_ptr,但实际上并不是因为它完全符合所设置的所有权规则.这个unique_ptr.然后,当然,还有其他一些很好的优点,例如干净的语法,对a所拥有的对象没有限制unique_ptr,等等.