C完全是关于内存所有权
阿卡“ Ownership Semantics ”
一块动态分配的内存的所有者负责释放该内存 . 所以这个问题真的变成了拥有记忆的人 .
在C中,所有权由RAW指针包含在内部的类型记录,因此在一个好的(IMO)C程序中,很少见[RARE并非永远]看到RAW指针传递(因为RAW指针没有推断的所有权因此我们不能告诉谁拥有记忆,因此如果没有仔细阅读文件,你无法分辨谁负责所有权) .
相反,很少看到RAW指针存储在类中,每个RAW指针都存储在自己的SMART指针包装器中 . ( N.B.: 如果你没有一个对象,你不应该存储它,因为你不知道什么时候它会超出范围并被销毁 . )
所以问题是:
-
人们遇到什么类型的所有权语义?
-
使用哪些标准类来实现这些语义?
-
您觉得哪些情况有用?
让我们为每个答案保留一种语义所有权,这样他们就可以单独上下投票
摘要:
从概念上讲,智能指针很简单,而且简单易用 . 我已经看过许多尝试过的实现,但总是以某种方式打破它们,这对于随意使用和示例来说并不明显 . 因此,我建议始终使用经过良好测试的“智能指针”,而不是自己动手 . std :: auto_ptr或其中一个提升智能指针似乎涵盖了我的所有需求 .
std :: auto_ptr <T>:
单身人士拥有该物品 .
但允许转让所有权 .
用法:
这允许您定义显示所有权显式转移的接口 .
boost :: scoped_ptr <T>
单身人士拥有该物品 .
不允许转让所有权 .
用法:
用于显示明确的所有权 .
对象将被析构函数或显式重置时销毁 .
boost :: shared_ptr <T>(std :: tr1 :: shared_ptr <T>)
多个所有权 .
这是一个简单的引用计数指针 . 当引用计数达到零时,对象被销毁 .
用法:
当对象可以有多个owers,其生命周期无法在编译时确定 .
boost :: weak_ptr <T>
与shared_ptr <T>一起使用 .
在指针循环可能发生的情况下 .
用法:
用于在仅循环维护共享引用计数时停止保留对象的周期 .
11 回答
我认为自己无法在设计中分享所有权 . 事实上,从我的头脑中,我能想到的唯一有效的案例是Flyweight模式 .
从boost,还有pointer container库 . 如果您只在容器的上下文中使用对象,则它们比智能指针的标准容器更有效且更易于使用 .
在Windows上,有COM指针(IUnknown,IDispatch和朋友),以及用于处理它们的各种智能指针(例如ATL的CComPtr和基于_com_ptr类的Visual Studio中"import"语句自动生成的智能指针) .
对我来说,这三种类型涵盖了我的大部分需求:
shared_ptr
- 当计数器达到零时,引用计数,解除分配weak_ptr
- 与上面相同,但's a '奴隶'为shared_ptr
,无法解除分配auto_ptr
- 当创建和释放发生在同一个函数内部时,或者当对象必须被认为只有一个所有者时 . 当您将一个指针指定给另一个时,第二个指针来自第一个指针 .我有自己的实现,但它们也可以在
Boost
中找到 .我仍然通过引用传递对象(尽可能
const
),在这种情况下,被调用的方法必须假定对象仅在调用期间处于活动状态 .还有另一种我使用的指针,我称之为 hub_ptr . 当你拥有一个必须可以从嵌套在其中的对象访问的对象时(通常作为一个虚拟基类) . 这可以通过将
weak_ptr
传递给它们来解决,但它本身没有shared_ptr
. 因为它知道这些对象不仅仅是常规指针的模板包装器 .简单C模型
在我看到的大多数模块中,默认情况下,假设接收指针是 not 接收所有权 . 实际上,放弃指针所有权的函数/方法都非常罕见,并且在其文档中明确表达了这一事实 .
This model assumes that the user is owner only of what he/she explicitly allocates . 其他所有内容都会自动处理(在范围退出处或通过RAII) . 这是一个类似C的模型,扩展的事实是大多数指针都由对象拥有,这些对象将自动或在需要时释放它们(主要是在所述对象破坏时),并且对象的生命持续时间是可预测的(RAII是你的朋友,再次) .
在这个模型中,原始指针是自由流通的,并且大多数都没有危险(但如果开发人员足够聪明,他/她将尽可能使用引用) .
原始指针
std :: auto_ptr
boost :: scoped_ptr
智能指针C.模型
在充满智能指针的代码中,用户可以希望忽略对象的生命周期 . 所有者永远不是用户代码:它是智能指针本身(RAII,再次) . The problem is that circular references mixed with reference counted smart pointers can be deadly ,所以你必须同时处理共享指针和弱指针 . 因此你仍然需要考虑所有权(弱指针很可能指向任何东西,即使它优于原始指针也是它可以告诉你的) .
boost :: shared_ptr
boost :: weak_ptr
结论
无论我描述的模型, unless exception, receiving a pointer is not receiving its ownership 和 it is still very important to know who owns who . 即使对于大量使用引用和/或智能指针的C代码 .
没有共享所有权 . 如果您这样做,请确保它只包含您无法控制的代码 .
这解决了100%的问题,因为它迫使你理解一切如何相互作用 .
共享所有权
boost :: shared_ptr
在多个对象之间共享资源时 . boost shared_ptr使用引用计数来确保在每个人都被finsihed时解除分配资源 .
std::tr1::shared_ptr<Blah>
通常是你最好的选择 .一位所有者
boost :: scoped_ptr
当您需要动态分配内存但希望确保它在块的每个出口点都被释放 .
我发现这很有用,因为它可以很容易地重新安装,并且可以在不必担心泄漏的情况下发布
yasper :: ptr是一个轻量级的boost :: shared_ptr之类的替代品 . 它适用于我(现在)的小项目 .
在http://yasper.sourceforge.net/的网页中,它描述如下:
还有另一种经常使用的单一可转让所有者形式,它最好是
auto_ptr
,因为它避免了由于分配语义的疯狂腐败而引起的问题 .我说的是
swap
. 任何具有合适的swap
函数的类型都可以被视为对某些内容的智能引用,它通过交换所有权直到将所有权转移到相同类型的另一个实例 . 每个实例都保留其标识,但绑定到新内容 . 这就像一个安全可重新绑定的参考 .(这是一个智能参考而不是智能指针,因为您不必明确取消引用它来获取内容 . )
这意味着auto_ptr变得不那么必要 - 它具有良好的
swap
功能 . 但是所有std容器都可以 .一位所有者:Aka删除复制
std :: auto_ptr
当对象的创建者想要明确地将所有权交给其他人时 . 这也是我给你的代码记录的方式,我不再跟踪它,所以一定要在完成后删除它 .