C++學(xué)習(xí)筆記:(五)繼承&&多態(tài)

本文章分為知識點(diǎn)旨椒、例子和心得,交流群728483370,一起學(xué)習(xí)加油堵漱!

7.組合综慎、繼承與多態(tài)性

7.1 組合

7.2 繼承

7.3繼承和組合

7.4構(gòu)造與析構(gòu)順序

7.5派生類重載基類函數(shù)的訪問

7.6多態(tài)性&虛函數(shù)

7.7純虛函數(shù)和抽象基類

7.8 多重繼承

7.9派生類成員的標(biāo)識與訪問

7.組合、繼承與多態(tài)性

面向?qū)ο笤O(shè)計(jì)的重要目的之一就是代碼重用勤庐,這也是C++重要性能之一示惊。軟件的重用性鼓勵(lì)人們使用已有的好港、得到認(rèn)可并經(jīng)過測試的高質(zhì)量代碼。多態(tài)性可以以常規(guī)方式書寫程序來訪問多種現(xiàn)有的且專門化了的相關(guān)類米罚。繼承和多態(tài)是面向?qū)ο蟪绦蛟O(shè)計(jì)方法的兩個(gè)最主要的特征钧汹。繼承可以將一群相關(guān)的類組織起來,并共享期間的相同數(shù)據(jù)和操作行為录择;多態(tài)使得設(shè)計(jì)者在這些類上編程時(shí)拔莱,可以如同操作一個(gè)單一體,而非相互獨(dú)立的類隘竭,且給設(shè)計(jì)者更多靈活性來加入或刪除類的一些屬性或方法塘秦。

7.1 組合

對于比較簡單的類,其數(shù)據(jù)成員可能都是基本數(shù)據(jù)類型动看,但對于某些復(fù)雜的類來說尊剔,其某些數(shù)據(jù)成員可能又是另一些類類型,這就形成了類的組合弧圆。實(shí)際上赋兵,C語言中一直在使用組合,如C語言中的結(jié)構(gòu)體就可以看成是不同類型數(shù)據(jù)的組合搔预,前面提及的類也是不同類型數(shù)據(jù)及操作的組合霹期,只不過是基本數(shù)據(jù)類型的組合而已。

#include <iostream>

class Myclass

{

enum{NUM = 50};

char cName[20];

int iNum;

Student stulist[Num];

public:

const char* Getclassname();

const char* Getstuname(int ino);

};

Myclass::Myclass()

{

iNum = 0;

inline const char* Myclass::Getclassname()

{

return cName;

}

const char* Myclass::GetStuName(int iNo)

{

for(int i = 0; i < iNum; i++)

{

if(stulist[i].Getno() == iNo)

return stulist[i].GetName();

}

return NULL;

}

7.2 繼承

類的繼承就是新類從已有類哪里獲得已有的屬性和行為拯田,或者說是基類派生了具有基類特征又有新特征的派生類历造。繼承是軟件可重用型的一種形式,新類通過繼承從現(xiàn)有類中吸取其屬性和行為船庇,并對其進(jìn)行覆蓋或改寫吭产,產(chǎn)生新類所需要的功能。同樣鸭轮,新類也可以派生出更新的類臣淤。創(chuàng)建新類,但不是從頭創(chuàng)建窃爷∫亟可以使用其他人已經(jīng)創(chuàng)建并調(diào)試過的類。關(guān)鍵是使用類按厘,而不是更改已存在的代碼医吊。

類繼承語法:

class 子類名:[public|private|protected] 父類名

{

...

}

子類具有父類的所有屬性和行為,且可以增加新的行為和屬性逮京。

C++提供了三種繼承的方式:公有繼承(public)卿堂、私有繼承(private)、保護(hù)繼承(protected)懒棉。

公有繼承

(1)基類的private草描、public览绿、protected成員的訪問屬性在派生類中保持不變。

(2)派生類中繼承的成員函數(shù)可以直接訪問基類中所有成員陶珠,派生類中新增的成員函數(shù)只能訪問基類的public和protected成員挟裂,不能訪問基類的private成員。

(3)通過派生類的對象只能訪問基類的public成員揍诽。

矩形移動(dòng):

#include <iostream>

using namespace std;

class Point

{

private:

float x,y;

public:

void InitP(float xx = 0, float yy = 0)

{

x = xx;

y = yy;

}

void Move(float a, float b)

{

x += a;

y += b;

}

float Getx(){return x;}

float Gety(){return y;}

};

class Rectangle:public Point

{

private:

float w,h;

public:

void InitR(float x, float y, float w, float h)

{

InitP(x, y);

this->w = w;

this->h = h;

}

float Geth(){return h;}

float Getw(){return w;}

};

int main()

{

Rectangle rect;

rect.InitR(2,3,20,10);

//rect.h;

//rect.w; //錯(cuò)誤诀蓉,父類私有成員

rect.Move(3,2);

cout << rect.Getx() << ',' <<rect.Gety() << ',' <<rect.Geth() << ',' <<rect.Getw() <<endl;

return 0;

}

保護(hù)繼承

(1)基類的public、protected成員都以protected身份出現(xiàn)在派生類中暑脆。

(2)派生類中新增的成員函數(shù)可以直接訪問基類中的public和protected成員渠啤,但不能訪問基類的private成員。

(3)通過派生類的對象不能訪問基類的任何成員添吗。

私有繼承

(1)基類的public沥曹、protected成員都以private身份出現(xiàn)在派生類中。

(2)派生類中新增的成員函數(shù)可以直接訪問基類中的public和protected成員碟联,但不能訪問基類的private成員妓美。

(3)通過派生類的對象不能訪問基類的任何成員。

例如:把上述程序的繼承改為私有繼承鲤孵。

class Rectangle:private Point

{

private:

float w,h;

public:

void InitR(float x, float y, float w, float h)

{

InitP(x, y);

this->w = w;

this->h = h;

}

float geth(){retrun h;}

float getw(){return w;}

};

當(dāng)聲明的子對象調(diào)用父類的成員函數(shù)時(shí)壶栋,會(huì)發(fā)生錯(cuò)誤:

Rectangle rect;

Rect.InitP(); //錯(cuò)誤,通過私有繼承普监,父類的InitP()在子類中變成私有的

如果確實(shí)需要在子類里對私有繼承父類的公有成員變?yōu)楣械墓笫裕恍枰谧宇愔新暶鳛楣屑纯?

class Rectangle:private Point

{

...

public:

Point::InitP;

Point::getx;

...

};

注意:在派生類中聲明基類的函數(shù)時(shí),只需要給出函數(shù)的名稱凯正,函數(shù)的參數(shù)和返回值類型不應(yīng)出現(xiàn)毙玻。

class Rectangle:private Point

{

...

public:

Point::InitP;

Point::getx;

void Point::InitR(float x, float y, float w, float h); //錯(cuò)誤

Point::InitR(float x, float y, float w, float h); //錯(cuò)誤

Point::InitR(); //錯(cuò)誤

...

};

關(guān)于繼承,我將專門用一篇來介紹廊散,這里只是先了解基本概念桑滩。

7.3繼承和組合

實(shí)際工作中往往需要在定義一個(gè)新類時(shí)這個(gè)新類的一部分內(nèi)容時(shí)從已有類中繼承的,還有一部分內(nèi)容則是需要由其他類組合的允睹,這就需要把組合和繼承放在一起使用施符。例如,在定義Z類時(shí)它的一部分內(nèi)容需要從X類繼承擂找,另一部分內(nèi)容則是Y類型的,具體定義:

class X

{

int i;

};

class Y

{

float f;

};

class Z:public X

{

double d;

Y y;

};

組合通常在希望新類內(nèi)部有已存在類性能時(shí)使用浩销,而不希望已存在類作為它的接口贯涎。這就是說,嵌入一個(gè)計(jì)劃用于實(shí)現(xiàn)新類性能的對象慢洋,而新類的用戶看到的是新定義的接口,而不是來自父類的接口。

繼承是取一個(gè)已存在的類姻锁,并制作它的一個(gè)專門的版本谷醉。通常,這意味著取一個(gè)一般目的的類舆吮,并為了特殊的需要對它進(jìn)行專門化。

建立班級類:

#include <iostream>

#include <string.h>

using namespace std;

class Person

{

char strname[20];

int iage;

char csex;

public:

Person(const char* cpname = NULL, int age = 0, char sex = 'm');

const char* Getname(){return strname;}

int Getage(){return iage;}

char Getsex(){return csex;}

void Setname(const char *cpname);

void Setage(int age){iage = age;}

void Setsex(char sex){csex = sex;}

};

class Teacher:public Person

{

int wid;

public:

Teacher(const char* cpname = NULL, int age = 0, char sex = 'm', int no = 0):Person(cpname, age, sex),wid(no){}

int Getwid(){return wid;}

void Setwid(int no){wid = no;}

};

class Student:public Person

{

int ino;

float score[5];

float ave;

public:

Student(const char* cpname = NULL,int age = 0, char sex = 'm', int no = 0):Person(cpname, age, sex),ino(no){}

int Getno(){return ino;}

void Setno(int no){ino = no;}

void Setscore(const float fdata[]);

float Getavescore(){return ave;}

void print();

};

void Studentprint(Student &a)

{

cout << "Student's infomation:";

cout << "Ino:" << a.Getno() << " " << "Age:" << a.Getage() << " " << "Name:" << a.Getname() << "Sex:" << a.Getsex() <<endl;

}

Person::Person(const char* cpname, int age, char sex)

{

csex = sex;

iage = age;

if(strlen(cpname) < 20)

{

strcpy(strname, cpname);

}

else

strname[0] = '\0';

}

void Person::Setname(const char* cpname)

{

if(strlen(cpname) < 20)

strcpy(strname, cpname);

else

strname[0] = '\0';

}

void Student::Setscore(const float fdata[])

{

float fsum = 0;

for(int i = 0; i < 5; i++)

{

score[i] = fdata[i];

fsum += fdata[i];

}

ave = fsum/5;

}

int main()

{

Student a("Tyler", 23, 'm', 00001);

Studentprint(a);

return 0;

}

7.4構(gòu)造與析構(gòu)順序

當(dāng)采用繼承方式創(chuàng)建子類對象時(shí),首先從父類開始執(zhí)行構(gòu)造函數(shù)酸员,父類的成員,然后再執(zhí)行子類的構(gòu)造函數(shù)讳嘱,子類成員幔嗦;當(dāng)撤銷子類對象時(shí),執(zhí)行相反的順序沥潭,即首先撤銷子類的成員邀泉,執(zhí)行子類的析構(gòu)函數(shù),在撤銷父類成員钝鸽,執(zhí)行父類的析構(gòu)函數(shù)汇恤。

#include <iostream>

using namespace std;

class A

{

int a;

public:

A(int i = 0):a(i)

{

cout << "A is constructed" <<endl;

}

~A()

{

cout << "A is destructed" <<endl;

}

};

class B:public A

{

int b;

public:

B(int j = 0):b(j)

{

cout << "B is constructed" <<endl;

}

~B()

{

cout << "B is destructed" <<endl;

}

};

int main()

{

B b;

return 0;

}

構(gòu)造順序:

(1)調(diào)用基類的構(gòu)造函數(shù)。

(2)根據(jù)類中聲明的順序構(gòu)造組合對象拔恰。

(3)派生類中構(gòu)造函數(shù)的執(zhí)行因谎。

派生類中構(gòu)造函數(shù)的格式:

class 派生類名:[public|private|protected]基類名

{

public:

派生類名(參數(shù)列表1):基類名(參數(shù)列表2),組合對象列表{...}

};

組合類中構(gòu)造函數(shù)與析構(gòu)函數(shù)的調(diào)用順序:

#include <iostream>

using namespace std;

class X

{

public:

X()

{

cout << "X is constructed" <<endl;

}

~X()

{

cout << "X is destructed" <<endl;

}

};

class A

{

int a;

X x;

public:

A(int i = 0)

{

cout << "A is constructed" <<endl;

}

~A()

{

cout << "A is destructed" <<endl;

}

};

class Y

{

int y;

public:

Y(int i = 0)

{

y = i;

cout << "Y is constructed" <<endl;

}

~Y()

{

cout << "Y is destructed" <<endl;

}

};

class Z

{

int z;

public:

Z(int i = 0)

{

z = i;

cout << "Z is constructed" <<endl;

}

~Z()

{

cout << "Z is destructed" <<endl;

}

};

class B:public A

{

int b;

Y y;

Z z;

public:

B(int j = 0):A(1),z(j),b(j),y(j)

{

cout << "B is constructed" <<endl;

}

~B()

{

cout << "B is destructed" <<endl;

}

};

int main()

{

B b;

return 0;

}

如果基類的構(gòu)造函數(shù)沒有參數(shù),或者沒有顯示定義構(gòu)造函數(shù)時(shí)仁连,派生類構(gòu)造時(shí)可以不向基類傳遞參數(shù)蓝角,甚至可以不定義構(gòu)造函數(shù)。

注意:

(1)派生類不能繼承基類的構(gòu)造函數(shù)和析構(gòu)函數(shù)饭冬。當(dāng)基類帶有參數(shù)的構(gòu)造函數(shù)時(shí)使鹅,派生類必須定義構(gòu)造函數(shù),以便把參數(shù)傳遞給基類構(gòu)造函數(shù)昌抠。

(2)當(dāng)派生類也作為基類使用時(shí)患朱,則各派生類只負(fù)責(zé)其直接的基類的構(gòu)造。

(3)因?yàn)槲鰳?gòu)函數(shù)不帶參數(shù)炊苫,派生類中析構(gòu)函數(shù)的存在不依賴于基類裁厅,基類中析構(gòu)函數(shù)的存在也不依賴于派生類。

繼承類中構(gòu)造與析構(gòu)函數(shù)的調(diào)用順序:

#include <iostream>

using namespace std;

class A

{

int a;

public:

A(int i = 0)

{

cout << "A is constructed" <<endl;

}

~A()

{

cout << "A is destructed" <<endl;

}

};

class Y

{

int y;

public:

Y(int i = 0)

{

y = i;

cout << "Y is constructed" <<endl;

}

~Y()

{

cout << "Y is destructed" <<endl;

}

};

class B:public A

{

int b;

Y y;

public:

B(int j = 0):b(j),y(j)

{

cout << "B is constructed" <<endl;

}

~B()

{

cout << "B is destructed" <<endl;

}

};

class C:public B

{

int c;

public:

C(int i):B(1),c(i)

{

cout << "C is constructed" <<endl;

}

~C()

{

cout << "C is destructed" <<endl;

}

};

int main()

{

C c(2);

return 0;

}

7.5派生類重載基類函數(shù)的訪問

如果在基類中有一個(gè)函數(shù)名被重載幾次侨艾,在派生類中又重定義了這個(gè)函數(shù)名执虹,則在派生類中會(huì)覆蓋這個(gè)函數(shù)的所有基類定義。也就是說唠梨,通過派生類來訪問該函數(shù)時(shí)袋励,由于采用就近匹配的原則,只會(huì)調(diào)用在派生類中所定義的該函數(shù),基類中所定義的函數(shù)都變得不再可用茬故。

派生類中重載基類函數(shù):

#include <iostream>

using namespace std;

class Number

{

public:

void print(int i) {cout << i;}

void print(char c) {cout << c;}

void print(float f){cout << f;}

};

class Data:public Number

{

public:

void print(){}

void f()

{

//print(5); //錯(cuò)誤盖灸,因?yàn)樾露x的print()函數(shù)不帶參數(shù)

}

};

int main()

{

Data data;

data.print(); //正確

data.print(1); //錯(cuò)誤

//data.print(12.3); //錯(cuò)誤

//data.print('a'); //錯(cuò)誤

return 0;

}

因?yàn)镈ata類中對print()函數(shù)做了重定義,所以沒有找到一個(gè)匹配的函數(shù)給Data類對象調(diào)用磺芭。如果要訪問基類中聲明的函數(shù)赁炎,則用以下方法:

(1)使用作用域標(biāo)識符限定。

Data.Number::print(5); //正確钾腺,被調(diào)用的函數(shù)是基類Number的print(int)

(2)避免名稱覆蓋徙垫。

class Number

{

public:

void print(int i) {cout << i;}

void print(char c) {cout << c;}

void print(float f) {cout << f;}

};

class Data:public Number

{

public:

void print2(){}

void f()

{

print(1);

}

};

函數(shù)f()中調(diào)用基類函數(shù)print()的方法有稱為”向上映射”,即派生類向上調(diào)用基類的函數(shù)垮庐。向上映射是派生類中在不引用歧義的情形下才有松邪,即沒有名稱覆蓋發(fā)生。

7.6多態(tài)性&虛函數(shù)

面向?qū)ο蟪绦蛟O(shè)計(jì)的真正優(yōu)勢不僅僅在于繼承哨查,還在于將派生類對象當(dāng)基類對象一樣處理的功能逗抑。支持這種功能的機(jī)制就是多態(tài)和動(dòng)態(tài)綁定。

多態(tài)是指同樣的消息被不同類型的對象接收時(shí)導(dǎo)致不同的行為寒亥。所謂消息是指對類的成員函數(shù)的調(diào)用邮府,不同的行為是指不同的實(shí)現(xiàn),也就是調(diào)用了不同的函數(shù)溉奕。最簡單的例子就是運(yùn)算符褂傀,使用同樣的加號”+”,就可以實(shí)現(xiàn)整型數(shù)之間加勤、浮點(diǎn)數(shù)之間仙辟、雙精度浮點(diǎn)數(shù)之間的加法,以及這幾種數(shù)據(jù)類型混合的加法運(yùn)算鳄梅。同樣的相加操作叠国,被不同類型的對象接收后,不同類型的變量采用不同的方式進(jìn)行加法運(yùn)算戴尸。如果是不同類型的變量相加粟焊,例如浮點(diǎn)數(shù)和整型數(shù),則要先將整型數(shù)轉(zhuǎn)換為浮點(diǎn)數(shù)孙蒙,然后再進(jìn)行加法運(yùn)算项棠,這就是典型的多態(tài)現(xiàn)象。

多態(tài)的類型:面向?qū)ο蟮亩鄳B(tài)性可以分為4類挎峦,重載多態(tài)香追、強(qiáng)制多態(tài)、包含多態(tài)和參數(shù)多態(tài)坦胶。前面兩種統(tǒng)稱為專用多態(tài)翅阵,而后面的兩種稱為通用多態(tài)歪玲。之前所學(xué)習(xí)的普通函數(shù)、類的成員函數(shù)的重載以及運(yùn)算符重載都屬于重載多態(tài)掷匠。上述加法運(yùn)算符在進(jìn)行浮點(diǎn)數(shù)與整型數(shù)相加時(shí),首先進(jìn)行類型強(qiáng)制轉(zhuǎn)換岖圈,把整數(shù)變?yōu)楦↑c(diǎn)數(shù)再相加的情況讹语,就是強(qiáng)制多態(tài)的實(shí)例。包含多態(tài)是類族定義與不同類中的同名成員函數(shù)的多態(tài)行為蜂科,主要是通過虛函數(shù)來實(shí)現(xiàn)的顽决。參數(shù)多態(tài)與類模板相關(guān)聯(lián),在使用時(shí)必須賦予實(shí)際的類型才可以實(shí)例化导匣。這樣才菠,由類模板實(shí)例化的各個(gè)類都具有相同的操作,而操作對象的類型卻各不相同贡定。

多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計(jì)的重要特征赋访,重載和虛函數(shù)是體現(xiàn)多態(tài)性的兩個(gè)重要手段。虛函數(shù)體現(xiàn)了多態(tài)的靈活性缓待,進(jìn)一步減少冗余信息蚓耽,顯著提高了軟件的可擴(kuò)充性。通過學(xué)習(xí)函數(shù)重載與繼承方法后旋炒,經(jīng)常會(huì)遇到以下問題:在派生類中存在對基類函數(shù)的重載步悠,當(dāng)通過派生類對象調(diào)用重載函數(shù)時(shí)卻調(diào)用了基類的原函數(shù)。

通過派生類對象間接調(diào)用重載函數(shù):

#include <iostream>

using namespace std;

class Ins

{

public:

void play() const

{

cout << "Ins::play" <<endl;

}

};

class Piano:public Ins

{

public:

void play() const

{

cout << "Piano::play" <<endl;

}

};

void tune(Ins &i) //由參數(shù)類型決定

{

i.play();

}

int main()

{

Piano a;

tune(a);

return 0;

}

7.6.1靜態(tài)綁定與動(dòng)態(tài)綁定

綁定瘫镇,又稱聯(lián)編鼎兽,是使一個(gè)計(jì)算機(jī)程序的不同部分彼此關(guān)聯(lián)的過程。根據(jù)進(jìn)行綁定所處階段的不同铣除,有兩種不同的綁定方法谚咬,靜態(tài)綁定和動(dòng)態(tài)綁定。

(1)靜態(tài)綁定在編譯階段完成通孽,所有綁定過程都在程序開始之前完成序宦。靜態(tài)綁定具有執(zhí)行速度快的特點(diǎn),因?yàn)樵诔绦蜻\(yùn)行前背苦,編譯程序能夠進(jìn)行代碼優(yōu)化互捌。

函數(shù)重載(包括成員函數(shù)重載和派生類對基類函數(shù)的重載)就是靜態(tài)綁定。而上例中的問題根源在于:通過指針或引用引起的對普通成員函數(shù)的調(diào)用行剂,由參數(shù)的類型決定秕噪,而在指針或引用實(shí)際指向的對象無關(guān)。這也是靜態(tài)綁定的限定性厚宰。

(2)如果編譯器在編譯階段不確切地知道把發(fā)送到對象的消息和實(shí)現(xiàn)消息的哪段代碼具體聯(lián)系到一起腌巾,而是運(yùn)行時(shí)才把函數(shù)調(diào)用與函數(shù)具體聯(lián)系在一起遂填,就稱作動(dòng)態(tài)綁定。相對于靜態(tài)綁定澈蝙,動(dòng)態(tài)綁定是在編譯后綁定吓坚,也稱晚綁定,又稱運(yùn)行時(shí)識別灯荧。動(dòng)態(tài)綁定具有靈活性好礁击、更高級更自然的問題抽象、易于擴(kuò)充和易于維護(hù)等特點(diǎn)逗载。通過動(dòng)態(tài)綁定哆窿,可以動(dòng)態(tài)地根據(jù)指針或引用指向的對象實(shí)際類型來選擇調(diào)用的函數(shù)。

7.6.2虛函數(shù)

虛函數(shù)定義格式:

class 基類名

{

...

virtual 返回值類型 要在派生類中重載的函數(shù)名(參數(shù)列表);

};

用虛函數(shù)方法實(shí)現(xiàn)對派生類中重載函數(shù)的調(diào)用:

#include <iostream>

using namespace std;

class Ins

{

public:

virtual void play() const

{

cout << "Ins::play" <<endl;

}

};

class Piano:public Ins

{

public:

void play() const

{

cout << "Piano::play" <<endl;

}

};

class Newpiano:public Piano

{

public:

void play() const

{

cout << "Newpiano::play" <<endl;

}

};

void tune(Ins &i) //由參數(shù)類型決定

{

i.play();

}

int main()

{

Piano a;

tune(a);

Newpiano b;

tune(b);

Ins *p = &a;

tune(*p);

p = &b;

tune(*p);

Ins c;

tune(c);

return 0;

}

使用虛函數(shù)時(shí)需要注意:

(1)必須在基類中聲明虛函數(shù)厉斟,即需要在派生類中重載的函數(shù)挚躯,必須在基類中聲明為虛函數(shù)。

(2)虛函數(shù)一經(jīng)聲明擦秽,在派生類中重載的基類中的函數(shù)即是虛函數(shù)码荔,不需要再加virtual。

(3)只有非靜態(tài)成員函數(shù)可以聲明為虛函數(shù)号涯。靜態(tài)成員函數(shù)和全局函數(shù)不能聲明為虛函數(shù)目胡。

(4)編譯器把名稱相同、參數(shù)不同的函數(shù)看做不同的函數(shù)链快∮海基類和派生類中有相同名字但是參數(shù)列表不同的函數(shù),不需要聲明為虛函數(shù)域蜗。

(5)普通對象調(diào)用虛函數(shù)巨双,系統(tǒng)仍然以靜態(tài)綁定方式調(diào)用函數(shù)。因?yàn)榫幾g器編譯時(shí)能確切知道對象的類型霉祸,能確切調(diào)用其成員函數(shù)筑累。

Piano x;

Newpiano y;

X = y;

tune(x);

運(yùn)行結(jié)果是Piano::play

7.6.3虛析構(gòu)函數(shù)

通過學(xué)習(xí)虛函數(shù),可以掌握虛函數(shù)在繼承和派生中的調(diào)用方式丝蹭。

(1)構(gòu)造函數(shù)不能聲明為虛函數(shù)慢宗。因?yàn)闃?gòu)造函數(shù)有特殊的工作,它處在對象創(chuàng)建初期奔穿,首先調(diào)用基類構(gòu)造函數(shù)镜沽,然后調(diào)用按照繼承順序派生的派生類的構(gòu)造函數(shù)。

(2)析構(gòu)函數(shù)能夠且常常必須是虛函數(shù)贱田。析構(gòu)函數(shù)調(diào)用順序與構(gòu)造函數(shù)完全相反缅茉,從最晚派生類開始,依次向上到基類男摧。析構(gòu)函數(shù)確切地知道它是從哪個(gè)類派生而來的蔬墩。

虛析構(gòu)函數(shù)聲明格式:

virtual ~析構(gòu)函數(shù)名稱();

虛析構(gòu)函數(shù)定義:

class Ins

{

public:

virtual void play()

{

cout << “Ins::play” <<endl;

}

virtual ~Ins();

};

虛函數(shù)的目的是讓派生類去做”自己想做的事”译打。所以應(yīng)該在基類中聲明虛析構(gòu)函數(shù)。當(dāng)類中存在虛函數(shù)時(shí)拇颅,也應(yīng)該使用虛析構(gòu)函數(shù)奏司。這樣保證類對象銷毀時(shí)得到”完整”空間。如果某個(gè)類不包含虛函數(shù)蔬蕊,一般是表示它將不作為一個(gè)基類來使用结澄。當(dāng)一個(gè)類不準(zhǔn)備作為基類使用時(shí),建議不要將析構(gòu)函數(shù)聲明為虛函數(shù)岸夯,以保證程序執(zhí)行的高效性。

7.7純虛函數(shù)和抽象基類

工作有時(shí)需要定義這么一個(gè)類们妥,對這個(gè)類中的處理函數(shù)只需要說明函數(shù)的名稱猜扮、參數(shù)列表,以及返回值的類型监婶,也就是只提供一個(gè)接口旅赢,以說明和規(guī)范其他程序?qū)Υ朔?wù)的調(diào)用。至于這個(gè)函數(shù)如何實(shí)現(xiàn)惑惶,則根據(jù)具體需要在派生類中定義即可煮盼。通常把這樣的類稱為抽象基類,而把這樣的函數(shù)稱為純虛函數(shù)带污。純虛函數(shù)定義格式:

virtual 返回值類型 函數(shù)名稱(參數(shù)列表) = 0;

當(dāng)一個(gè)類中存在純虛函數(shù)時(shí)僵控,這個(gè)類就是抽象類。

class Ins

{

public:

virtual void play()const = 0; //純虛函數(shù)

};

抽象類的主要作用是通過它為一個(gè)類建立一個(gè)公共的接口鱼冀,使他們能夠更有效地發(fā)揮多態(tài)特性报破。使用抽象類時(shí)要注意:

(1)抽象類只能用做其他類的基類,不能建立抽象類對象千绪。抽象類處于繼承層次結(jié)構(gòu)的較上層充易,抽象類自身無法實(shí)例化,只能通過繼承機(jī)制荸型,生成抽象類的非抽象派生類盹靴,然后再實(shí)例化(抽象類提供了若干函數(shù)接口,當(dāng)有其他類需要時(shí)瑞妇,可以通過多重繼承來使用這些函數(shù)接口稿静,而新派生類不是一個(gè)抽象類,所以可以實(shí)例化)踪宠。

(2)抽象類不能用做參數(shù)類型自赔、函數(shù)返回值或顯式轉(zhuǎn)換的類型。

(3)可以聲明一個(gè)抽象類的指針和引用柳琢。通過指針或引用绍妨,可以指向并訪問派生類對象润脸,以訪問派生類的成員。

(4)抽象類派生出新的類之后他去,如果派生類給出所有純虛函數(shù)的函數(shù)實(shí)現(xiàn)毙驯,這個(gè)派生類就可以聲明自己的對象,因而不再是抽象類灾测;反之爆价,如果派生類沒有給出全部純虛函數(shù)的實(shí)現(xiàn),這時(shí)的派生類仍然是一個(gè)抽象類媳搪。

用抽象類的方法:

#include <iostream>

using namespace std;

class Ins

{

public:

virtual void play() const = 0;

};

class Piano:public Ins

{

public:

void play() const

{

cout << "Piano::play" <<endl;

}

};

class Newpiano:public Piano

{

public:

void play() const

{

cout << "Newpiano::play" <<endl;

}

};

void tune(Ins &i) //由參數(shù)類型決定

{

i.play();

}

int main()

{

Piano a;

tune(a);

Newpiano b;

tune(b);

Ins *p = &a;

tune(*p);

p = &b;

tune(*p);

//Ins c; //錯(cuò)誤铭段,Ins是抽象類,不能實(shí)例化

return 0;

}

純虛函數(shù)非常有用秦爆,因?yàn)樗沟妙愑忻黠@的抽象性序愚,并告訴用戶和編譯器希望如何使用。在基類中等限,對純虛函數(shù)提供定義時(shí)有可能的爸吮,告訴編譯器不允許純抽象基類聲明對象,而且純虛函數(shù)在派生類中必須定義望门,以便于創(chuàng)建對象形娇。然而,如果希望一塊代碼對于一些或所有派生類定義能共同使用筹误,而不希望在每個(gè)函數(shù)中重復(fù)這段代碼桐早,具體實(shí)現(xiàn)方法如下:

#include <iostream>

using namespace std;

class Ins

{

public:

virtual void play() const = 0;

virtual void showmsg() const

{

cout << "Ins::showmsg()" <<endl;

}

};

class Piano:public Ins

{

public:

void play() const

{

cout << "Piano::play" <<endl;

}

void showmsg()const

{

Ins::showmsg();

}

};

class Newpiano:public Piano

{

public:

void play() const

{

cout << "Newpiano::play" <<endl;

}

void showmsg()const

{

Ins::showmsg();

}

};

void tune(Ins &i) //由參數(shù)類型決定

{

i.play();

}

int main()

{

Piano a;

tune(a);

a.showmsg();

Newpiano b;

tune(b);

b.showmsg();

return 0;

}

7.8 多重繼承

在派生類的聲明中,基類名可以有一個(gè)纫事,也可以有多個(gè)勘畔。如果只有一個(gè)基類名,則這種繼承方式稱為單繼承丽惶;如果基類名有多個(gè)炫七,則這種繼承方式稱為多繼承,這時(shí)的派生類同時(shí)得到了多個(gè)已有類的特征钾唬。

7.8.1多繼承語法

多繼承允許派生類有兩個(gè)或多個(gè)基類的能力万哪,這是為了使多個(gè)類以這種方式組合起來,使得派生類對象的行為具有多個(gè)基類對象的特征抡秆。多繼承聲明語法:

class 派生類名:[繼承方式]基類名1奕巍,[繼承方式]基類名2,...[繼承方式]基類名n

多繼承的使用:

#include <iostream>

using namespace std;

class A

{

int a;

public:

void SetA(int x)

{

a = x;

}

};

class B

{

int b;

public:

void SetB(int x)

{

b = x;

}

};

class C:public A, private B

{

int c;

public:

void SetC(int , int, int);

};

void C::SetC(int x, int y, int z)

{

SetA(x);

SetB(y);

c = z;

}

int main()

{

C obj;

obj.SetA(5);

obj.SetC(6, 7, 9);

//obj.setB(6); //錯(cuò)誤儒士,不能訪問私有繼承的基類成員

return 0;

}

7.8.2多繼承中的二義性

多繼承中的二義性例子:

#include <iostream>

using namespace std;

class A

{

int a;

public:

void f(int x)

{

a = x;

}

};

class B

{

int b;

public:

void f(int x)

{

b = x;

}

};

class C:public A, private B

{

int c;

public:

void SetC(int);

};

void C::SetC(int x)

{

c = x;

}

int main()

{

C obj;

//obj.f(5); //錯(cuò)誤的止,不能訪問私有繼承的基類成員

return 0;

}

對編譯器來說,不知道是調(diào)用基類A的f()着撩,還是調(diào)用基類B的f()诅福。解決的辦法是使用域名控制來解決:

obj.A::f(5);

obj.B::f(5);

增加作用域分辨符可以消除二義性匾委,但降低了程序的可讀性。因此氓润,盡量避免在基類中使用同名函數(shù)赂乐。

多繼承里還有這樣的情況:有相同基類帶來的二義性。例如:

class base

{

...

public:

void show();

};

class c1:public base

{

...

public:

void show();

};

class c2:public base

{

...

public:

void show();

};

class a:public c1,public c2

{

...

public:

void show();

};

這種情況被稱為菱形繼承咖气。類a的父類c1挨措、c2繼承了基類base的成員函數(shù)show(),當(dāng)然類a也繼承了成員函數(shù)show()崩溪,a調(diào)用show()則產(chǎn)生了二義性浅役;類a繼承c1、c2的同時(shí)也包含了兩份基類base伶唯,菱形繼承使得子對象重疊担租,增加了額外的空間開銷谣旁。為了解決此類問題涩嚣,C++引入了虛基類莺债。

如把一個(gè)基類定義為虛基類,必須在派生子類時(shí)在父類的名字前加關(guān)鍵字virtual反惕。

Class 派生類名:virtual 訪問權(quán)限修飾符 父類名{};

虛基類使用方法:

#include <iostream>

using namespace std;

class base

{

public:

virtual char* show()const = 0;

};

class d1:virtual public base

{

public:

char* show()const{return (char*)"d1";}

};

class d2:virtual public base

{

public:

char* show()const{return (char*)"d2";}

};

class m:public d1,public d2

{

public:

char* show()const

{

return d2::show();

}

};

int main()

{

m m1;

cout << m1.show() <<endl;

return 0;

}

7.8.3最終派生類

在上一個(gè)例子中,各類沒有構(gòu)造函數(shù)演侯,使用的是默認(rèn)構(gòu)造函數(shù)姿染。如果類里有了構(gòu)造函數(shù),情形將有所不同秒际。如在上面的base基類里增加構(gòu)造函數(shù):

Base(int){}

則用派生類聲明對象時(shí)悬赏,編譯器報(bào)錯(cuò):沒有合適的構(gòu)造函數(shù)調(diào)用。為解決此類問題娄徊,首先引入最終派生類的概念闽颇。

最終派生類,又稱為最晚輩派生類寄锐,指當(dāng)前所在的類兵多。例如,在基類base的構(gòu)造函數(shù)里橄仆,base就是最終派生類剩膘;在派生類a里,a就稱為最終派生類盆顾;在類c1里怠褐,類c1就成為最終派生類。當(dāng)使用虛基類時(shí)您宪,尤其是帶有參數(shù)的構(gòu)造函數(shù)的虛基類時(shí)奈懒,最終派生類的構(gòu)造函數(shù)必須對虛基類初始化奠涌。不管派生類離虛基類多遠(yuǎn),都必須對虛基類初始化筐赔。

含虛基類的構(gòu)造函數(shù)使用方法:

#include <iostream>

using namespace std;

class base

{

int i;

public:

base(int x):i(x){}

int geti(){return i;}

virtual char* show()const

{

return (char*)"base";

}

};

class d1:virtual public base

{

int id1;

public:

d1(int x = 1):base(0),id1(x){}

char* show()const{return (char*)"d1";}

};

class d2:virtual public base

{

int id2;

public:

d2(int x = 2):base(1),id2(x){}

char* show()const{return (char*)"d2";}

};

class m:public d1,public d2

{

int im;

public:

m(int x = 0):base(3),im(x){}

char* show()const

{

return d2::show();

}

};

int main()

{

m m1;

cout << m1.show() <<endl;

cout << m1.geti() <<endl;

d1 d11;

cout << d11.geti() <<endl;

cout << d11.show() <<endl;

return 0;

}

使用虛基類時(shí)要注意:

(1)必須在派生類的構(gòu)造函數(shù)中調(diào)用初始化虛基類的構(gòu)造函數(shù)铣猩。

(2)給虛基類安排默認(rèn)構(gòu)造函數(shù)。使得虛基類的使用者變得簡單易行茴丰。

7.8.4多繼承的構(gòu)造順序

#include <iostream>

using namespace std;

class base

{

int i;

public:

base(int x):i(x)

{

cout << "base is cnostructed" <<endl;

}

virtual ~base()

{

cout << "base is destructed" <<endl;

}

int geti(){return i;}

virtual char* show()const = 0;

};

class d1:virtual public base

{

int id1;

public:

d1(int x = 1):base(0),id1(x)

{

cout << "d1 is constructed" <<endl;

}

virtual ~d1()

{

cout << "d1 is destructed" <<endl;

}

char* show()const{return (char*)"d1";}

};

class d2:virtual public base

{

int id2;

public:

d2(int x = 2):base(1),id2(x)

{

cout << "d2 is constructed" <<endl;

}

virtual ~d2()

{

cout << "d2 is destructed" <<endl;

}

char* show()const{return (char*)"d2";}

};

class m:public d1,public d2

{

int im;

public:

m(int x = 0):base(3),im(x)

{

cout << "m is constructed" <<endl;

}

virtual ~m()

{

cout << "m is destructed" <<endl;

}

char* show()const

{

return d2::show();

}

};

int main()

{

m m1;

cout << m1.show() <<endl;

return 0;

}

多繼承構(gòu)造順序與單繼承構(gòu)造順序類似达皿,從基類開始,沿著派生順序逐層向下贿肩,當(dāng)同一層次派生同一個(gè)類時(shí)峦椰,按照聲明繼承的順序自左到右。例如汰规,c1汤功、c2派生a時(shí),先構(gòu)造c1溜哮,再構(gòu)造c2滔金。析構(gòu)順序與構(gòu)造順序相反。

7.9派生類成員的標(biāo)識與訪問

經(jīng)過類的派生茂嗓,就形成了一個(gè)具有層次結(jié)構(gòu)的類族餐茵。接下來就看看派生類使用過程中的一些問題,也就是標(biāo)識和訪問派生類及其對象的成員問題述吸。

派生類中忿族,成員可以按訪問屬性劃分為以下4種:

(1)不可訪問的成員。這是從基類私有成員繼承而來的蝌矛,派生類或是建立派生類對象的模塊都沒有辦法訪問到它們道批,如果從派生類繼續(xù)派生新類,也是無法訪問的入撒。

(2)私有成員隆豹。包括從基類繼承過來的成員以及新增加的成員,在派生類內(nèi)部可以訪問衅金,但是建立派生類對象的模塊中無法訪問噪伊,繼續(xù)派生,就成為了新的派生類中的不可訪問成員氮唯。

(3)保護(hù)成員鉴吹。可能是新增也可能是從基類繼承過來的惩琉,派生類內(nèi)部成員可以訪問豆励,建立派生類對象的模塊無法訪問,進(jìn)一步派生,在新的派生類中可以成為私有成員或保護(hù)成員良蒸。

(4)公有成員技扼。派生類、建立派生類的模塊都可以訪問嫩痰,繼續(xù)派生剿吻,可能是新派生類中的私有、保護(hù)或者共有成員串纺。

7.9.1作用域分辨符

作用域分辨符丽旅,就是我們經(jīng)常見到的”::”,它可以用來限定要訪問的成員所在的類的名稱纺棺。一般使用形式是:

類名::成員名 //數(shù)據(jù)成員

類名::成員名(參數(shù)表) //函數(shù)成員

對于在不同的作用域聲明的標(biāo)識符榄笙,可見性原則是:如果存在兩個(gè)或多個(gè)具有包含關(guān)系的作用域,外層聲明了一個(gè)標(biāo)識符祷蝌,而內(nèi)層沒有再次聲明同名標(biāo)識符茅撞,那么外層標(biāo)識符在內(nèi)層仍然可見;如果在內(nèi)層聲明了同名標(biāo)識符巨朦,則外層標(biāo)識符在內(nèi)層不可見米丘,這時(shí)稱內(nèi)層標(biāo)識符隱藏了外層同名標(biāo)識符,這種現(xiàn)象稱為隱藏規(guī)則糊啡。

在類的派生層次結(jié)構(gòu)中蠕蚜,基類的成員和派生類新增的成員都具有類作用域。二者的作用范圍不同悔橄,是相互包含的兩個(gè)層,派生類在內(nèi)層腺毫。這是如果派生類聲明了一個(gè)和某個(gè)基類成員同名的新成員癣疟,派生的新成員就隱藏了外層同名成員,直接使用成員名只能訪問到派生類的成員潮酒。如果派生類中聲明了與基類成員函數(shù)同名的新函數(shù)睛挚,即使函數(shù)的參數(shù)表不同,從基類繼承的同名函數(shù)的所有重載形式也都會(huì)被隱藏急黎。如果要訪問被隱藏的成員扎狱,就需要使用作用域分辨符和基類名來限定。

對于多繼承情況勃教,首先要考慮各個(gè)基類之間有沒有任何繼承關(guān)系淤击,同時(shí)考慮有沒有共同基類的情況。最經(jīng)典的情況就是所有基類都沒有上級基類故源。如果某派生類的多個(gè)基類擁有同名的成員污抬,同時(shí),派生類有新增這樣的同名成員,在這種情況下印机,派生類成員將隱藏所有基類的同名成員矢腻。這時(shí)使用”對象名.成員名”或”對象指針->成員名”方式可以唯一標(biāo)識和訪問派生類新增成員,基類的同名成員也可以使用基類名和作用域分辨符訪問射赛。但是多柑,如果派生類沒有聲明同名成員,”對象名.成員名”或”對象指針->成員名”方式就無法唯一標(biāo)識成員楣责。這時(shí)竣灌,從不同基類繼承過來的成員具有相同的名稱,同時(shí)具有相同的作用域腐魂,系統(tǒng)僅僅根據(jù)這些信息根本無法判斷到底是調(diào)用哪個(gè)基類的成員帐偎,這時(shí)就必須通過基類名和作用域分辨符來標(biāo)識成員。

細(xì)節(jié):如果子類中定義的函數(shù)與父類的函數(shù)同名但是具有不同的參數(shù)數(shù)量或參數(shù)類型蛔屹,不屬于函數(shù)重載削樊,這時(shí)子類中的函數(shù)將使父類中的函數(shù)隱藏,調(diào)用父類中的函數(shù)必須使用父類名稱來限定兔毒。只有在相同的作用域中定義的函數(shù)才可以重載漫贞。

#include <iostream>

using namespace std;

class Base1

{

public:

int var;

void fun(){cout << “Member of Base1” <<endl;}

};

class Base2

{

public:

int var;

void fun(){cout << “Member of Base2” <<endl}

};

class Derived:public Base1,public Base2

{

public:

int var;

void fun(){cout << “Member of Derived” <<endl;}

};

int main()

{

Derived d;

Derived *p = &d;

d.var = 1;

d.fun();

d.Base1::var = 2;

d.Base2::fun();

p->Base2::var = 3;

p->Base2::fun();

return 0;

}

在主函數(shù)中,創(chuàng)建了一個(gè)派生類的對象d育叁,根據(jù)隱藏規(guī)則迅脐,如果通過成員名稱來訪問該類成員,就只能訪問到派生類新增加的兩個(gè)成員豪嗽,從基類繼承過來的成員由于處于外層作用域而被隱藏谴蔑。這時(shí)要想訪問從基類繼承來的成員,就必須使用類名和作用域分辨符龟梦。程序中后兩組語句就是分別訪問由基類Base1隐锭,Base2繼承來的成員。通過作用域分辨符计贰,就明確地唯一標(biāo)識了派生類中由基類所繼承來的成員钦睡,達(dá)到了訪問的目的,解決了成員被隱藏的問題躁倒。

如果將類Derived寫成這樣:

class Derived:public Base1,public Base2{};

此時(shí)在主函數(shù)中寫:

d.var = 1; //錯(cuò)誤荞怒,這樣會(huì)有二義性

d.fun(); //錯(cuò)誤,這樣會(huì)有二義性

如果希望d.var和d.fun()的用法不產(chǎn)生二義性秧秉,可以使用using關(guān)鍵字加以澄清褐桌。例如:

class Derived:public Base1,public Base2

{

public:

using Base1::var;

using Base2::fun;

};

這樣,d.var和d.fun()都可以明確表示對Base1中相關(guān)成員的引用了象迎。using的一般功能是將一個(gè)作用域中的名字引入到另一個(gè)作用域中撩嚼,它還有一個(gè)非常有用的用法:將using用于基類中的函數(shù)名,這樣派生類中如果定義同名但參數(shù)不同的參數(shù),基類的函數(shù)不會(huì)被隱藏完丽,兩個(gè)重載的函數(shù)將會(huì)并存在派生類的作用域中恋技。例如:

class Derived2:public Base1

{

public:

using Base1::fun;

void fun(int i){...}

};

這時(shí),使用Derived2的對象逻族,既可以直接調(diào)用無參數(shù)的fun()函數(shù)蜻底,又可以直接調(diào)用帶int型參數(shù)的fun函數(shù)。

發(fā)布于 19:44

C / C++

?贊同?添加評論

?分享

?收藏

?設(shè)置

?投稿

推薦閱讀

高曉松一句話暴露情商:會(huì)聊天的人聘鳞,從不說這3句話

槽值發(fā)表于槽值

楊超越是直男斬薄辅?看她穿很少女的裙子就知道了

曉文視角

我,一個(gè)直男抠璃,在親眼目睹了一次女人P圖后站楚,頓悟了

行尸走肥肉發(fā)表于直男實(shí)驗(yàn)室

細(xì)思極恐小故事系列

七味Ay

還沒有評論

寫下你的評論...

發(fā)布

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市搏嗡,隨后出現(xiàn)的幾起案子窿春,更是在濱河造成了極大的恐慌,老刑警劉巖采盒,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旧乞,死亡現(xiàn)場離奇詭異,居然都是意外死亡磅氨,警方通過查閱死者的電腦和手機(jī)尺栖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來烦租,“玉大人延赌,你說我怎么就攤上這事〔娉鳎” “怎么了皮胡?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長赏迟。 經(jīng)常有香客問我,道長蠢棱,這世上最難降的妖魔是什么锌杀? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮泻仙,結(jié)果婚禮上糕再,老公的妹妹穿的比我還像新娘。我一直安慰自己玉转,他們只是感情好突想,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般猾担。 火紅的嫁衣襯著肌膚如雪袭灯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天绑嘹,我揣著相機(jī)與錄音稽荧,去河邊找鬼。 笑死工腋,一個(gè)胖子當(dāng)著我的面吹牛姨丈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播擅腰,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼蟋恬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了趁冈?” 一聲冷哼從身側(cè)響起歼争,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎箱歧,沒想到半個(gè)月后矾飞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡呀邢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年洒沦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片价淌。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡申眼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蝉衣,到底是詐尸還是另有隱情括尸,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布病毡,位于F島的核電站濒翻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏啦膜。R本人自食惡果不足惜有送,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望僧家。 院中可真熱鬧雀摘,春花似錦、人聲如沸八拱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至清蚀,卻和暖如春匕荸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背轧铁。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工每聪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人齿风。 一個(gè)月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓药薯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親救斑。 傳聞我的和親對象是個(gè)殘疾皇子童本,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

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