我对 boost 库中实现的智能指针有一些疑问。shared_ptr 和 scoped_ptr 之间的唯一区别是 scoped_ptr 没有复制构造函数而 shared_ptr 有吗?当对象不调用复制构造函数时,我是否应该始终使用 scoped_ptr 而不是 shared_ptr?我也不明白共享/范围数组的想法。我不能只使用 std::vector 代替它吗?
5 回答
shared_ptr 和 scoped_ptr 之间的唯一区别是 scoped_ptr 没有复制构造函数而 shared_ptr 有吗?
区别比这更根本。它与智能指针如何拥有它指向的对象有关。智能指针与哑指针的不同之处在于所有权的概念是其功能的核心组成部分。所有权语义是区分不同类型智能指针的原因。
因为智能指针“拥有”它们所指向的东西,所以它们可以做一些有用的事情,比如在智能指针消失时删除对象(仅使用语言规则就可以做到这一点;不需要编译器魔法)。这样,C++ 中的内存管理几乎可以实现自动化(尽管有相反的说法,现代 C++ 中几乎不需要手动内存管理)。
shared_ptr
实现内存管理的引用计数语义。多个shared_ptr
s 可以拥有一个对象。shared_ptr
离开不一定会删除它指向的对象,因为可能有另一个拥有shared_ptr
该对象。最后shared_ptr
一个拥有对象并离开的人将删除它拥有的对象。scoped_ptr
实现独占所有权语义。只有一个人scoped_ptr
可以拥有任何一个对象。当 ascoped_ptr
消失时,它总是会删除它拥有的对象(因为只有一个所有者)。它通常用作在自由存储上分配的对象的轻量级 RAII 机制。
数组版本 (shared_array
和scoped_array
) 具有基本相同的语义,但专门为数组设计,例如它们使用delete[]
而不是delete
,实现数组下标运算符等。
shared_ptr
如果默认行为不适合对象,shared_array
还允许您指定自定义删除器。并且不具备这种能力,因为与 和 相比,它们非常轻量级。delete
scoped_ptr
scoped_array
shared_ptr
shared_array
在最新的 C++ 版本 C++11 中,unique_ptr
还有scoped_ptr
一个unique_ptr
. 在 C++03 中,一个较旧但更广泛支持的 C++ 版本,除了它很容易意外地以不安全的方式使用它(这就是它在 C++11 中被弃用的原因)之外,它auto_ptr
等同于它。unique_ptr
当对象不调用复制构造函数时,我是否应该始终使用 scoped_ptr 而不是 shared_ptr?
您选择哪一个不取决于复制构造函数的存在,因为shared_ptr
并且scoped_ptr
不需要对象是可复制构造的。您根据所需的所有权语义选择一个。如果对象将有多个所有者,则使用shared_ptr
. 如果对象只有一个所有者并且对象的存在仅在一个范围内持续,请使用scoped_ptr
(因此命名为scoped_ptr
)。
我也不明白共享/范围数组的想法。我不能只使用 std::vector 代替它吗?
std::vector
不像那样实现引用计数语义shared_array
。std::vector
更像scoped_array
,但可以复制。当你复制 astd::vector
时,你也复制了它包含的所有元素。情况并非如此scoped_array
。std::vector
还具有允许您操作和检查其内容的功能(例如push_back
、insert
、erase
等),但比scoped_array
.
我会说你想错了。问题不在于您是否调用了复制构造函数——而是您是否需要调用复制构造函数。换句话说,您应该决定是使用 shared_ptr 还是 scoped_ptr,而不是基于阅读您的代码,而是基于对对象所有权和对象生命周期的思考。
假设您有一个要在堆上而不是在堆栈上创建的对象,无论出于何种原因(也许它太大而不能在堆栈上,也许您可能想在某个时候用不同的对象替换它,也许您希望它晚初始化),但它的生命周期永远不应该长于它的包含范围。一个常见的例子是类中的实例变量:当它们所在的对象被删除时,它们应该经常被删除。然后,您应该使用 scoped_ptr。
但有时您不知道何时可以安全地提前删除对象。例如,考虑查找表中的一个对象。您想返回它以响应查找,但是当它被删除时会发生什么——有人仍然在使用之前查找它的对象吗?在这种情况下,您可以使用 shared_ptr,它共享对象的所有权,这样只有在没有人拥有指针副本时才会将其删除。
那么为什么有人会使用 scoped_ptr 而不是 shared_ptr 呢?首先,知道何时调用析构函数是 C++ 等非内存管理语言的一大优势。也许析构函数很昂贵,或者它释放了资源;很高兴知道这些事情何时发生。此外,对于 shared_ptr,如果您不小心创建了循环引用,则可能会导致内存泄漏。
一般来说,几乎你拥有的每个指针都应该是“拥有的”——在你的代码中应该有一个地方可以通知并删除它。scoped_ptr 非常适合这个;当您希望将拥有的对象传递给非所有者时,您可以使用裸指针。但是,如果您绝对需要共享对象的所有权,请使用 shared_ptr - 只要您小心正确地使用它!
至于作用域/共享数组,您当然可以使用 std::vector,但数组更便宜,有时人们想要性能优势。
是的。scoped_ptr
不允许复制,而允许shared_ptr
。但是这种“简单”的区别对智能指针的实现和使用产生了巨大的影响。
scoped_ptr
比shared_ptr
不涉及引用计数更快更轻。shared_ptr
将计算分配的数量并且在所有引用都过期/超出范围之前不会删除对象。
现在您关于向量的问题意味着您实际上并不熟悉动态分配的概念以及该概念与静态分配与静态分配之间的区别。您确实应该阅读有关 C(++) 的入门书,并了解动态分配的原因以及何时需要它。
向量存储对象列表。AXXX_ptr
存储指向(单个)动态分配对象的指针。苹果和橙子。
shared_ptr
与 有很大不同scoped_ptr
。 scoped_ptr
(现在在 C++11 中标准化为std::unique_ptr
)只是一个 RAII 样式的智能指针,它获取资源的所有权,然后在指针超出范围时释放拥有的资源。
然而, Ashared_ptr
可以与 shared_ptr 的其他实例共享资源的所有权。只要一个或多个shared_ptr
实例拥有该资源,该资源就会保持活动状态。这是一种称为引用计数的自动内存管理技术(一种垃圾收集形式) 。它基本上提供了与更高级的垃圾收集算法相同的效果,除了与其他垃圾收集技术不同的是,它不处理循环引用。
至于使用std::vector
vs boost::scoped_array
,是的 -scoped_array
并没有真正提供太多优势。但是,boost::shared_array
提供引用计数语义,就像shared_ptr
.
如果分配内存,可以将新创建的指针放在作用域指针中,这样如果 malloc/noew 失败,内存将被释放。这就是我通常使用它的方式,或者如果它是一个需要在堆上分配的对象,但我想将它视为一个堆栈对象,因为它只会在作用域结束时才有效。
共享指针是如果您想传递指针并允许对象拥有多个所有者。然后没有所有者需要对对象负责,他们都可以停止使用它并确保它会被正确释放。(您不想释放您知道已被其他人使用的对象)