C++解析(25):關於動態內存分配、虛函數和繼承中強制類型轉換的疑問
阿新 • • 發佈:2018-12-09
cas ror src 一個 聲明 eof struct 定義 namespace
0.目錄
1.動態內存分配
- 1.1 new和malloc的區別
- 1.2 delete和free的區別
2.虛函數
- 2.1 構造函數與析構函數是否可以成為虛函數?
- 2.2 構造函數與析構函數是否可以發生多態?
3.繼承中的強制類型轉換
4.小結
1.動態內存分配
1.1 new和malloc的區別
new關鍵字與malloc函數的區別:
- new關鍵字是C++的一部分
- malloc是由C庫提供的函數
- new以具體類型為單位進行內存分配
- malloc以字節為單位進行內存分配
- new在申請內存空間時可進行初始化
- malloc僅根據需要申請定量的內存空間
下面的代碼輸出什麽?為什麽?
示例——new和malloc的區別:
#include <iostream>
#include <cstdlib>
using namespace std;
class Test
{
public:
Test()
{
cout << "Test::Test()" << endl;
}
};
int main()
{
Test* pn = new Test;
Test* pm = (Test*)malloc(sizeof(Test));
return 0;
}
運行結果為:
[root@bogon Desktop]# g++ test.cpp [root@bogon Desktop]# ./a.out Test::Test()
new和malloc的區別:
- new在所有C++編譯器中都被支持
- malloc在某些系統開發中是不能調用
- new能夠觸發構造函數的調用
- malloc僅分配需要的內存空間
- 對象的創建只能使用new
- malloc不適合面向對象開發
1.2 delete和free的區別
下面的代碼輸出什麽?為什麽?
示例——delete和free的區別:
#include <iostream> #include <cstdlib> using namespace std; class Test { int* mp; public: Test() { cout << "Test::Test()" << endl; mp = new int(100); cout << *mp << endl; } ~Test() { delete mp; cout << "~Test::Test()" << endl; } }; int main() { Test* pn = new Test; Test* pm = (Test*)malloc(sizeof(Test)); free(pn); free(pm); return 0; }
運行結果為:
[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Test::Test()
100
可以看到,free不會觸發析構函數,會造成內存泄漏!
delete和free的區別:
- delete在所有C++編譯器中都被支持
- free在某些系統開發中是不能調用
- delete能夠觸發析構函數的調用
- free僅歸還之前分配的內存空間
- 對象的銷毀只能使用delete
- free發不適合面向對象開
2.虛函數
2.1 構造函數與析構函數是否可以成為虛函數?
構造函數是否可以成為虛函數?析構函數是否可以成為虛函數?
構造函數不可能成為虛函數:
- 在構造函數執行結束後,虛函數表指針才會被正確的初始化
析構函數可以成為虛函數:
- 建議在設計類時將析構函數聲明為虛函數
示例——不把析構聲明為虛函數:
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
~Base()
{
cout << "~Base()" << endl;
}
};
class Derived : public Base
{
public:
Derived()
{
cout << "Derived()" << endl;
}
~Derived()
{
cout << "~Derived()" << endl;
}
};
int main()
{
Base* p = new Derived();
cout << endl;
delete p;
return 0;
}
運行結果為:
[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Base()
Derived()
~Base()
由於沒有把析構聲明為虛函數,因此,編譯器直接根據指針p的類型來決定調用哪一個析構函數,又由於指針p的類型是父類的,所以編譯器認為直接調用父類的析構函數就可以了。
示例——把析構聲明為虛函數:
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
virtual ~Base()
{
cout << "~Base()" << endl;
}
};
class Derived : public Base
{
public:
Derived()
{
cout << "Derived()" << endl;
}
~Derived()
{
cout << "~Derived()" << endl;
}
};
int main()
{
Base* p = new Derived();
cout << endl;
delete p;
return 0;
}
運行結果為:
[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Base()
Derived()
~Derived()
~Base()
如果將構造函數聲明為虛函數,報錯信息如下:
error: constructors cannot be declared virtual
2.2 構造函數與析構函數是否可以發生多態?
構造函數中是否可以發生多態?析構函數中是否可以發生多態?
構造函數中不可能發生多態行為:
- 在構造函數執行時,虛函數表指針未被正確初始化
析構函數中不可能發生多態行為:
- 在析構函數執行時,虛函數表指針已經被銷毀
構造函數和析構函數中不能發生多態行為,只調用當前類中定義的函數版本!!
示例——構造函數和析構函數中不可能發生多態:
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base()" << endl;
func();
}
virtual void func()
{
cout << "Base::func()" << endl;
}
virtual ~Base()
{
func();
cout << "~Base()" << endl;
}
};
class Derived : public Base
{
public:
Derived()
{
cout << "Derived()" << endl;
func();
}
virtual void func()
{
cout << "Derived::func()" << endl;
}
~Derived()
{
func();
cout << "~Derived()" << endl;
}
};
int main()
{
Base* p = new Derived();
cout << endl;
delete p;
return 0;
}
運行結果為:
[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Base()
Base::func()
Derived()
Derived::func()
Derived::func()
~Derived()
Base::func()
~Base()
3.繼承中的強制類型轉換
繼承中如何正確的使用強制類型轉換?
dynamic_cast是與繼承相關的類型轉換關鍵字
dynamic_cast要求相關的類中必須有虛函數
用於有直接或者間接繼承關系的指針(引用)之間
- 指針:
- 轉換成功:得到目標類型的指針
- 轉換失敗:得到一個空指針
- 引用:
- 轉換成功:得到目標類型的引用
- 轉換失敗:得到一個異常操作信息
編譯器會檢查dynamic_cast的使用是否正確
類型轉換的結果只可能在運行階段才能得到
示例——dynamic_cast的使用:
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base::Base()" << endl;
}
virtual ~Base()
{
cout << "Base::~Base()" << endl;
}
};
class Derived : public Base
{
};
int main()
{
Base* p = new Base;
Derived* pd = dynamic_cast<Derived*>(p);
if( pd != NULL )
{
cout << "pd = " << pd << endl;
}
else
{
cout << "Cast error!" << endl;
}
delete p;
return 0;
}
運行結果為:
[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Base::Base()
Cast error!
Base::~Base()
4.小結
- new / delete會觸發構造函數或者析構函數的調用
- 構造函數不能成為虛函數
- 析構函數可以成為虛函數
- 構造函數和析構函數中都無法產生多態行為
- dynamic_cast是與繼承相關的專用轉換關鍵字
C++解析(25):關於動態內存分配、虛函數和繼承中強制類型轉換的疑問