错误:"被释放的指针未被分配"关于虚拟析构函数

时间:2014-11-28 02:36:48

标签: c++ memory-management virtual

以下是产生错误的程序:

#include <iostream>
#include <string>
using namespace std;

class Pet {
protected:
    string name;
public:
    Pet(string nam):name(nam){}
    ~Pet(){cout<< "Pet destroyed" << endl;} // attention
    void play();
};

void Pet::play(){
    std::cout<< "Pet " << name <<" is playing ..." <<endl;
}

class Cat : public Pet {
public:
    Cat(string nam):Pet(nam){}
    ~Cat(){cout<< "Cat destroyed" <<endl;}
    void play();
};

void Cat::play(){
    cout<< "cat " << name <<" is playing ..." <<endl;
}

class Dog : public Pet {
public:
    Dog(string nam):Pet(nam){}
    ~Dog(){cout<< "Dog destroyed" <<endl;}
    virtual void play(); // attention
};

void Dog::play(){
    cout<< "dog " << name <<" is playing ..." <<endl;
}

int main(void) {
    Pet *cat1 = new Cat("Kitty");
    Pet *dog1 = new Dog("Tom");
    Pet *pet = new Pet("Nicky");

    cat1->Pet::play();
    dog1->play();
    pet->play();

    delete cat1;
    delete pet;
    delete dog1;
    return 0;
}

这个程序编译得很好,但是当我运行它时,我得到了以下输出:

Pet Kitty is playing ...
Pet Tom is playing ...
Pet Nicky is playing ...
Pet destroyed
Pet destroyed
Pet destroyed
t(35634,0x7fff7ae04310) malloc: *** error for object 0x7f8623c03b68: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

错误信息之前的结果才有意义,但我无法理解为什么会出现此错误。错误在行delete dog1;处产生。 0x7f8623c03b68是对象dog1的地址。由于dog1->play();只是执行正常,因此应该很好地分配对象dog1的内存空间。为什么错误说“指针被释放没有分配”???

当我将类Pet的析构函数更改为虚拟时,程序执行正常且没有错误。为什么???这是输出:

Pet Kitty is playing ...
Pet Tom is playing ...
Pet Nicky is playing ...
Cat destroyed
Pet destroyed
Pet destroyed
Dog destroyed
Pet destroyed

在这种情况下,当删除dog1时,Dog的析构函数将首先被调用,然后是Pet的析构函数。那么在这两种情况下,“虚拟”这个词在内存上的变化有什么不同呢?

在这篇文章中 Why should I declare a virtual destructor for an abstract class in C++? 它解释了当基类的析构函数未声明为虚拟时,当删除引用子类对象的基类类型的指针时,将生成未定义的行为。那么这个错误只是所谓的Undefined行为的一个例子吗?因为当我从类Dog中删除虚拟时,程序执行得很好。

我注意到,如果这是一个重复的问题,就像上面提到的问题一样。只是想确定并找到该错误的原因。

欢迎任何指示。感谢。

2 个答案:

答案 0 :(得分:2)

delete cat1;导致未定义的行为,因为cat1具有类型Pet *,但是Pet没有虚拟析构函数,并且指针实际指向派生的实例类。

当行为未定义时,任何事情都可能发生。要解决此问题,您需要virtual ~Pet(),如您所述。

我猜测malloc错误与Dog的类布局有关:Dog需要一个vtable,所以可能分配了一个更大的区域,它没有从{{1}开始的同一地址开始}。 (熟悉vtable布局的人可能会证实这一点)

答案 1 :(得分:0)

When I change the destructor of class Pet to be virtual, the program executes fine without errors. Why???

因为只有在析构函数是虚拟的时候,标准才能保证正确的行为:

  

5.3.5删除

     
      
  1. 在第一个替代(删除对象)中,如果是静态类型的   操作数与其动态类型不同,静态类型应为操作数动态类型的基类,静态类型应具有虚拟析构函数 或行为未定义
  2.