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

Posted

技术标签:

【中文标题】如何知道指针是不是已在其他地方释放【英文标题】:How to know if a pointer have been freed somewhere else如何知道指针是否已在其他地方释放 【发布时间】:2018-07-10 21:34:58 【问题描述】:

我想如何跟踪我的不同指针(对象)。 例如让我们假设这段代码(语言无关紧要,我在 C++ 和 Python 中遇到了同样的问题):

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

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

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

谢谢!!!

【问题讨论】:

一个选项是你有一个对象,它是唯一负责删除所有和每个对象的对象,包括它自己。但我不推荐它。更喜欢RAII,智能指针和封装。 这不是 shared_pointer 的用途吗?还是我误解了您的要求? 【参考方案1】:

语言确实很重要,因为某些语言可能有做你想做的事的方法,这取决于它们如何定义和实现对象和对象生命周期等思想。事实上,许多语言都无法或几乎不可能拥有类似于悬空指针或悬空引用的东西。但是这个问题被标记为 C++ 并且有一个 C++ 示例,而 C++ 肯定没有办法在 delete 表达式之后进行这种检查。

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

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

    使用std::unique_ptr

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

结果:程序无法编译!

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

关于它的另一个好处是您不需要任何等效的delete。如果unique_ptr 的生命周期结束(例如,到达block 的末尾,销毁具有unique_ptr 作为成员的对象,或从容器中删除unique_ptr),它会为你做delete。同样,如果您将unique_ptr 重新分配为指向其他对象,它将delete 之前指向的任何对象。所以a = nullptr; 行是一种强制指针立即清理并忘记其对象的方法,但通常不是必需的。所有这些都有助于避免泄漏、双重删除和悬空指针。

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

    使用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 指向它的指针存在时才被销毁。

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

    使用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 is null”。

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

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

所以你有办法检查指针是否仍然有效。

使用shared_ptrweak_ptr 确实需要考虑导致它们指向的对象保持活动或允许它们被清理的条件,但使用newdelete 的设计也是如此直接。

【讨论】:

谢谢!!这是你在这里给我的一个很好的答案! 当我说语言无关紧要时,我期待某种设计模式,或者可以在不同语言中使用的更通用的技术,我没想到会有一些构建-在 C++ 中的机制中。因为在 C# 中应该是不可能的,所以我没有找到任何内置机制。但是在 Unity 中,当一个游戏对象被销毁时,所有引用都变为 null,我不知道这是如何完成的。

以上是关于如何知道指针是不是已在其他地方释放的主要内容,如果未能解决你的问题,请参考以下文章

如何检查电子邮件地址是不是已在使用 Firebase

free()的释放是何涵义,是把内容清空还是把指针置为NULL啊,还是其他啥啊。。求指点啊。。

Django 服务器错误:端口已在使用中

了解函数如何使用和存储变量

释放 NETBIOS 名称

如何防止网站在其他地方被完全消费和镜像服务?