【问题标题】:Why does calling the Base class destructor crash this program?为什么调用基类析构函数会导致程序崩溃?
【发布时间】:2015-04-29 12:13:24
【问题描述】:
class ParentClass {
protected:
    int* intArray;

public:
    ~ParentClass(){
        delete [] intArray;
    }
};

class ChildClass : public ParentClass {
public:
    ChildClass() : ParentClass() {
        intArray = new int[5];
    }
};

int main(int argc, const char * argv[]) {
    ChildClass child;
    child.~ChildClass(); //This line crashes the program. why??
}

它抛出的具体错误: 初始化(37640,0x7fff78623300)malloc:* 对象 0x100100aa0 的错误:未分配被释放的指针 * 在 malloc_error_break 中设置断点进行调试

指针正在引用ParentClass中声明的intArray,错误指出内存没有分配,但它是在ChildClass构造函数中分配的。

有人能解释一下产生这个错误的过程是什么吗?

【问题讨论】:

  • 您确定是 that 行,而不是变量 child 超出范围并使其析构函数 再次调用
  • 是指定的行导致程序崩溃,还是该行的存在导致程序崩溃?

标签: c++


【解决方案1】:

问题不在于intArray 未分配,而在于您将其释放了两次。

ChildClass child; 实例化ChildClass 实例并调用分配intArray 的默认构造函数,没问题。

然后您的代码显式调用析构函数(对于堆栈分配/自动对象,您通常不需要这样做)。

然后编译器在范围清理期间插入另一个对析构函数的调用,这导致delete[]被第二次调用,这是不正确的,并导致崩溃。您的调试器可能会报告函数的最后一行(显式析构函数调用所在的位置),而它确实应该指向右大括号。

可以肯定的是,在你的析构函数中设置一个断点并运行你的程序,看看它被命中了多少次。

【讨论】:

  • 编译器可能child的自动销毁移动到报告它的确切位置,因为child从未在此程序中实际有效使用。
  • 感谢您的明确回复。断点清除了一切!
【解决方案2】:

您遇到了未定义的行为。来自 C++ 标准:

一旦为对象调用析构函数,该对象就不再存在;如果 为生命周期已结束的对象调用析构函数(3.8)。 [示例:如果析构函数用于自动 对象被显式调用,并且块随后以通常会的方式离开 调用对象的隐式销毁,行为未定义。

【讨论】:

    【解决方案3】:

    childmain() 的本地对象。离开函数范围时它会自动销毁。

    不幸的是,您在离开函数之前通过显式调用析构函数手动销毁它。所以它被摧毁了两次(一次太多):第二次它崩溃了!

    您不必销毁本地对象。仅当您使用new 动态分配对象时,才需要对指针进行显式销毁。但是你应该使用delete 删除它们。

    析构函数的显式调用仅在极少数情况下相关:当您希望通过 placement-new 重用动态对象的存储时。

    备注:ParentClass 中,您应该将intArray 初始化为nullptr。这将确保如果意外没有分配,delete 不会尝试解除分配一个单元化的指针。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-09-08
      • 1970-01-01
      • 2011-06-12
      • 2021-12-27
      • 2020-05-26
      • 1970-01-01
      • 2014-10-05
      相关资源
      最近更新 更多