还原virtual函数的本质-----C++
當你每次看到C++類中聲明一個virtual函數(shù),特別是看到了一個virtual的虛構(gòu)函數(shù)。你知道它的意思嗎?你肯定會毫不猶豫的回答:不就是多態(tài)么。。。在運行時確定具體的行為么。。。完全正確,但這里我要講的不只是這些東西。
?有些類需要虛函數(shù),有些不需要虛函數(shù)。這是為什么,一般你看到的類如果有一個虛析構(gòu)函數(shù),那么這個類中應(yīng)該會有至少一個是虛函數(shù)的。。這是為什么呢??如果我們類中沒有用其他虛函數(shù)的話,你創(chuàng)建了這個也是多余的,而且會增加類對象的大小。。說這些純理論的東西,也許大家不知所云。。下面我就給例子來驗證。。
1:
?
class A { public:A(){}; // virtual ~A(){};~A(); }; void main() {A a;cout<<sizeof(a)<<endl; }
結(jié)果為1。這個1應(yīng)該是編譯器自己為它加上的。。哪怕你不在類中不寫任何東西,它也是1;例如;
?
?
?
class A {}; void main() {A a;cout<<sizeof(a)<<endl; }
如果你把析構(gòu)函數(shù)聲明為虛函數(shù)。。如:
?
?
2:
class A
{
public:
A(){};
virtual ~A(){};
//~A();
};
void main()
{
A a;
cout<<sizeof(a)<<endl;
}
結(jié)果是4。先不說這是為什么。。
然后還是說一下關(guān)于虛函數(shù)基礎(chǔ)的東西(多態(tài))吧,也給個例子:
?
#include<iostream> #include<string> using namespace std;class Base { public:Base();virtual ~Base();virtual void test(); private :int count; }; Base::Base(){cout<<"Base部分創(chuàng)建了"<<endl; } Base::~Base(){cout<<"Base部分被銷毀了"<<endl; } void Base::test() {cout<<"Base Test"<<endl;}class Derive1:public Base { public:Derive1();virtual ~Derive1();void test();}; Derive1::Derive1(){cout<<"子類部分創(chuàng)建了"<<endl; } Derive1::~Derive1(){cout<<"子類部分被銷毀了"<<endl; } void Derive1::test() {cout<<"Derive1 Test"<<endl; }void main() {Base* d1=new Derive1();d1->test();delete d1;}?
?
由此看見,當通過聲明一個父類指針并且讓它指向一個子類的對象,在子對象創(chuàng)建的時候,會先去調(diào)用父類的構(gòu)造函數(shù),然后再是自己的構(gòu)造函數(shù),當通過父類指針去調(diào)用一個虛函數(shù)test()時,它實際上回去調(diào)用子類的test()函數(shù),這是為什么呢,它肯定有什么信息讓它這樣做嗎。。這個信息肯定是子類對象給它的。。這個信息就是虛函數(shù)指針(vptr),它指向一個虛函數(shù)表(vtbl),這個虛函數(shù)表其實就是包含了這個類的所有虛函數(shù)的函數(shù)名(函數(shù)指針),每個類就只包含了那一個虛函數(shù)指針和它的一些成員變量。這下可以解釋上面為什么是1,為什么是4了。。
?在win32的機器上,每個指針是4字節(jié)。剛才也提到每個類的大小取決于兩部分,一個是成員變量,一個是虛函數(shù)指針而且有且只有一個,在例子一中,因為沒有成員變量,而有一個虛函數(shù)---析構(gòu)函數(shù),此時肯定會有一個虛函數(shù)指針,所以是4。。 其實剛才也就同時說清楚了多態(tài)的本質(zhì),就是子對象的虛函數(shù)指針給出了這個信息,父類指針才知道去執(zhí)行哪個函數(shù)。。
最后一個問題:為什么析構(gòu)函數(shù)要聲明為虛函數(shù)呢?(當至少有一個為虛函數(shù)的時候)
從剛才的那個結(jié)果也可以看出,當我們delete那個指針的時候,會發(fā)生析構(gòu),而且這個過程是從子類到父類的順序進行。假如此時析構(gòu)函數(shù)不為虛函數(shù),父類指針也就不知道去執(zhí)行子類的析構(gòu)函數(shù)。。也就不會去釋放子對象的那部分內(nèi)存,造成內(nèi)存泄漏。。例如:(這里我們只是對上一段代碼進行修改,去掉了父類中的virtual):
?
#include<iostream> #include<string> using namespace std;class Base { public:Base();~Base();virtual void test(); private :int count; }; Base::Base(){cout<<"Base部分創(chuàng)建了"<<endl; } Base::~Base(){cout<<"Base部分被銷毀了"<<endl; } void Base::test() {cout<<"Base Test"<<endl;}class Derive1:public Base { public:Derive1();~Derive1();void test();}; Derive1::Derive1(){cout<<"子類部分創(chuàng)建了"<<endl; } Derive1::~Derive1(){cout<<"子類部分被銷毀了"<<endl; } void Derive1::test() {cout<<"Derive1 Test"<<endl; }void main() {Base* d1=new Derive1();d1->test();delete d1;}?
?
所以當至少有一個虛函數(shù)的話,我們也要把它的析構(gòu)函數(shù)聲明為virtual。(插一句:有些人會說你子類中的那個函數(shù)哪里是虛函數(shù)哦,我沒看到virtual 啊。。其實C++允許我們這樣做,重寫父類的虛函數(shù),不是必須要聲明出來的。)
總結(jié):一個類有了虛函數(shù),是為了成為一個基類,如果不是這樣的話,那么父類中的任何函數(shù)都沒有必要是虛函數(shù),甚至?xí)黾宇惖拇笮 6鄳B(tài)告訴了我們這點。。一旦成為了基類,那么就要把析構(gòu)函數(shù)聲明為一個虛函數(shù)。。
好了,虛函數(shù)的內(nèi)容就Over了。。。。。
?
轉(zhuǎn)載于:https://www.cnblogs.com/pangblog/p/3258031.html
總結(jié)
以上是生活随笔為你收集整理的还原virtual函数的本质-----C++的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020寒假 08
- 下一篇: MVC-05 Model(1)