【发布时间】:2009-12-17 04:06:06
【问题描述】:
我知道智能指针是用于资源管理的,并且支持RAII。
但是在哪些极端情况下智能指针看起来不智能以及使用它时要记住的事情呢?
【问题讨论】:
-
是的,你没看过我的 MI 系列。
标签: c++ smart-pointers
我知道智能指针是用于资源管理的,并且支持RAII。
但是在哪些极端情况下智能指针看起来不智能以及使用它时要记住的事情呢?
【问题讨论】:
标签: c++ smart-pointers
智能指针无助于防止图形结构中的循环。
例如,对象 A 拥有一个指向对象 B 和对象 B 的智能指针——返回对象 A。如果在 A 与 B(或 B 与 A)断开连接之前释放指向 A 和 B 的所有指针,A 和 B 都会抱在一起,形成快乐的内存泄漏。
垃圾收集可以帮助解决这个问题 - 它可以看到两个对象都无法访问并释放它们。
【讨论】:
我想提一下性能限制。智能指针通常使用原子操作(如 Win32 API 中的InterlockedIncrement )进行引用计数。这些函数比普通整数运算要慢得多。
在大多数情况下,这种性能损失不是问题,只要确保不要对智能指针对象制作太多不必要的副本(最好在函数调用中通过引用传递智能指针)。
【讨论】:
这很有趣:Smart Pointers.
这是 Andrei Alexandrescu 的“现代 C++ 设计”中的一个示例章节。
【讨论】:
注意转换 - 在原始指针和智能指针之间分配时。糟糕的智能指针——比如_com_ptr_t——允许隐式转换使情况变得更糟。大多数错误发生在转换时。
注意循环 - 如前所述,您需要弱指针来打破循环。然而,在复杂的图表中,这并不总是容易做到的。
选择太多 - 大多数库提供具有不同优点/缺点的不同实现。不幸的是,大多数时候这些不同的变体是不兼容的,这在混合库时成为一个问题。 (比如说,LibA 使用 LOKI,LibB 使用 boost)。必须为enable_shared_from_this 提前计划很糟糕,必须为一堆对象决定intrusive_ptr、shared_ptr 和weak_ptr 之间的命名约定很糟糕。
对我来说,shared_ptr(或类似功能之一)的最大优势在于它与创建时的销毁策略相耦合。 C++ 和 Win32 都提供了很多摆脱事物的方法,这甚至都不好笑。在构建时进行耦合(不影响指针的实际类型)意味着我将这两种策略放在一个地方。
【讨论】:
除了技术限制(已经提到:循环依赖),我想说智能指针最重要的一点是要记住它仍然是删除堆分配对象的一种解决方法。 在大多数情况下,堆栈分配是管理对象生命周期的最佳选择 - 以及引用的使用。
【讨论】:
【讨论】:
这里有一些事情
weak_ptr 来救援的地方。【讨论】:
weak_ptr只能隐藏问题。
在某些类型的具有循环的数据结构中,引用计数存在问题。从多个线程访问智能指针也可能会出现问题,对引用计数的并发访问可能会导致问题。 boost 中有一个名为atomic.hpp 的实用程序可以缓解这个问题。
【讨论】:
许多人在将智能指针与原始指针(指向相同的对象)混合使用时会遇到问题。一个典型的例子是与使用原始指针的 API 交互时。
例如;在boost::shared_ptr 中有一个返回原始指针的.get() 函数。如果小心使用,功能会很好,但似乎很多人会绊倒它。
恕我直言,这是“泄漏抽象”的一个例子。
【讨论】:
Raymond Chen 对智能指针的矛盾是出了名的。关于when the destructor actually runs 存在一些问题(请注意,析构函数在明确定义的时间以明确定义的顺序运行;只是偶尔你会忘记它是在最后一个函数中的行)。
还请记住,“智能指针”是一个相当大的类别。我将std::vector 包含在该类别中(a std::vector is essentially a smart array)。
【讨论】:
operator* 是const 成员函数并返回非const 指针的原因。
std::auto_ptr 的值与指向对象的值无关。这就是为什么 std::auto_ptr operator* 是 const 成员函数和返回一个非const 指针。” (显然,具有原始指针语义的智能点将是......原始指针。)