【发布时间】:2019-08-25 06:42:21
【问题描述】:
毫无疑问,共享指针是个好主意。但只要大型程序包含原始指针,我认为使用共享指针存在很大风险。主要是,您将失去对指向包含原始指针的对象的指针的实际生命周期的控制,并且错误将出现在更难查找和调试的位置。
所以我的问题是,是否没有尝试向现代 c++ 添加不依赖于使用共享指针的“弱指针”?我的意思是在程序的任何部分中删除时都会变为 NULL 的指针。有理由不用这样的自制包装器吗?
为了更好地解释我的意思,下面是我做的这样一个“弱指针”。我将其命名为 WatchedPtr。
#include <memory>
#include <iostream>
template <typename T>
class WatchedPtr {
public:
// the only way to allocate new pointer
template <typename... ARGS>
WatchedPtr(ARGS... args) : _ptr (new T(args...)), _allocated (std::make_shared<bool>(true)) {}
WatchedPtr(const WatchedPtr<T>& other) : _ptr (other._ptr), _allocated (other._allocated) {}
// delete the pointer
void del () {delete _ptr; *_allocated = false;}
auto& operator=(const WatchedPtr<T> &other) { return *this = other; }
bool isNull() const { return *_allocated; }
T* operator->() const { return _ptr; }
T& operator*() const { return *_ptr; }
private:
T* _ptr;
std::shared_ptr <bool> _allocated;
};
struct S {
int a = 1;
};
int main () {
WatchedPtr<S> p1;
WatchedPtr<S> p2(p1);
p1->a = 8;
std::cout << p1.isNull () << std::endl;
std::cout << p2.isNull () << std::endl;
p2.del ();
std::cout << p1.isNull () << std::endl;
std::cout << p1.isNull () << std::endl;
return 0;
}
结果:
1
1
0
0
-已编辑-
谢谢大家。到目前为止的cmets和答案之后的一些澄清:
- 我为 WatchedPtr 提供的实现只是为了说明我的意思:一个指针不能从外部分配中获取副本,不能从外部删除,如果删除则变为 null。实施远非完美,也并非完美。
- shared_ptr 和原始指针混合的问题很常见:A* 被保存为原始指针,因此在程序的某个点创建并在程序的某个点显式删除。 B 持有 A*,B* 持有 shared_ptr,因此 B* 具有模糊的寿命。因此,在 B 持有的 A* 被删除后,B 可能会存活很长时间。
- 在我看来,“WatchedPtr”的主要用途是防御性编程。即检查 null 并尽最大可能确保连续性(和调试错误)。 shared_ptr 可以做到,但以一种非常危险的方式 - 它会隐藏和延迟问题。
- “WatchedPtr”也可以有设计用途(非常少且明确的“所有者”),但这不是主要思想。为此,共享指针确实起到了作用。
- “WatchedPtr”的目的不是一次性替换程序中所有现有的原始指针。这与替换为 shared_ptr 的工作不同,恕我直言,它已经为整个程序一次性完成。这对于大型项目来说是不现实的。
【问题讨论】:
-
如果您已经在更改代码以使用某种类型的指针包装器,那么为什么不将其更改为正确使用正确的智能指针呢?
-
这不是线程安全的。尽管您使用了共享指针,但一个线程可能正在使用该指针,而另一个线程将其删除。只有在它被删除后,才会告诉线程“不,它不再存在”。这里有一个 UB 的窗口 AFAICT。
-
您需要原始指针,而不是在无用时传递共享指针。如果您的设计内存响应正确,那么弱原始指针的概念就没有实际意义。
-
我很好奇你为什么想要这个。你基本上有一个原始指针。你能举一个你想做的例子吗?
unique_ptr、shared_ptr和weak_ptr几乎可以满足您需要的所有所有权机制。 -
std::weak_ptr的一个重要安全功能是在使用它之前将其锁定,只要您需要使用引用的对象,就会生成一个std::shared_ptr。使用您的解决方案,无法防止破坏指向的对象。即使这不是一个问题,看起来你正在实施一个半措施。你保留了原始的拥有指针并实现了std::shared_ptr的一些优点。它不能解决您仍在使用原始拥有指针的基本问题。该更改并没有使代码库比您发现的更好。
标签: c++ c++11 smart-pointers