为什么原始指针没有“弱指针”?还是在那里?

时间:2019-04-03 16:38:37

标签: c++ c++11 smart-pointers

毫无疑问,共享指针是个好主意。但是,只要大型程序包含原始指针,我认为使用共享指针就有很大的风险。主要是,您将失去对指向包含原始指针的对象的指针的实际生命周期的控制,并且错误将发生在难以查找和调试的位置。

所以我的问题是,是否没有尝试将不依赖于使用共享指针的“弱指针”添加到现代c ++中?我的意思是仅当指针在程序的任何部分被删除时,该指针将变为NULL。有没有理由不使用这种自制包装纸?

为了更好地解释我的意思,以下是我所做的“弱指针”。我将其命名为WatchedPtr。

Person

结果:

#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;
}

-已编辑

谢谢大家。到目前为止的评论和答案有一些澄清:

  • 我为WatchedPtr提供的实现只是为了说明我的意思:一个指针,该指针不能从外部分配中获取副本,不能在外部删除,并且如果删除则为null。众所周知,该实现还远远不够完美,并且并不意味着完美。
  • shared_ptr和原始指针混合在一起的问题非常普遍:A *被保留为原始指针,因此在程序的某个位置创建,并在程序的某个位置显式删除。 B持有A *,而B *作为shared_ptr持有,因此B *的寿命模糊。因此,B可能会在删除B持有的A *之后存活很长时间。
  • 在我看来,“ WatchedPtr”的主要用法是防御性编程。即检查是否为空,并尽最大可能进行连续性检查(以及调试错误)。 shared_ptr可以做到,但是以一种非常危险的方式-它会隐藏并延迟问题。
  • “ WatchedPtr”(很少和明确的“所有者”)也可以有设计用途,但这不是主要思想。为此,共享指针确实可以完成工作。
  • “ WatchedPtr”的目的不是立即替换程序中所有现有的原始指针。这与替换shared_ptr所付出的努力不同,IMHO已针对整个程序立即完成。对于大规模程序而言,这是不现实的。

3 个答案:

答案 0 :(得分:5)

弱指针依赖于智能指针基础结构的通知,因此您永远无法使用实际的原始指针进行操作。

可以想象一个unique_ptr的扩展,它肯定支持弱指针。大概没有人急于实现这种功能的主要原因是,弱指针已经处于规模的“使用引用计数,一切都应该正常工作”的末端,而unique_ptr处于“通过RAII管理您的生命”或者您不是真正的 C ++程序员”。弱指针还要求每个分配有一个单独的控制块,这意味着与WatchedPtr相比,shared_ptr的性能优势极小。

答案 1 :(得分:2)

  

我认为使用共享指针存在很大的风险。主要是,您将失去对指向包含原始指针的对象的指针的实际生命周期的控制,并且错误将发生在难以查找和调试的位置。

那你说

  

只是一个指针,该指针在程序的任何部分被删除后都会变为NULL。

看不到矛盾吗?

您不想使用共享指针,因为对象的生存期是在运行时确定的。到目前为止一切顺利。

但是,您希望指针在所有者删除时自动变为空。问题是,如果知道指针的寿命,则根本不需要它!如果您知道指针的生命周期何时结束,那么您应该能够删除该指针的所有实例,这有一种手段可以检查指针是否失效。

如果您有一个指针,您不知道所有者什么时候会释放它,并且对于弱者的观点没有办法检查或没有明显的副作用,那么您真的可以控制生命周期吗?你的指针?不是。

实际上,您的实现依赖于包含共享指针。从某种意义上说,这很有启发性,您需要某种形式的共享所有权才能实现可能具有弱指针的原始指针。然后,如果您需要共享所有权来实现带有弱引用的原始指针,则剩下一个共享指针。这就是为什么您提议的课程的存在是矛盾的。

std::shared_ptr + std::weak_ptr用于解决“程序的某些部分不知道所有者何时释放资源”的问题。您需要一个std::shared_ptr和多个std::weak_ptr,以便他们知道何时释放资源。这些类具有在运行时检查变量生存期所需的信息。

或者相反,如果您知道指针的寿命,则可以使用该知识并找到一种方法来删除悬挂的指针,或者公开一种检查悬挂的指针的方法。

答案 2 :(得分:0)

通过阅读答案和评论以及C++ Core Guidelines by Bjarne Stroustrup & Herb Sutter,我得出了以下答案:
遵循准则时,不需要包含“新”和“删除”的“ WatchedPtr”。但是,对于我来说,出于调试/ QA目的,跟踪从智能指针获取的原始指针的有效性的方法仍然存在疑问。

详细信息:

原始指针应继续使用。由于各种原因。但是,显式的“ new”和“ delete”不应该。分别用shared_ptr / unique_ptr代替调用“ new”和“ delete”的情况。
在当前分配了原始指针的地方,没有必要用“ WatchedPtr”替换它。
如果将原始指针替换为分配了其他指针的原始指针,则在大多数情况下将替换为unique_ptr,在其他情况下将替换为shared_ptr。 “ WatchedPtr”(如果有的话)将从该点继续,从共享/唯一指针构建。

因此,我有posted a somewhat different question