如何知道指针是否已在其他地方释放

时间:2018-07-10 21:34:58

标签: c++ pointers object memory memory-management

我想如何跟踪我的差异指针(对象)。 例如,假设此代码(语言无关紧要,我在C ++和Python中都遇到相同的问题):

int *a = new int(2);
int *b = a;
delete a;
a = nullptr;

我想知道b不再可用。我曾考虑使用包装纸,但我认为这种解决方案缺乏优雅感。

你们知道更好的解决方案吗,例如某种簿记技术。

谢谢!!!

1 个答案:

答案 0 :(得分:3)

事实上,语言确实很重要,因为某些语言可能会根据您定义和实现对象和对象生命周期等想法的方式来完成您想要的事情。实际上,许多语言使得不可能或几乎不可能拥有类似于悬挂指针或悬挂引用的内容。但是这个问题被标记为C ++,并且有一个C ++示例,并且C ++肯定没有在delete表达式之后进行这种检查的方式。

为什么不呢?因为C ++语言及其典型编译器的核心原则之一就是您不用为不使用的东西付费。在几乎所有计算机体系结构上,都可以以非常有效的方式获取C ++原始指针的方式来实现,但并未提供任何方式来保证或检查该内存实际上仍然有效并且不会为完全不同的用途重用。编译器可以自动添加额外的簿记功能,从而可以检查C ++指针对象是否有效,但这意味着在实际程序执行过程中不可避免地要进行额外的工作,这将显着降低所有程序的速度,即使在部分程序中使用这类检查。因此,C ++可以通过这种方式生成快速代码,但存在要求程序正确使用指针的危险。

另一方面,C ++确实使实现各种包装程序和簿记操作相当容易,这些包装程序确实提供了所需的额外功能(如所需的检查),并且仍然使用与熟悉的原始指针语法非常相似的语法。更好的是,在标准库中为您提供了一些最有用的包装模式。我们可能会以几种方式重写您的示例代码,并提供一条语句来实际尝试使用*b

  1. 使用std::unique_ptr

    std::unique_ptr<int> a = std::make_unique<int>(2);
    std::unique_ptr<int> b = a;
    a = nullptr;
    std::cout << *b;
    

结果:程序无法编译!

b初始化a是非法的,因为unique_ptr中的“ unique”意味着编译器将帮助您确保一次只能指向一个指针。拥有的对象。并不是针对您所问的情况,而是实际上需要动态创建的对象,但每个对象只有一个指针,这也很普遍,它也控制对象的生存期。在这种情况下,unique_ptr的好处是,它有助于避免意外创建其他指针,而这些指针最终可能会悬空。

与此相关的另一件好事是,您不需要任何等效的delete。如果unique_ptr的生存期结束(例如,通过到达{}的末尾,则销毁具有unique_ptr作为成员的对象,或删除一个unique_ptr来自容器),它将为您完成delete。同样,如果您重新分配unique_ptr指向其他内容,它将delete之前指向的任何内容。因此,a = nullptr;行是一种强制指针立即清除并忘记其对象的方法,但这通常不是必需的。所有这些都有助于避免泄漏,重复删除和悬空指针。

顺便说一句,由于unique_ptr只是进行编译时检查以避免在明确定义的位置进行复制和自动清除,因此使用它的程序通常可以编译成可执行程序,其执行速度与直接使用原始指针newdelete。减少了对程序员的工作量和危险。

  1. 使用std::shared_ptr

    std::shared_ptr<int> a = std::make_shared<int>(2);
    std::shared_ptr<int> b = a;
    a = nullptr;
    std::cout << *b;
    

结果:打印2张。

shared_ptr将簿记添加到指针,以便它始终知道存在该对象的指针。通常只有在不再存在指向该对象的shared_ptr指针时,该对象才会被销毁。

但是有时您不希望您的第二个副本真正使该对象保持活动状态。更改原始指针时,对象应消失,就像原始代码中一样。这将我们带到最类似于原始代码的示例:

  1. 使用std::shared_ptrstd::weak_ptr

    std::shared_ptr<int> a = std::make_shared<int>(2);
    std::weak_ptr<int> b = a;
    a = nullptr;
    if (std::shared_ptr<int> b_lock = b.lock())
        std::cout << *b_lock;
    else
        std::cout << "b is null\n";
    

结果:打印“ b为空”。

weak_ptrshared_ptr一起使用。当同时使用两者时,指针指向的对象通常会在没有shared_ptr指向该对象时立即被销毁。而且,当该对象被销毁时,指向该对象的任何weak_ptr指针都会自动更改为空指针。

需要b.lock()调用和shared_ptr b_lock,因为您不能像weak_ptr那样直接使用*b。这是一项安全功能,因为如果您编写了代码来检查b是否不为空,然后又使用了*b,那么如果在检查和使用之间调用某些函数(或其他线程!),该怎么办? )碰巧破坏或改变了a?相反,我们使用lock()weak_ptr转换回本地shared_ptr,检查该指针是否为空,然后使用shared_ptr。本地shared_ptr保证对象可以继续生存足够长的时间,以用于将要使用它的代码,但此后不需要再停留了。

因此,您可以使用一种方法检查指针是否仍然有效。

使用shared_ptrweak_ptr确实需要考虑一些条件,这些条件会导致它们指向的对象保持活动状态或允许对其进行清理,但是使用{{1 }}和new