cpp面向?qū)ο蟮囊恍﹤€人總結(jié)

總覽

一提到面向?qū)ο笏孤担蜁岬矫嫦驅(qū)ο蟮娜齻€特征,封裝,繼承和多態(tài)羡微,雖然這種概括可能顯得比較籠統(tǒng)谷饿,但是都這里還是從這三個角度來進行cpp面向?qū)ο蟮姆治觥?/p>

封裝

所謂的封裝一般指的就是,隱藏自身的一些屬性和方法妈倔,對外暴露出一些抽象的有具體的使用目的的方法博投,這樣做的一個顯著的好處就是在類的屬性需要修改的時候,使用方不需要做相應(yīng)的改動盯蝴。使用方不需要關(guān)注類的內(nèi)部具體的實現(xiàn)細節(jié)毅哗,只需要關(guān)注特定的功能,在一定程度上實現(xiàn)了解耦捧挺,同時提供了很多靈活性虑绵。

多態(tài)

提到多態(tài)呢,顧名思義闽烙,就是說有一樣東西他會有幾種不同的形態(tài)翅睛,具體從c++的角度來說,可以分為編譯時多態(tài)和運行時多.
編譯時多態(tài)一般就指的是函數(shù)的重載黑竞;運行時多態(tài)一般特指父類的指針去指向子類的對象捕发。關(guān)于這部分內(nèi)容可以看看另一篇隨筆:http://www.reibang.com/p/b2fb9943aaf0

繼承

一般說到繼承,離不開以下幾點很魂,繼承方式:公有(public), 私有(private),保護(protected)扎酷,友元函數(shù)。

  • 1 公有繼承時遏匆,基類的公用成員和保護成員在派生類中保持原有的訪問屬性法挨,其私有成員仍為基類私有,即在派生類中不能訪問幅聘,在類外也不能訪問坷剧。私有成員體現(xiàn)了數(shù)據(jù)的封裝性,如果基類的私有成員可以被派生類所訪問喊暖,即破壞了基類的封裝性,這就會失去C++的一個重要特性撕瞧。
  • 2 保護繼承(protected)的特點是基類的所有公有成員和保護成員都成為派生類的保護成員陵叽,并且只能被它的派生類成員函數(shù)或友元訪問,基類的私有成員仍然是私有的丛版。對派生類而言巩掺,保護成員類似于公有成員,但對于外部而言页畦,保護成員與私有成員類似胖替。
  • 3 私有繼承即所有基類成員均變成派生類的私有成員,基類的私有成員仍然不能在派生類中訪問。(基類的保護成員在子類中也會變成私有成員)

小結(jié):公有和保護可以被子類繼承独令,私有不可以端朵。父類成員變量的權(quán)限和子類繼承父類的方式會以一種兩者比較取較低的方式存在于此類中。

對象

從下面的代碼我們演示了一個先有父親的基類燃箭,然后三個子類分別以公有冲呢,私有,保護的方式繼承這個父類招狸,發(fā)生的現(xiàn)象敬拓。
首先講一下繼承類的構(gòu)造方式:首先會調(diào)用父類的構(gòu)造函數(shù),然后去調(diào)用自身的構(gòu)造函數(shù)裙戏;析構(gòu)的時候是相反的乘凸,首先析構(gòu)自身,然后再去析構(gòu)父類對象累榜。

#include <string>
#include <iostream>
using namespace std;
class Father {
private:
    string name = "Tom";
    int age = 40;
    int height = 175;
public:
    string gender = "male";
    string eyeColor = "blue";

    string getName() {
        cout << "father's name is " << name << endl;
        return name;
    }

    int getAge () {
        cout << "father's age is " << age << endl;
        return age;
    }

protected:
    string address = "Utopia";
};

class SonA : public Father {

};

class SonB : protected Father {

};

class SonC : private Father {

};
    cout << "sizeof father is " << sizeof(Father) << endl;
    cout << "sizeof sonA is " << sizeof(SonA) << endl;
    cout << "sizeof sonB is " << sizeof(SonB) << endl;
    cout << "sizeof sonC is " << sizeof(SonC) << endl;

結(jié)果:
sizeof father is 104
sizeof sonA is 104
sizeof sonB is 104
sizeof sonC is 104

我們首先去看一下营勤,以不同方式繼承父類的子類的size會不會不同,實驗發(fā)size都是一樣的信柿,說明不同的繼承方式不影響父類在子類內(nèi)部的存儲冀偶。

下面如果在子類A中重寫父類的一個成員變量,看看會發(fā)生什么渔嚷?

class SonA : public Father {
public:
    string gender = "male";
};

sizeof(SonA) = 128

這說明子類新的成員變量并沒有把父類的成員變量覆蓋掉进鸠。同樣,要遵守內(nèi)存對齊形病。

友元

如果從現(xiàn)實的角度去分析父類客年,子類,友元之間的關(guān)系漠吻,友元是你家的生死之交量瓜,雖然跟你家沒有什么血緣關(guān)系,但是他有著你家的鑰匙途乃,跟你爹的關(guān)系可能比你跟你爹關(guān)系還要親绍傲。下面給出一個正式一點的說法:

類對數(shù)據(jù)進行了隱藏和封裝后,類的數(shù)據(jù)成員一般都定義為私有成員耍共,成員函數(shù)一般都定義為公有的烫饼,以此提供類與外界的通訊接口。但是试读,有時需要定義一些函數(shù)杠纵,這些函數(shù)不是類的一部分颅筋,但又需要頻繁地訪問類的數(shù)據(jù)成員歹鱼,這時可以將這些函數(shù)定義為該函數(shù)的友元函數(shù)。除了友元函數(shù)外贡这,還有友元類,兩者統(tǒng)稱為友元银亲。 友元函數(shù)是類外的函數(shù)慢叨,所以它的聲明可以放在類的私有段或公有段且沒有區(qū)別。友元類的所有成員函數(shù)都是另一個類的友元函數(shù)群凶,都可以訪問另一個類中的隱藏信息(包括私有成員和保護成員)插爹。

下面來看一個例子:友元函數(shù)getheight

class Father {
private:
    string name = "Tom";
    int age = 40;
    int height = 175;

    friend int getHeight(Father father);
public:
    string gender = "male";
    string eyeColor = "blue";

    string getName() {
        cout << "father's name is " << name << endl;
        return name;
    }

    int getAge () {
        cout << "father's age is " << age << endl;
        return age;
    }

protected:
    string address = "Utopia";
};

如果在棧上定義
image.png

會提示成員私有,無法獲取请梢。但是友元可以獲取到類的私有信息:

int getHeight(Father father) {
    return father.height;
}

int main()
{
    Father father;
    cout << getHeight(father);

    return 0;
}

結(jié)果: 
175

菱形繼承

在多重繼承時可能會導致菱形繼承(鉆石繼承的問題)赠尾。

  • 多重繼承的情況下,嚴格按照派生類定義時從左到右的順序來調(diào)用構(gòu)造函數(shù)毅弧,析構(gòu)函數(shù)與之相反气嫁。但是如果基類(基類,父類够坐,超類是指被繼承的類寸宵,派生類,子類是指繼承于基類的類.)中有虛基類的話則構(gòu)造函數(shù)的調(diào)用順序如下:
    (1) 虛基類的構(gòu)造函數(shù)在非虛基類的構(gòu)造函數(shù)之前調(diào)用元咙;
    (2) 若同一層次中包含多個虛基類梯影,這些虛基類的構(gòu)造函數(shù)按照他們的說明順序調(diào)用;
    (3) 若虛基類由非虛基類派生而來庶香,則任然先調(diào)用基類構(gòu)造函數(shù)甲棍,再調(diào)用派生誒,在調(diào)用派生類的構(gòu)造函數(shù)赶掖。
  • 鉆石繼承:B繼承A感猛,C繼承A,D多重繼承B,C奢赂,則A會在D中存在兩份拷貝陪白,而且調(diào)用的時候不知道調(diào)用誰,不過可以通過D.B::f來指定調(diào)用膳灶。
    解決這個問題的方式是使用虛繼承咱士,虛繼承的原理是通過虛基類表指針指向虛基類表,虛表中記錄了虛基類與本類的偏移地址轧钓;通過偏移地址司致,這樣就找到了虛基類成員,而虛繼承也不用像普通多繼承那樣維持著公共基類(虛基類)的兩份同樣的拷貝聋迎,節(jié)省了存儲空間。
    可以通過下面的代碼看一下:
class Father {
private:
    string name = "Tom";
    int age = 40;
    int height = 175;

    friend int getHeight(Father father);
public:
    Father() {
        cout << "construct Father" << endl;
    }
    string gender = "male";
    string eyeColor = "blue";

    string getName() {
        cout << "father's name is " << name << endl;
        return name;
    }

    int getAge () {
        cout << "father's age is " << age << endl;
        return age;
    }

protected:
    string address = "Utopia";
};

class SonA : public Father {
public:
    SonA() {
        cout << "contruct SonA" << endl;
    }
    string gender = "male";
};

class SonD : public Father {
public:
    SonD() {
        cout << "contruct SonB" << endl;
    }

};

class Grandson : public SonA , public SonD {
public:
    Grandson() {
        cout << "construct GrandSon" << endl;
    }
};

在構(gòu)造GrandSon時會調(diào)用分別調(diào)用兩次Father的構(gòu)造函數(shù):

int main(void) {
    Grandson* grandson = new Grandson;
    //grandson->getName();
    return 0;
}

結(jié)果:
construct Father
contruct SonA
construct Father
contruct SonB
construct GrandSon

在調(diào)用getName方式時枣耀,編譯器會提示:
image.png

這就是所謂的鉆石繼承霉晕。也可以顯式的指明調(diào)用的是哪個函數(shù):

int main(void) {
    Grandson* grandson = new Grandson;
    grandson->SonA::getName();
    return 0;
}

結(jié)果:
construct Father
contruct SonA
construct Father
contruct SonB
construct GrandSon
father's name is Tom

但這樣其實還有一個問題庭再,我們只需要一個Father,但是現(xiàn)在卻有了兩個:

sizeof(*grandson) = 232

這種問題可以通過虛繼承來避免

using namespace std;
class Father {
private:
    string name = "Tom";
    int age = 40;
    int height = 175;

    friend int getHeight(Father father);
public:
    Father() {
        cout << "construct Father" << endl;
    }
    string gender = "male";
    string eyeColor = "blue";

    string getName() {
        cout << "father's name is " << name << endl;
        return name;
    }

    int getAge () {
        cout << "father's age is " << age << endl;
        return age;
    }

protected:
    string address = "Utopia";
};

class SonA : virtual public Father {
public:
    SonA() {
        cout << "contruct SonA" << endl;
    }
    string gender = "male";
};

class SonD : virtual public Father {
public:
    SonD() {
        cout << "contruct SonB" << endl;
    }

};

class Grandson : public SonA , public SonD {
public:
    Grandson() {
        cout << "construct GrandSon" << endl;
    }
};

看一下效果:

using namespace std;

int main(void) {
    Grandson* grandson = new Grandson;
    cout << sizeof(* grandson) << endl;
    grandson->getName();
    return 0;
}

結(jié)果:
construct Father
contruct SonA
contruct SonB
construct GrandSon
144
father's name is Tom

編譯器不再困惑牺堰,對象占用的空間也少了拄轻。減到了 144.
注意一點,虛繼承要在繼承Father時添加伟葫,如果另GrandSon虛繼承SonA,SonD的話就沒有效果了恨搓,反而會因為引入虛指針增加對象大小。

class Father {
private:
    string name = "Tom";
    int age = 40;
    int height = 175;

    friend int getHeight(Father father);
public:
    Father() {
        cout << "construct Father" << endl;
    }
    string gender = "male";
    string eyeColor = "blue";

    string getName() {
        cout << "father's name is " << name << endl;
        return name;
    }

    int getAge () {
        cout << "father's age is " << age << endl;
        return age;
    }

protected:
    string address = "Utopia";
};

class SonA : public Father {
public:
    SonA() {
        cout << "contruct SonA" << endl;
    }
    string gender = "male";
};

class SonD : public Father {
public:
    SonD() {
        cout << "contruct SonB" << endl;
    }

};

class Grandson : virtual public SonA , virtual public SonD {
public:
    Grandson() {
        cout << "construct GrandSon" << endl;
    }
};

看一下效果:

int main(void) {
    Grandson* grandson = new Grandson;
    cout << "sizeof grandson is " << sizeof(* grandson) << endl;
    return 0;
}

結(jié)果:
construct Father
contruct SonA
construct Father
contruct SonB
construct GrandSon
sizeof grandson is 240

可以看到size 反而增加了筏养,分析一下:
從結(jié)果上看:被虛繼承的類斧抱,在發(fā)生多重繼承時,只會被實例化一次渐溶。
在發(fā)生虛繼承時辉浦,子類實例中會有一個虛指針指向虛表,虛表中存著一個地址或者是一個偏移量茎辐,發(fā)生了多重繼承時宪郊,不同的對象指向的父類地址是一個。

虛表的內(nèi)存布局

上面說過拖陆,對象的第一個位置有一個虛指針指向該類的虛表弛槐,在發(fā)生多重繼承時,其實是有多個指針的:

class A{
    virtual void play() {

    }
};
class B{
    virtual void plays() {

    }
};

class C: public A, public B {

};
sizeof(C) = 16;

說明有兩個虛指針指向兩個虛表依啰,虛指針并非指向表頭乎串,而是直接指到了虛函數(shù)的位置。虛表的內(nèi)存布局是什么樣的呢:

  • 首先是top_offset:非多重繼承時都為0孔飒,在多重繼承時為第二個被繼承的偏移值灌闺。
  • 其次是子類的type_info。
  • 之后是虛函數(shù)坏瞄。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桂对,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鸠匀,更是在濱河造成了極大的恐慌蕉斜,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缀棍,死亡現(xiàn)場離奇詭異宅此,居然都是意外死亡,警方通過查閱死者的電腦和手機爬范,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進店門父腕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人青瀑,你說我怎么就攤上這事璧亮∠艚耄” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵枝嘶,是天一觀的道長帘饶。 經(jīng)常有香客問我,道長群扶,這世上最難降的妖魔是什么及刻? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮竞阐,結(jié)果婚禮上缴饭,老公的妹妹穿的比我還像新娘。我一直安慰自己馁菜,他們只是感情好茴扁,可當我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著汪疮,像睡著了一般峭火。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上智嚷,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天卖丸,我揣著相機與錄音,去河邊找鬼盏道。 笑死稍浆,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的猜嘱。 我是一名探鬼主播衅枫,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼朗伶!你這毒婦竟也來了弦撩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤论皆,失蹤者是張志新(化名)和其女友劉穎益楼,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體点晴,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡感凤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了粒督。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陪竿。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖屠橄,靈堂內(nèi)的尸體忽然破棺而出萨惑,到底是詐尸還是另有隱情捐康,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布庸蔼,位于F島的核電站,受9級特大地震影響贮匕,放射性物質(zhì)發(fā)生泄漏姐仅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一刻盐、第九天 我趴在偏房一處隱蔽的房頂上張望掏膏。 院中可真熱鬧,春花似錦敦锌、人聲如沸馒疹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽颖变。三九已至,卻和暖如春听想,著一層夾襖步出監(jiān)牢的瞬間腥刹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工汉买, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留衔峰,地道東北人。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓蛙粘,卻偏偏與公主長得像垫卤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子出牧,可洞房花燭夜當晚...
    茶點故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容