我们先来看一个代码,这是在继承与虚函数学生过程中发生的一个错误,涉及到了C++的对象内存的知识,因为这方面知识比较复杂,这里不做过多的介绍,只简单分析一下出错原因。
class Base
{
public:
void fun()
{
Cout << “Base::fun()” << endl;
}
~Base() //虚析构函数
{
cout << ”Base::~Base” << endl;
}
};
class Child:public Base //继承Base类
{
Public:
virtual void fun() //虚函数
{
Cout << “Child:fun()” <<endl;
}
};
int main()
{
Base *p = new Child; //新建一个子类的对象,赋值给一个父类指针
p->fun();
delete p; //通过父类指针释放内存
return 0;
}
VS运行程序时,发生如下错误:
通过提示发现,VS提示应该是内存方面的错误。而且对于上面的代码来说,父类中的fun函数不是虚函数,而子类中的fun函数是虚函数,所以p->fun也是会调用父类的fun函数。
那么为什么会出现内存释放的错误呢?重新写一个main函数如下:
int main()
{
Child *c = new Child; //在堆上创建一个子类对象
Base *p = c; //将子类对象指针赋值给一个父类对象指针
cout << c << " " << p << endl; //打印信息
p->fun(); //通过父类指针来调用fun函数
delete p; //通过父类指针释放内存
return 0;
}
再次运行以上代码,结果如下:
我们从打印的信息可以看到,Child对象指针值为0x01393FD0,Base对象指针为0x1393FD4,通过打印信息发现两个值并不一样,这样释放内存时产生了错误。
原因如下:
- 存在虚函数的类对象中会隐藏了一个虚指针,虚指针存放在对象内存的开始位置,这里子类中有一个虚指针而父类中没有;
- 子类中有一块内存布局和父类对象的内存布局是一样的,但是这块内存肯定不是子类对象的起始位置,所以将子类对象指针赋值给一个父类对象指针时,为了操作上不产生错误,会把这块和父类内存布局相同的位置赋值给父类指针,因而发生了内存的偏移;
- 在堆上分配的子类对象内存,如上面代码起始地址是0x01393FD0,这里是通过delete父类指针来释放内存,而父类指针的值为0x1393FD4,这样释放内存中检测不是正确的起始位置而发生了错误。
解决方法:
要解决以上问题,就要想办法让子类指针赋值给父类指针时,两个指针的值是一个样,这里我们可以在父类中设置任意一个虚函数(将父类中的fun设置为虚函数或者将父类的析构函数设置为虚函数),这样父类和子类中都有虚指针,赋值时不会发生地址的偏移。
为了防止出现以上错误,我们代码中一定要多加注意。尽量不要在子类中设置虚函数和父类中的普通函数重名,另外还有一个编码小技巧,如果一个类中有虚函数或者纯虚函数时,要将其析构函数设置为虚析构函数,以防发生内存泄漏等问题。
本文版权归传智播客C++培训学院所有,欢迎转载,转载请注明作者出处。谢谢!
作者:传智播客C/C++培训学院
首发:http://www.itcast.cn/c/