首頁技術文章正文

C/C++培訓之繼承與虛函數(shù)結合過程中的內存釋放問題

更新時間:2017-04-14 來源: 黑馬程序員C/C++培訓學院 瀏覽量:

我們先來看一個代碼,這是在繼承與虛函數(shù)學生過程中發(fā)生的一個錯誤,涉及到了C++的對象內存的知識,因為這方面知識比較復雜,這里不做過多的介紹,只簡單分析一下出錯原因。
class Base
{
public:
void fun()
{
Cout << “Base::fun()” << endl;
}
~Base() //虛析構函數(shù)
{
cout << ”Base::~Base” << endl;
}
};
class Child:public Base //繼承Base類
{
Public:
virtual void fun() //虛函數(shù)
{
Cout << “Child:fun()” <<endl;
}
};
int main()
{
Base *p = new Child; //新建一個子類的對象,賦值給一個父類指針
p->fun();
delete p; //通過父類指針釋放內存
return 0;
}
VS運行程序時,發(fā)生如下錯誤:
 
通過提示發(fā)現(xiàn),VS提示應該是內存方面的錯誤。而且對于上面的代碼來說,父類中的fun函數(shù)不是虛函數(shù),而子類中的fun函數(shù)是虛函數(shù),所以p->fun也是會調用父類的fun函數(shù)。
那么為什么會出現(xiàn)內存釋放的錯誤呢?重新寫一個main函數(shù)如下:
int main()
{
Child *c = new Child; //在堆上創(chuàng)建一個子類對象
Base *p = c; //將子類對象指針賦值給一個父類對象指針
cout << c << " " << p << endl; //打印信息
p->fun(); //通過父類指針來調用fun函數(shù)
delete p; //通過父類指針釋放內存
return 0;
}
再次運行以上代碼,結果如下:
 
我們從打印的信息可以看到,Child對象指針值為0x01393FD0,Base對象指針為0x1393FD4,通過打印信息發(fā)現(xiàn)兩個值并不一樣,這樣釋放內存時產生了錯誤。
原因如下:
  1. 存在虛函數(shù)的類對象中會隱藏了一個虛指針,虛指針存放在對象內存的開始位置,這里子類中有一個虛指針而父類中沒有;
  2. 子類中有一塊內存布局和父類對象的內存布局是一樣的,但是這塊內存肯定不是子類對象的起始位置,所以將子類對象指針賦值給一個父類對象指針時,為了操作上不產生錯誤,會把這塊和父類內存布局相同的位置賦值給父類指針,因而發(fā)生了內存的偏移;
  3. 在堆上分配的子類對象內存,如上面代碼起始地址是0x01393FD0,這里是通過delete父類指針來釋放內存,而父類指針的值為0x1393FD4,這樣釋放內存中檢測不是正確的起始位置而發(fā)生了錯誤。
解決方法:
要解決以上問題,就要想辦法讓子類指針賦值給父類指針時,兩個指針的值是一個樣,這里我們可以在父類中設置任意一個虛函數(shù)(將父類中的fun設置為虛函數(shù)或者將父類的析構函數(shù)設置為虛函數(shù)),這樣父類和子類中都有虛指針,賦值時不會發(fā)生地址的偏移。
為了防止出現(xiàn)以上錯誤,我們代碼中一定要多加注意。盡量不要在子類中設置虛函數(shù)和父類中的普通函數(shù)重名,另外還有一個編碼小技巧,如果一個類中有虛函數(shù)或者純虛函數(shù)時,要將其析構函數(shù)設置為虛析構函數(shù),以防發(fā)生內存泄漏等問題。



本文版權歸黑馬程序員C/C++培訓學院所有,歡迎轉載,轉載請注明作者出處。謝謝!
作者:黑馬程序員C/C++培訓學院
首發(fā):http://m.3rdspacecomics.com/news/c.html 
分享到:
在線咨詢 我要報名
和我們在線交談!