使用std::unique_ptr<T>
(除了不必记住调用delete
或delete[]
显式调用)的优点是,它可以保证指针是nullptr
或指向(基)对象的有效实例。我会回到这个我回答你的问题之后,但在第一个消息是DO使用智能指针来管理动态分配对象的生存期。
现在,您的问题实际上是如何在旧代码中使用它。
我的建议是,如果您不想转让或共享所有权,则应始终将引用传递给该对象。这样声明您的功能(const
根据需要使用或不使用限定符):
bool func(BaseClass& ref, int other_arg) { ... }
然后,具有的调用方std::shared_ptr<BaseClass> ptr
将处理nullptr
案件或要求bool func(...)
计算结果:
if (ptr) {
result = func(*ptr, some_int);
} else {
}
这意味着任何调用者都必须保证引用是有效的,并且在整个函数体执行期间它将继续有效。
这是为什么我坚信你应该之所以没有通过原始指针或引用智能指针。
原始指针只是一个内存地址。可以具有(至少)4个含义之一:
- 所需对象所在的内存块的地址。(好)
- 您可以确定的地址0x0是不可引用的,并且可能具有“ nothing”或“ no object”的语义。(坏)
- 内存块的地址超出了进程的可寻址空间(取消引用该内存块可能会导致程序崩溃)。(丑)
- 可以取消引用但不包含您期望的内存块地址。也许指针被意外修改,现在它指向另一个可写地址(进程中完全其他变量的地址)。写入该内存位置有时会在执行过程中带来很多乐趣,因为只要允许您在该位置写操作系统,操作系统就不会抱怨。(Zoinks!)
正确使用智能指针可以缓解情况3和情况4,这在编译时通常是无法检测到的,并且通常只在运行时程序崩溃或发生意外情况时才会遇到这种情况。
将智能指针作为参数传递有两个缺点:如果不进行复制就不能更改指向对象的const
-ness (这会增加开销,对于而言是不可能的),并且仍然留下第二个()含义。shared_ptr
unique_ptr
nullptr
从设计角度来看,我将第二种情况标记为(不好)。关于责任,这是一个更微妙的论点。
想象一下当一个函数接收一个nullptr
参数作为参数时的含义。它首先必须决定如何处理它:使用“魔术”值代替丢失的对象?完全更改行为并计算其他内容(不需要该对象)?恐慌并抛出异常?此外,当函数通过原始指针接受2个,3个或更多参数时会发生什么?它必须检查它们中的每一个并相应地调整其行为。这在没有任何真正原因的情况下,在输入验证之上增加了一个全新的水平。
呼叫者应该是具有足够的上下文信息来做出这些决定的呼叫者,换句话说,坏消息就越可怕,您就知道越多。另一方面,该函数应仅遵循调用者的承诺,即所指向的内存可以安全地按预期使用。(引用仍然是内存地址,但从概念上讲是对有效性的保证。)
std::unique_ptr
一个std::vector<std::unique_ptr>
说法?