A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 倾心莫若初见 于 2016-10-18 17:00 编辑

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


0 个回复

您需要登录后才可以回帖 登录 | 加入黑马