以下是产生错误的程序:
#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中删除虚拟时,程序执行得很好。
我注意到,如果这是一个重复的问题,就像上面提到的问题一样。只是想确定并找到该错误的原因。
欢迎任何指示。感谢。
答案 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删除
- 在第一个替代(删除对象)中,如果是静态类型的 操作数与其动态类型不同,静态类型应为操作数动态类型的基类,静态类型应具有虚拟析构函数 或行为未定义。
醇>