3

我在 C++ 中看到很多类似下面示例的代码,通常建议将其作为约定:

class Foo
{
public:

    Foo()
    {
        bar = new int[100];
        size = 100;
    }

    // ... copy/assignment stuff ...

    ~Foo()
    {
        if (bar) // <--- needed?
        {
            delete[] bar;
            bar = nullptr; // <--- needed?
        }

        size = 0; // <--- needed?
    }

private:

    int* bar;
    int size;
};

对我来说,三个语句if (bar) { ... }bar = nullptr;size = 0;是多余的,原因有两个:

  1. delete nullptr;非常安全,它什么也没做。
  2. 如果对象被销毁并且内存被释放,我不应该担心设置bar为0nullptrsize0 的安全性。

这些理由正确吗?这些陈述真的是多余的吗?如果是这样,为什么人们继续使用和建议它们?我希望看到一些可以通过保持这个约定来解决的潜在问题。

4

4 回答 4

6

你是对的,这些不是必需的,一些编译器无论如何都会优化它们。

然而——人们通常这样做的原因是为了帮助发现问题。例如,假设您没有将指针设置为 null。该对象被销毁,但随后您错误地尝试访问(曾经是)指针。由于运行时可能不会清除它,您仍然会在那里看到有效的东西。这仅对调试有价值,并且仍然是未定义的行为,但有时会有所回报。

于 2013-07-18T15:59:35.480 回答
4

这是哪里usually recommended as a convention

通常建议不要手动管理内存分配。如果您使用std::vector(或如果您真的想要智能指针)来实现这一点,那么所有问题都会完全消失,您根本不需要编写析构函数。

但是,如果您真的坚持这样做(并且为了正确性,您已经编写了未向我们展示的复制构造函数/复制赋值),那么我发现析构函数中的额外工作实际上隐藏了真正发生的事情并且提供的很少以牺牲代码混淆为代价。

于 2013-07-18T16:18:05.823 回答
0

正如许多其他答案中提到的那样,这if(bar)是相当愚蠢的。

但是,设置释放的指针nullptr和重置size = 0有其用途。当您拥有虚拟构造函数、虚拟析构函数和类层次结构时,其中一些冗余在 OOP 中是很好的。在析构函数期间,如果子类释放指针但未将其设置为nullptr,然后基类也尝试释放它,会发生什么情况?在类似的情况下,如果基类假定指针有效 if会发生size > 0什么?

像这样的小记账细节会产生非常难以调试的细微错误。在某些情况下可能会显得迂腐,让编译器带走不必要的东西。

于 2013-07-18T16:14:22.980 回答
0

你应该使用delete[](数组)

确实if是没用的。性能方面的影响通常可以忽略不计。当你调试时,你有时会很高兴让事情处于安全状态(比如防止查看一些释放的内存并挠头)。当它隐藏在树的深处时,它会有所帮助(至少重新初始化大小和栏)。

顺便说一句,更好的写作方式是(RAII:http ://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization )

Foo(): 
  bar (new int[100]),
  size(100)
{
}

编辑

  • 旧习惯很难改掉,为此您应该使用 ascoped_ptr并对此感到满意(甚至更好, a vector)并杀死普通的旧愚蠢指针。
  • 年长的人写了太多的 C 或在太多的语言之间交换:便宜的时候安全总比后悔好
于 2013-07-18T16:02:35.057 回答