所謂虛函數是指:html
在類中被聲明爲virtual的成員,基類但願這種成員在派生類中重定義。除了構造函數外,任意非static成員均可覺得虛成員。保留字 virtual 只在類內部的成員函數聲明中出現,不能用在類定義體外部出如今函數定義上。ios
protected標號:less
protected爲受保護的訪問標號,protected成員能夠被該類的成員、友元和派生類成員(非友元)訪問,而不能夠被該類型的普通用戶訪問。函數
而private成員,只能被基類的成員和友元訪問,派生類不能訪問。佈局
派生類只能經過派生類對象訪問其基類的protected成員,派生類對其基類類型對象的protected成員沒有特殊訪問權限。post
派生類中虛函數的聲明必須與基類中的定義方式徹底匹配,但有一個例外:返回對基類型的引用(或指針)的虛函數。派生類中的虛函數能夠返回基類函數所返回類型的派生類的引用(或指針),或返回基類類型的引用(或指針);this
一旦函數在基類中聲明爲虛函數,它就一直爲虛函數,派生類沒法改變該函數爲虛函數的這一事實。派生類重定義虛函數時,可使用 virtual 保留字,可是不是必須這樣作。url
已定義的類才能夠作基類。若是已經聲明瞭一個類,可是沒有定義它,則不能用做基類。spa
若是需聲明(但不實現)一個派生類,則聲明包含類名但不包含派生列表。例如,下邊的前向聲明會致使編譯時錯誤:3d
//error:a forward declaration must not include the derivation list class Bulk_item:public Item_base;
正確的前向聲明爲:
//forward declarations of both derived and nonderived class class Bulk_item; class Item_base;
C++中的函數調用默認不使用動態綁定。要觸發動態綁定,必須知足兩個條件:
第一:只有指定爲虛函數的成員函數才能進行動態綁定,成員函數默認爲非虛函數,非虛函數不進行動態綁定;
第二:必須經過基類類型的引用或指針進行函數調用。
派生類虛函數調用基類版本時,必須顯示使用做用與操做符。若是派生類函數忽略了這樣作,則函數調用會在運行時肯定而且將是一個自身調用,從而致使無窮遞 歸。(只有成員函數中的代碼才應該使用做用域操做符覆蓋虛函數機制)例如:
1 // Bulk_item 是 Item_base 的派生類 2 Bulk_item derived; 3 Item_base *baseP = &derived; 4 //calls version from the base class regardless of the dynamic type of baseP 5 double d = baseP->Item_base::net_price(42); //顯示調用做用域操做符
注意:虛函數也能夠有默認實參。一般,若是有用在給定調用中的默認實參值,該值將在 編譯(註定不是動態綁定) 時肯定。若是調用省略了具備默認值的實參,則所用的值由調用該函數的類型定義,與對象的動態類型無關。經過基類的引用或指針調用虛函數時,默認實參爲在基類虛函數聲明中指定的值,若是經過派生類的指針或引用調用虛函數,則默認實參是在派生類的版本中聲明的值。在同一虛函數的基類版本和派生類版本中使用不一樣的默認實參幾乎必定會引發麻煩。若是經過基類的引用或指針調用虛函數,但實際執行的是派生類中定義的版本,這時就可能會出現問題。在這種狀況下,爲虛函數的基類版本定義的默認實參將傳給派生類定義的版本,而派生類版本是用不一樣的默認實參定義的。
注意理解下邊的代碼:
1 /* 2 不管派生列表中是什麼訪問標號,全部繼承 Base 的類對 Base 中的成員 3 具備相同的訪問。派生訪問標號將控制派生類的用戶對從Base繼承而來的 4 成員的訪問: 5 */ 6 class Base 7 { 8 public: 9 void basemem(); 10 protected: 11 int i; 12 // ... 13 }; 14 15 struct Public_derived : public Base 16 { 17 int use_base() 18 { 19 return i; // OK:derived classes can access i 20 } 21 }; 22 23 struct Private_derived : private Base 24 { 25 int use_base() 26 { 27 return i; // OK:derived classes can access i,這裏能夠訪問 28 } 29 }; 30 31 int main() 32 { 33 Base b; 34 Public_derived d1; 35 Private_derived d2; 36 b.basemem(); // ok: basemem is public 37 d1.basemem(); // ok: basemem is public in the derived class 38 //d2.basememe(); // error: basemem is private in the derived class 39 }
從Public——derived 派生的類能夠訪問來自Base類的 i, 是由於該成員在Public_derived 中仍爲 protected 成員。從 Private_derived 派生的類沒 有這樣的訪問,對它們而言,Private_derived 從 Base 繼承的全部成員均爲 private。
派生類不能訪問基類的 private 成員,也不能使本身的用戶可以訪問那些成員。若是基類成員爲 public 或 protected,則派生類列表中使用的訪問標號決定該成員在派生類中的訪問級別:
一、若是是公有繼承,基類成員保持本身的訪問級別:基類的public成員爲派生類的public成員,基類的protected成員爲派生類的protected成員;
二、若是是受保護繼承,基類的public和protected成員在派生類中爲protected成員;
三、若是是私有繼承,基類的全部成員在派生類中爲private成員。
在派生類構造函數中使用默認實參:
1 // 不使用折扣策略的基類 2 class Item_base 3 { 4 public: 5 Item_base(const std::string &book = "", double sales_price = 0.0) : isbn(bool), price(sales_price){} 6 7 std::string book() const 8 { 9 return isbn; 10 } 11 12 // 返回特定購書量的總價格 13 // 派生類將重載該函數以應用不一樣的折扣策略 14 virtual double net_price(size_t n) const 15 { 16 return n * price; 17 } 18 19 virtual ~Item_base(){} 20 21 private: 22 std::string isbn; 23 protected: 24 double price; 25 }; 26 27 //批量購買折扣類 28 class Bulk_item : public Item_base 29 { 30 public: 31 Bulk_item(const std::string& book = "", double sales_price = 0.0, 32 size_t qty = 0, double disc_rate = 0.0) : 33 Item_base(book, sales_price), min_qty(qty), discount(disc_rate){} 34 35 // 重定義基類版本以實現批量購買折扣策略:若購買量高於下限,則使用折扣價格 36 double net_price(size_t cnt) const 37 { 38 if(cnt >= min_qty) 39 return cnt * (1 - discount) * price; 40 else 41 return cnt * price; 42 } 43 private: 44 size_t min_qty; // 可打折的最小購買量 45 double discount; // 折舊率 46 };
下邊是轉載的內容:http://www.cnblogs.com/bakari/archive/2012/08/12/2635369.html
虛函數是C++中很是重要的一個概念,它最大的好處是可以觸發動態綁定。C++中的函數默認不使用動態綁定,要觸發動態綁定,必須知足 兩個條件:
第一,只有指定爲虛函數的成員函數才能進行動態綁定,成員函數默認爲非虛函數,非虛函數不進行動態綁定;
第二,必須經過基類類型的指針或引用進行函數的調用。具體理解指針或引用在使用繼承層次中某一類型的對象時會發生什麼,本文不展開討論,
這兩天主要研習了虛函數的具體應用這一塊,而它的應用又很是普遍,學MFC的應該可以感覺到它的強大,要說是總結也不必定可以總結全,本人目前也處在studying中,因此用10個具體的例子來講明。例子是從難 到易,看到的朋友若是懂前面的能夠不用看後面的。每個例子就是一個類,經過類在內存中的佈局來形象地分析虛函數到底是如何運做的。圖表示可能抽象一點,通常帶有V開頭的表示一個虛函數表,若是是學過編譯原理這門課就很容易看懂,沒學過的只要懂虛函數的一些機制,耐着性子也是沒問題的。每一個圖示都配有相應的代碼。能夠對照着代碼來看。
一、 虛函數繼承的複雜例子
二、 菱形繼承無虛擬繼承的狀況
三、 虛擬繼承的簡單狀況
四、 單一普通繼承(無虛函數)
五、 單一繼承(含虛函數)(虛函數表只有一個)
六、 多重繼承(不含虛函數)
七、 多重繼承(一個含虛函數,一個不含虛函數)
八、 多重繼承(兩個都含有虛函數)
九、 純虛汗繼承
十、 private 的虛函數
1、虛函數繼承的複雜例子,見下圖:
見圖:左邊是這個類的內存佈局,右邊是繼承圖示。 farther類和Uncle類都是虛擬繼承,其內部也都有偏移表,但我以爲這兩個表只是內部隱藏的,不在Son的內存佈局中表示出來,本題Son的內存只有32個字節,若是表示出來就不止32個了,可是下面這個地方在內存中顯示是00 00 00 00,我猜測是否是GrandFather的偏移地址。
VbtSon(Father) |
Farther~Num |
VbtSon(Uncle) |
Uncle~Num |
Son~Num |
Offset(這個地方??) |
Vftable(GrandFather) |
GrandFather~Num |
例子代碼:
1 class GrandFather 2 { 3 public: 4 GrandFather():i_G(5){cout<<"GrandFather() is called!"<<endl;} 5 virtual ~GrandFather(){cout<<"~GrandFather() is called!"<<endl;} 6 public: 7 virtual void Test(){cout<<"GrandFather::Test() is called!"<<endl;} 8 private: 9 int i_G; 10 }; 11 12 class Father: virtual public GrandFather //虛擬繼承 13 { 14 public: 15 Father():i_F(7){cout<<"Father() is called!"<<endl;}; 16 virtual ~Father(){cout<<"~Father() is called!"<<endl;} 17 public: 18 virtual void Test(){cout<<"Father::Test() is called!"<<endl;} 19 private: 20 int i_F; 21 }; 22 23 class Uncle: virtual public GrandFather //虛擬繼承 24 { 25 public: 26 Uncle():i_U(3){cout<<"Uncle is called!"<<endl;} 27 virtual ~Uncle(){cout<<"~Uncle is called!"<<endl;} 28 public: 29 virtual void Test(){cout<<"Uncle ::Test() is called!"<<endl;} 30 private: 31 int i_U; 32 }; 33 34 class Son:public Father,public Uncle 35 { 36 public: 37 Son():i_S(9){cout<<"Son is called!"<<endl;}; 38 virtual ~Son(){cout<<"~Son is called!"<<endl;} 39 public: 40 virtual void Test(){cout<<"Son ::Test() is called!"<<endl;} 41 private: 42 int i_S; 43 }; 44 45 int main(void) 46 { 47 Son p; 48 p.Test(); 49 cout<<sizeof(Son)<<endl; 50 cout<<sizeof(Father)<<endl; 51 cout<<sizeof(GrandFather)<<endl; 52 return 0; 53 }
運行狀況:
二、 菱形繼承無虛擬繼承的狀況
VPTr1(Father) |
GrandFarther~Num |
Father~Num |
VPtr(Uncle) |
GrandFarther~Num |
Uncle~Num |
Son~Num |
1 #include<iostream> 2 using namespace std; 3 class GrandFather 4 { 5 public: 6 GrandFather():i_G(5){cout<<"GrandFather() is called!"<<endl;} 7 virtual ~GrandFather(){cout<<"~GrandFather() is called!"<<endl;} 8 public: 9 virtual void Test(){cout<<"GrandFather::Test() is called!"<<endl;} 10 private: 11 int i_G; 12 }; 13 class Father: public GrandFather //無虛擬繼承 14 { 15 public: 16 Father():i_F(7){cout<<"Father() is called!"<<endl;}; 17 virtual ~Father(){cout<<"~Father() is called!"<<endl;} 18 public: 19 virtual void Test(){cout<<"Father::Test() is called!"<<endl;} 20 private: 21 int i_F; 22 }; 23 24 class Uncle: public GrandFather //無虛擬繼承 25 { 26 public: 27 Uncle():i_U(3){cout<<"Uncle is called!"<<endl;} 28 virtual ~Uncle(){cout<<"~Uncle is called!"<<endl;} 29 public: 30 virtual void Test(){cout<<"Uncle ::Test() is called!"<<endl;} 31 private: 32 int i_U; 33 }; 34 35 class Son:public Father,public Uncle 36 { 37 public: 38 Son():i_S(9){cout<<"Son is called!"<<endl;}; 39 virtual ~Son(){cout<<"~Son is called!"<<endl;} 40 public: 41 virtual void Test(){cout<<"Son ::Test() is called!"<<endl;} 42 private: 43 int i_S; 44 }; 45 46 int main(void) 47 { 48 Son p; 49 p.Test(); 50 cout<<sizeof(Son)<<endl; 51 cout<<sizeof(Father)<<endl; 52 cout<<sizeof(GrandFather)<<endl; 53 54 return 0; 55 }
運行狀況:
三、 虛擬繼承的簡單狀況 見下圖:
VPTrD(A) 4 |
Offset(A) 4 |
A~number 4 |
D~number 4 |
VPtr(Base) 4 |
Base~Number
12 + 3cc + 4 = 40 |
1 class Base 2 { 3 public: 4 Base(){strcpy_s(ch_rc,"abcdefg");} //初始化Base()::im 5 public: 6 virtual void Read(){cout<<"Base::Read()is called!"<<endl;} 7 private: 8 char ch_rc[12]; 9 bool ir; 10 int im; 11 }; 12 class A: virtual public Base //虛擬繼承 13 { 14 public: 15 A():im_A(5){} //初始化A()::im_A 16 public: 17 virtual void Read(){cout<<"A::Read()is called!"<<endl;} 18 private: 19 int im_A; 20 }; 21 class D:public A 22 { 23 public: 24 D():im_D(3){} 25 public: 26 virtual void Read(){cout<<"D::Read()is called!"<<endl;} 27 private: 28 int im_D; 29 }; 30 int _tmain(int argc, _TCHAR* argv[]) 31 { 32 D obj; 33 cout<<sizeof(D)<<endl; 34 return 0; 35 }
運行狀況:
4、單一普通繼承(無虛函數)(這個沒什麼好說的)
內存佈局
Father~Number |
Son~Number |
1 class Father 2 { 3 public: 4 Father(){cout<<"Father() is called!"<<endl;} 5 void TestF(const int &m){ 6 i_B=m; 7 cout<<"Father::TestF() is called!"<<endl; 8 } 9 void Test(){cout<<"Base::Test() is called!"<<endl;} 10 ~Father(){cout<<"~Father() is called!"<<endl;} 11 private: 12 int i_B; 13 }; 14 15 class Son:public Father 16 { 17 public: 18 Son():i_A(5){cout<<"Son() is called!"<<endl;} 19 void Test(){cout<<"Son::Test() is called!"<<endl;} 20 ~Son(){cout<<"~Son() is called!"<<endl;} 21 private: 22 int i_A; 23 }; 24 int main(int argc,char *argv[]) 25 { 26 Father *p=new Son; 27 //Father *p=NULL; 28 p->Test(); 29 delete p; 30 p=NULL; 31 cout<<sizeof(Son)<<endl; 32 return 0; 33 }
五、單一繼承(含虛函數)(虛函數表只有一個)見圖:
VPTr(father) |
Father~number |
Son~number |
Child~number |
1 #include<iostream> 2 using namespace std; 3 class Father 4 { 5 public: 6 Father(){cout<<"Father() is called!"<<endl;} 7 virtual void Test(){cout<<"Base::Test() is called!"<<endl;} 8 virtual ~Father(){cout<<"~Father() is called!"<<endl;} 9 private: 10 int i_B; 11 }; 12 13 class Son:public Father 14 { 15 public: 16 Son():i_A(5){cout<<"Son() is called!"<<endl;} 17 void Test(){cout<<"Son::Test() is called!"<<endl;} 18 ~Son(){cout<<"~Son() is called!"<<endl;} 19 private: 20 int i_A; 21 }; 22 int main(int argc,char *argv[]) 23 { 24 Father *p=new Son; 25 //Father *p=NULL; 26 p->Test(); 27 delete p; 28 p=NULL; 29 cout<<sizeof(Son)<<endl; 30 return 0; 31 }
運行狀況:
6、多重繼承(不含虛函數)(這個也沒什麼好說的)
直接看代碼:
1 #include<iostream> 2 using namespace std; 3 class Father 4 { 5 public: 6 Father():i_B(6){cout<<"Father() is called!"<<endl;} 7 void TestF(const int &m){ 8 i_B=m; 9 cout<<"Father::TestF() is called!"<<endl; 10 } 11 void Test(){cout<<"Father::Test() is called!"<<endl;} 12 ~Father(){cout<<"~Father() is called!"<<endl;} 13 private: 14 int i_B; 15 }; 16 class Son 17 { 18 public: 19 Son():i_A(5){cout<<"Son() is called!"<<endl;} 20 void Test(){cout<<"Son::Test() is called!"<<endl;} 21 ~Son(){cout<<"~Son() is called!"<<endl;} 22 private: 23 int i_A; 24 }; 25 26 class Child:public Father,public Son //多重繼承 27 { 28 public: 29 Child():i_C(5){cout<<"Child() is called!"<<endl;} 30 void Test(){cout<<"Child::Test() is called!"<<endl;} 31 ~Child(){cout<<"~Child() is called!"<<endl;} 32 private: 33 int i_C; 34 }; 35 int main(int argc,char *argv[]) 36 { 37 Father *p=new Child; 38 //Father *p=NULL; 39 p->Test(); 40 cout<<sizeof(Son)<<endl; 41 cout<<sizeof(Child)<<endl; 42 return 0; 43 }
7、多重繼承(一個含虛函數,一個不含虛函數)(相似單一繼承)
VPTr(father) |
Father~number |
Son~number |
Child~number |
1 #include<iostream> 2 using namespace std; 3 4 class Father 5 { 6 public: 7 Father():i_B(6){cout<<"Father() is called!"<<endl;} 8 virtual void TestF(const int &m){ 9 i_B=m; 10 cout<<"Father::TestF() is called!"<<endl; 11 } 12 virtual void Test(){cout<<"Father::Test() is called!"<<endl;} 13 virtual ~Father(){cout<<"~Father() is called!"<<endl;} 14 private: 15 int i_B; 16 }; 17 18 class Son 19 { 20 public: 21 Son():i_A(5){cout<<"Son() is called!"<<endl;} 22 void Test(){cout<<"Son::Test() is called!"<<endl;} 23 ~Son(){cout<<"~Son() is called!"<<endl;} 24 private: 25 int i_A; 26 }; 27 28 class Child:public Father,public Son 29 { 30 public: 31 Child():i_C(5){cout<<"Child() is called!"<<endl;} 32 void Test(){cout<<"Child::Test() is called!"<<endl;} 33 ~Child(){cout<<"~Child() is called!"<<endl;} 34 private: 35 int i_C; 36 }; 37 int main(int argc,char *argv[]) 38 { 39 Father *p=new Child; 40 //Father *p=NULL; 41 p->Test(); 42 cout<<sizeof(Son)<<endl; 43 cout<<sizeof(Child)<<endl; 44 return 0; 45 }
運行狀況:
8、多重繼承(兩個都含有虛函數)
VPTr(father) |
Father~number |
VPTr(Son) |
Son~number Child~number
20個字節 |
1 #include<iostream> 2 using namespace std; 3 4 class Father 5 { 6 public: 7 Father():i_B(6){cout<<"Father() is called!"<<endl;} 8 virtual void TestF(const int &m){ 9 i_B=m; 10 cout<<"Father::TestF() is called!"<<endl; 11 } 12 virtual void Test(){cout<<"Father::Test() is called!"<<endl;} 13 virtual ~Father(){cout<<"~Father() is called!"<<endl;} 14 private: 15 int i_B; 16 }; 17 class Son 18 { 19 public: 20 Son():i_A(5){cout<<"Son() is called!"<<endl;} 21 virtual void Test(){cout<<"Son::Test() is called!"<<endl;} 22 virtual ~Son(){cout<<"~Son() is called!"<<endl;} 23 private: 24 int i_A; 25 }; 26 27 class Child:public Father,public Son 28 { 29 public: 30 Child():i_C(7){cout<<"Child() is called!"<<endl;} 31 void Test(){cout<<"Child::Test() is called!"<<endl;} 32 ~Child(){cout<<"~Child() is called!"<<endl;} 33 private: 34 int i_C; 35 }; 36 int main(int argc,char *argv[]) 37 { 38 //Father *p=new Child; 39 Child p; 40 //Father *p=NULL; 41 p.Test(); 42 cout<<sizeof(Son)<<endl; 43 cout<<sizeof(Child)<<endl; 44 return 0; 45 }
運行狀況:
9、純虛汗繼承
(只在父類中申明,並在子類中實現申明的函數才能夠用)
內存分配與前面只含虛函數的狀況相似
1 #include<iostream> 2 using namespace std; 3 class A 4 { 5 public: 6 A():i_A(5){cout<<"A() is called!"<<endl;} 7 public: 8 virtual void Test()= 0; //prue virtual function 9 virtual void Base() {cout<<"this is farther class"<<endl;} 10 private: 11 int i_A; 12 }; 13 14 class B:public A 15 { 16 public: 17 B():i_B(9){} 18 public: 19 void Test() { cout<<" this is SubVirtual!"<<endl;} //必須在子類中實現該函數才能夠用 20 void Base() { 21 cout<<"this is subclass Base"<<endl; 22 } 23 private: 24 int i_B; 25 }; 26 27 int main(void) 28 { 29 A* p = new B; //multstate pointer 30 p->Test(); 31 p->Base(); 32 cout<<sizeof(B)<<endl; 33 return 0 ; 34 }
10、private 的虛函數
1 #include<iostream> 2 using namespace std; 3 4 class A 5 { 6 public: 7 virtual void Test() { func();} 8 private: 9 int i_A; 10 virtual void func() {cout<<"A::func () is Called!"<<endl; } 11 }; 12 class B: public A 13 { 14 private: 15 //雖然func()在A類中是private的,可是仍然能夠出如今派生類中,並仍然能夠與public或者protected的虛函數同樣產生多態的效果。 16 virtual void func() { cout<<"B::func() is Called!"<<endl;} private: 17 int i_B; 18 }; 19 20 int main(void) 21 { 22 //A *p=new B; 23 A p; 24 //B p; 25 //p->func(); 26 p.Test(); 27 cout<<sizeof(B)<<endl; 28 return 0; 29 }
運行狀況: