第11章實現繼承

1.派生。在已有類的基礎上添加了新功能的類被成爲從原來的類派生而來。原來的類是新類的基類,派生類是基類的超集

2.例子:

#include <iostream>

using std:cout;

using std::endl;

enum BREED{GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB};

class Mammal//聲明一個類Mammal

{

public://公共屬性,下面的方法所有的外部類都可以訪問

Mamal():itsAge(2),itsWeight(5){}//構造函數,在實例化的時候運行

~Mammal(){}//析構函數,在對象銷燬時調用

int GetAge()const{return itsAge;}

void SetAge(int age){itsAge=age;}

int GeWeight()const{return itsWeight;}

void SetWeight(int weight){itsWeight=weight;}

void Speak()const{cout <<"mamal"<<endl;}

void Sleep()const{cout << "shhh"<<endl;}

 

protected://保護對象,只有類自己或派生類能訪問

int itsAge;

int itsWeight;

};//注意必須有分號

class Dog: public Mammal//定義一個類dog,並公共繼承Mammal類,也就是Mammal中的公共和保護的成員dog都可以用和訪問

{

public:

Dog():itsBreed(GOLDEN){}

~Dog(){}

BREED GetBreed()const{return itsBreed;}

void SetBreed(BREED breed){itsBreed=breed;}

void WagTail()const{cout<<"tail"<<endl;}

void BegForFood()const{cout <<"begging"<<endl;}

private:

BREED itsBreed;

};

int main()

{

Dog Fido;

Fido.Speak();//調用Fido的speak函數,由於Fido是dog的實例化,所以發現dog中沒有speak,那就調用基類Mammal的speak函數

Fido.WagTail();//調用Fido的wagtail函數

cout<<"Fido is"<Fido.GetAge()<<" years old"<<endl;

return 0;

}

輸出結果:

mamal

tail

Fido is 2 years old

3.構造函數和析構函數。每個類都有至少一個構造函數和唯一的一個析構函數。如果你沒有定義,那麼程序會給你定義默認的構造函數和析構函數。構造函數的格式是與類名相同,沒有返回值和類型,可以有參數。析構函數的格式是以~(波浪號)開頭,然後與類名相同,但不能有返回值和參數。例如

class Mammal

{

public:

Mammal();

Mammal(int age);

Mammal(float age);

~Mammal();

protected:

int age;

float agef;

};

Mammal::Mammal()//1

{

age=1;

}

Mammal::Mammal(int age)//2

{

this.age=age;

}

Mammal::Mammal(float age):age(4)//3. 調用這個構造函數時,會給age賦值3,

{

agef=age;

}

Mammal::~Mammal()//4

{

}

//實例化

Mammal Mybaby1;//這時就會調用1處的構造函數,裏面的age=1

Mammal Mybaby2(5);//這裏就會調用2出的構造函數,裏面的age=5

Mammal Mybaby3(6.4)//這裏就會調用3出的構造函數,裏面的agef=6.4,並且age=4

//當程序結束,銷燬時,Mybaby1,Mybaby2,Mybaby3都會銷燬,同樣都會調用一次4處的析構函數。

4.覆蓋。如果派生類中創建一個方法,它的參數列表和方法名字與基類相同,那麼就叫做覆蓋,返回值可以不同。

class Mammal

{

public:

void speak()const{cout << "sound" << endl;

}

class Dog : public Mammal

{

public:

void speak()const{cout << "woof" << endl;

}

Mammal mammal;

Dog dog;

mammal.speak();//這裏輸出的是sound

dog.speak();//這裏輸出的是woof

5.隱藏。上面dog的speak方法覆蓋了mammal的speak方法,我們也可以說是隱藏。如果基類有重載的方法,覆蓋時就要注意了。比如下面的例子

class Mammal

{

public:

void move()const{cout << "mammal move one step" << endl;}

void move(int distance){cout << "mamal move"<<distance<<" steps"<< endl;}

}

class Dog:public Mammal

{

public:

void move()const{cout <"dog move one step"<< endl;}

}

Mammal mammal;

Dog dog;

mammal.move();

mammal.move(3);

dog.move();

//dog.move(4);

上面的例子中Mammal中有個move函數,一個是沒有參數,一個是有一個int參數。但是dog中只有一個沒有參數的move函數。當你用Mammal的對象調用帶參數的move函數是可以的,但是Dog的對象不可以,因爲子類如果覆蓋了一個基類的函數,那麼基類的與這個函數相關的所有重載函數都會被隱藏。

所以覆蓋的時候要注意,需要的話就把所有的重載函數都覆蓋一遍,況且覆蓋的時候注意const,這個也是特徵標,和參數函數名一樣,必須一樣。

6.虛方法。

class Mammal

{

public:

void move() const{cout << " Mammal move"<< endl;}

virtual void speak() const{cout << "mammal speak" << endl;}

};

class Dog:public Mammal

{

public:

void move() const{cout << "dog move" << endl;}

void speak() cont{cout << "dog speak" << endl;}

};

Mammal *pDog=new Dog;

pDog->move();//輸出是mammal move

pDog->speak();//輸出是dog speak

上面在Mammal中定義了一個虛函數speak,其關鍵字是前面的virtual。然後實例化了一個Dog類的對象指針,但是是賦值給了一個Mammal的對象指針。這樣,調用pDog裏面的方法時,如果不是虛函數,那麼就會先調用Mammal的相應的方法,如果沒有再調用Dog的,如果是虛函數,則直接調用Dog的。

虛函數的作用就是,如果你有多個類,比如Cat, Pig, Monkey等分別繼承了Mammal,那麼可以直接調用一個Mammal的實例化對象的指針的speak函數,然後把不同的類的對象賦值給它,這個函數就會根據不同賦值的類,來調用不同的方法了。

比如接上面代碼

class Cat:public Mammal

{

public:

void move() const{cout << "cat move" << endl;}

void speak() cont{cout << "cat speak" << endl;}

};

Mammal *pMam=new Dog;

pMam->speak();//輸出結果 dog speak

pMam=new Cat;//這裏僅僅是爲了實驗,並不提倡直接這樣做,因爲dog的對象沒有釋放,成爲了野指針

pMam->speak();//輸出結果cat speak

7.切除.

class Mammal

{

public:

virtual void speak() const{cout << "mammal speak" << endl;}

};

class Dog:public Mammal

{

public:

void speak() cont{cout << "dog speak" << endl;}

};

void valudf(Mammal mv)

{

mv.speak();

}

void ptrf(Mammal * pm)

{

pm.speak();

}

void reff(Mammal & rm)

{

rm.speak();

}

Mammal * ptr=0;

ptr=new Dog;

valudf(*ptr);//輸出 mammal speak

ptrf(ptr);//輸出 dog speak

reff(*ptr);//輸出 dog speak

上面之所以是這樣,是因爲,傳遞指針或是引用時,程序能夠識別這個ptr的類型到底是Mammal還是dog,並根據虛函數來調用Dog的相應函數。而如果傳遞值,ptr裏面的Dog的部分被截斷,扔掉,然後賦值給了mv,所以在valudf函數內就只有Mammal的對象,所以調用了Mammal的函數

8.虛析構函數。如果雷轟任何一個函數是虛函數,那麼析構函數也應該是虛函數。因爲,如果用基類的指針來調用派生類的對象,那麼當對象銷燬時,我們希望的是調用派生類的析構函數,所以析構函數也應該是虛函數,不然就調用的是基類的析構函數。

9.不要把構造函數聲明爲虛方法。使用虛方法會創建對應的虛表,增加相應的開銷

10私有繼承。如果一個類只想使用基類的方法,不想對其做任何改變,可以使用私有繼承。

class em

{

pulbic:

void sm()

{

cout << "yes" << endl;

}

};

class fan :private em

{

public:

sfan()

{

sm();

}

};

fan mfan;

mfn.sfan();

mfn.sm();//這個是錯誤的編譯不過。因爲是私有繼承,只能在類的內部調用基類的公有或是保護函數,不能在外面調用。和類內部聲明的私有變量一樣

下章見(以上全是個人觀點,如有不當,敬請提出)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章