C++重要知識點小結---2


 

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 };
View Code

以上答案分別是1 , 1 , 4 , 1. 這說明:空類所占空間為1,單一繼承的空類空間也為1,多重繼承的空類空間還是1.但是虛繼承涉及到虛表(虛指針),所以sizeof(C)的大小為4

 

4.多繼承的構造順序

構造對象的規則需要擴展以控制多重繼承。構造函數按下列順序被調用:

  1. 任何虛擬基類的構造函數按照它們被繼承的順序構造;
  2. 任何非虛擬基類的構造函數按照它們被繼承的順序構造;
  3. 任何成員對象的構造函數按照它們聲明的順序調用;
  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對象的內容都得到清除。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM