35

C++中如何确定或知道一个指针之前是否被删除?

当我试图删除以前在代码的另一部分中删除的指针时,它引发了一个无法处理的异常。

我想知道是否有办法检查或尝试删除指针?有关高级内存操作的任何参考。

我也想掌握指针的未处理异常以及对受保护或访问的访问是违规的,......这种错误。

感谢那些提供一些知识和时间来帮助他人并分享他们的利益的人


更新

许多现代 c++ 开发人员社区的重要建议是 - 使用智能指针或尽量避免使用原始指针。但是为了抛出安全性和确保没有内存(ISO_CPP_FAQ),当然如果你想避免使用智能指针的小开销[可能并不总是很明显,但它们有开销],你可以编写处理原始指针的自定义方法 [ type*] - 这不是通用的。 总是喜欢智能指针而不是原始指针

在“Going Native 2013”​​中,一个常见的建议是——永远不要使用原始指针。

4

7 回答 7

36

可以有三种解决方案。您可能需要根据要达到的工作量/质量比来选择一个:

优雅和最正确的解决方案:

使用智能指针,您不必delete再次手动调用。这是克服此问题的最佳方法。它利用了 RAII 原理,该原理非常适用于像 C++ 这样没有内置垃圾收集器的语言。

不太优雅但可行的解决方案:

NULL删除后分配指针。调用指针deleteNULL无操作的,因此它消除了进行额外NULL检查的需要,但这可能会隐藏一些问题而不是使它们可见。

不太优雅但更正确的解决方案:

delete通过让你的程序崩溃来寻找所有的多个问题。您不妨使用内存分析器程序(如 valgrind),然后修复您的代码以避免所有这些问题。

于 2013-03-31T15:12:12.333 回答
6

这是一个很好的问题,但是在手动内存管理环境(如 C/C++ 及其表亲)中工作的基本事实之一是,没有好的方法可以事后查看指针并询问它是否有效——一旦失效了就没了,看着就容易炸。你的工作是确保它永远不会被删除或释放超过一次,并且在那之后永远不会被访问。

一定要看看智能指针,它的发明是为了在这些情况下让程序员的生活更轻松。(更传统的方法是要小心,不要把它搞砸,然后当你知道指针被删除时,可能会为指针分配 NULL,正如 Alok 所说。)

于 2013-03-31T15:11:41.313 回答
4

在 C++ 中如何确定或知道一个指针之前是否被删除?

语言标准没有提供任何合法的方法来确定任意指针是否有效。

有一种方法,但它是高度特定于编译器/操作系统的。您可以挂接到现有的内存管理器,也可以用您自己的内存管理器替换它,并为指针验证提供专用功能。不过,这可能并不容易做到。如果性能很关键,你真的不想依赖这个功能。

于 2013-03-31T15:33:09.683 回答
3

使用shared_ptr<>and shared_array<>, remembershared_ptr<>可用于管理分配给数组的内存,前提是提供了适当的 Deleter,否则用于shared_array<>管理您的数组

A* a_tab=new A[100];
boost::shared_ptr<A> a_tab_ok(a_tab,ArrayDeleter<A>()); 

//只有在

template <typename T>
    class ArrayDeleter
    {
    public:
        void operator () (T* d) const
        {
            delete [] d; //will delete array!
        }
    };

提供

于 2013-03-31T15:21:53.477 回答
2

指针不会告诉你任何事情。您的设计应该:如果您使用动态分配,通常是因为您的应用程序要求对象具有特定的生命周期,因此您知道何时正确删除该对象。如果对象是可复制的,或者具有与范围相对应的生命周期,则(通常)不会动态分配它。

当然,在非常低级的代码中也有例外——如果你正在实现类似的东西std::vector,你将不得不使用某种动态分配,因为在编译时大小是未知的。但是这样的分配不应该逃脱;处理内存是低级类的责任。

最后,缓冲区溢出、访问已删除的内存等都是未定义的行为。通常,它们不会导致异常,并且没有通用的方法来处理它们。(您通常可以安排在发生此类事情时获取信号,但是您可以从信号处理程序中做的事情很少,这并没有太大帮助。)通常,您想要的是让程序崩溃,因为您不知道它处于什么状态。在极少数情况下并非如此,您必须依靠实现定义的扩展(如果存在)。如果你用/EHa 例如,使用 VC++ 的选项,通常会发生崩溃的情况将被转换为 C++ 异常。但那是一个 VC++ 扩展,发生这种情况时你仍然不知道程序的整体状态。如果是因为您破坏了可用空间领域,那么即使您捕获了异常,您也可能无能为力(当您展开堆栈时,您很有可能会从试图释放内存的析构函数中获得另一个异常) .

于 2013-03-31T15:36:11.427 回答
2

我知道这个线程很旧。但如果其他人正在阅读本文,他应该知道 unique_ptr。shared_ptr 确实有开销。计数器存储在堆上。每次访问计数器时,都存在处理器缓存不匹配的风险。unique_ptr 受到更多限制,但与普通指针相比没有开销。我的建议是当您不需要引用计数时,更喜欢 unique_ptr 而不是 shared_ptr。另一个重要的注意事项是,unique_ptr 可以很好地处理数组。如果我没记错的话,这对于自 C++17 以来的 shared_ptr 也是如此。

于 2017-06-17T20:27:13.977 回答
1

智能指针是避免此类问题的更好选择(但您在使用它们之前也必须完全了解),但我想提一下与智能指针相关的性能限制,原因是它们通常使用原子操作,例如 Win32 API 中的 InterlockedIncrement 供参考数数。这些函数比普通整数算术要慢得多。我不确定在您的情况下是否可以接受这么小的性能损失。

我通常做的是(所以我不必再花几天时间来调试讨厌的错误),我在设计和对象生命周期上花费大量时间,然后再进行实际编码,因为我删除了我专门设置指针的内存NULL,就我而言,这是一个很好的做法。再次,也许真正的解决方案是在继续之前花更多时间来确定依赖关系和对象生命周期!

于 2013-03-31T15:30:38.173 回答