C++重要知識點小結--1 :http://www.cnblogs.com/heyonggang/p/3246631.html
1.C++允許程序員聲明一個不能有實例對象的類,這樣的類惟一的用途是被繼承。這種類成為抽象類。
一個抽象類至少具有一個純虛函數。所謂純虛函數是指被標明為不具體實現的虛成員函數。
如:
virtual void WithDrawal(float amount) = 0; //純虛函數
在WithDrawal()的聲明之后的“=0”表明程序員將不定義該函數。該聲明是為派生類而保留的位置。
一個抽象類不能有實例對象,即不能由該類抽象來制造一個對象。
純虛函數是在基類中為子類保留的一個位置,以便子類用自己的實在函數定義來重載之。如果在基類中沒有保留位置,則就沒有重載。
純虛函數是一個沒有定義函數語句的基類虛函數,純虛函數的值是0.派生類必須為每一個基類純虛函數提供一個相應的函數定義。
2.派生類可以繼承基類的所有公有和保護的數據成員和成員函數。
保護的訪問權限對於派生類來說是公有的,而對於其它的對象來說是私有的。即使是派生類也不能訪問基類的私有的數據成員和成員函數。
在派生類中允許重載基類的成員函數。如果基類中的函數是虛函數,當使用指針或引用訪問對象時,將基於實際運行時指針所指向的對象類型來調用派生類的函數。
3.筆試,面試中常考的C++虛擬繼承的知識點
第一種情況: 第二種情況: 第三種情況 第四種情況:
class a class a class a class a
{ { { {
virtual void func(); virtual void func(); virtual void func(); virtual void func();
}; }; char x; char x;
class b:public virtual a class b :public a }; };
{ { class b:public virtual a class b:public a
virtual void foo(); virtual void foo(); { {
}; }; virtual void foo(); virtual void foo();
}; };
如果對這四種情況分別求sizeof(a), sizeof(b)。結果是什么樣的呢?下面是輸出結果:(在vc6.0中運行)
第一種:4,12
第二種:4,4
第三種:8,16
第四種:8,8
可參考:http://blog.csdn.net/wangqiulin123456/article/details/8059536
想想這是為什么呢?
因為每個存在虛函數的類都要有一個4字節的指針指向自己的虛函數表,所以每種情況的類a所占的字節數應該是沒有什么問題 的,那么類b的字節數怎么算呢?看“第一種”和“第三種”情況采用的是虛繼承,那么這時候就要有這樣的一個指針vptr_b_a,這個指針叫虛類指針,也 是四個字節;還要包括類a的字節數,所以類b的字節數就求出來了。而“第二種”和“第四種”情況則不包括vptr_b_a這個指針,這回應該木有問題了 吧。
1 class a 2 { 3 virtual void func(); 4 }; 5 6 class b:public a 7 { 8 void foo(); 9 };
此時:sizeof(a) = 4 , sizeof(b) = 4
1 class a 2 { 3 void func(); 4 }; 5 6 class b:public a 7 { 8 virtual void foo(); 9 };
此時:sizeof(a) = 1 , sizeof(b) = 4
1 class a 2 { 3 void func(); 4 }; 5 6 class b:public a 7 { 8 void foo(); 9 };
此時:sizeof(a) = 1 , sizeof(b) = 1
如下例:

1 class A 2 { 3 }; 4 class A2 5 { 6 }; 7 class B : public A 8 { 9 }; 10 class C : public virtual B 11 { 12 }; 13 class D : public A , public A2 14 { 15 };
以上答案分別是1 , 1 , 4 , 1. 這說明:空類所占空間為1,單一繼承的空類空間也為1,多重繼承的空類空間還是1.但是虛繼承涉及到虛表(虛指針),所以sizeof(C)的大小為4
4.多繼承的構造順序
構造對象的規則需要擴展以控制多重繼承。構造函數按下列順序被調用:
- 任何虛擬基類的構造函數按照它們被繼承的順序構造;
- 任何非虛擬基類的構造函數按照它們被繼承的順序構造;
- 任何成員對象的構造函數按照它們聲明的順序調用;
- 類自己的構造函數。
5.C++子類繼承父類后子類的大小
1 #include <iostream> 2 using namespace std; 3 class A 4 { 5 private: 6 int a; 7 }; 8 9 class B:public A 10 { 11 private: 12 int b; 13 }; 14 15 int main() 16 { 17 cout<<sizeof(A)<<endl; 18 cout<<sizeof(B)<<endl; 19 return 0; 20 }
剛開始我一想子類繼承父類不會繼承父類的私有變量,如此我認為結果為4,4(錯誤)。而事實上結果是4,8。也就是說子類把父類的私有變量也繼承下來了,但是卻無法訪問,對於我這種菜鳥來說一下子沒法轉個彎來,后來看看資料煥然大悟,子類雖然無法直接訪問父類的私有變量,但是子類繼承的父類的函數卻可以訪問,不然的話如果只繼承函數而不繼承變量,哪么父類的函數豈不成了無米之炊了。所以必須把父類的所有變量都繼承下來,這樣既能保護父類的變量也能使用父類的函數。
6.繼承的訪問控制
繼承分為公共繼承、保護繼承和私有繼承。
在公共繼承的類中,基類的每個成員在子類中保持同樣的訪問方式。即在基類中為public的成員,子類中可以訪問,並據為public的;基類中為protected的成員,子類中也可訪問之,並據為protected的;基類中為private的成員,在子類中不能訪問之,這就像在應用程序中不能訪問類中似有成員一樣。
訪問控制權限:
- 私有繼承時,基類中不管是公有的,還是保護的或者為私有的,一律在子類中變成私有成員。
- 保護繼承時,基類中公共和保護的成員在子類中變成保護的,而基類中私有的成員在子類中變成私有的。
- 公共繼承時,基類中為公共、保護和私有的成員在子類中仍保持為公共、保護和私有的。
如果不標明繼承為公共還是保護或者私有,則默認的繼承是私有的。
在繼承關系中,基類的private成員不但對應用程序隱藏,甚至對派生類也隱藏。而基類的保護成員則只對應用程序隱藏,而對派生類則毫不隱瞞。
一個私有的或保護的派生類不是子類,因為非公共的派生類不能做基類能做的所有的事。
保護繼承與私有繼承類似,繼承之后的類相對於基類來說是獨立的;保護繼承的類對象,在公開場合同樣不能使用基類的成員。
派生類的構造函數必須激活所有基類的構造函數,並把相應的參數傳遞給它們。
也可參看:http://www.cnblogs.com/heyonggang/archive/2013/04/17/3026107.html
7.虛函數
C++虛函數用於實現動態綁定,或者說多態,默認的類方法是非虛函數,需要動態綁定的類方法,必需顯式聲明函數 virtual。
virtual函數必需在子類中再次聲明,明確告訴子類有這個方法,否則編譯時報錯,getRange方法未聲明的錯誤。
#include <iostream> using namespace std; class Range { public: int width; int height; virtual float getRange(); Range(int w, int h):width(w), height(h){}; Range(){}; }; float Range::getRange() { return width * height; } class Square:public Range { public: virtual float getRange(); Square(){}; Square(int w, int h):Range(w, h){}; }; float Square::getRange() { return width * width * 2; } class Circle:public Range { public: virtual float getRange(); Circle(){}; Circle(int w, int h):Range(w, h){}; }; float Circle::getRange() { return 3.14 * width * width / 2; } int main(int argc, char* args[]) { Square s1(3, 4); Circle c1(2, 5); Range *r1 = &s1; cout << r1->getRange() << endl; Range *r2 = &c1; cout << r2->getRange() << endl; return 0; }
輸出結果為:
18
6.28
Square 和 Circle 都由一個 Range 指針指向,當調用 getRange方法,動態找到相應 Square 和 Circle 實例的getRange方法進行調用。
純虛函數
C++的純虛函數用於表示一個類不能被創建實例, 必需是子類覆蓋該方法的定義后,方可新建類實例,格式是在虛函數后面添加 = 0。
假如上例中的Range只是一個初步表示區域的一個類,那么它的getRange()方法需要由子類實現才有效,表示為:
virtual float getRange() = 0;
此時不能再創建Range rt()實例,將會報錯:
cannot declare variable ‘rt’ to be of abstract type ‘Range’
range2.cpp:3:13: note: because the following virtual functions are pure within ‘Range’:
但我們仍然可以新建Range的指針,指向Circle或者是Square
一個有意思的問題:為什么析構函數要設置成虛函數
Range *r1 = new Circle(3, 4);
如果析構函數不是虛函數,則r1在釋放內存時,則調用提Range的析構函數。
結果並不是想要的結果,我們想要的結果是調到Circle對象的析構函數。
如果析構函數是虛函數,有多態的支持,r1調用Circle對象的析構函數,Circle對象的析構函數默認調用父類Range的析構函數,保證Circle和Range對象的內容都得到清除。