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();//這個是錯誤的編譯不過。因爲是私有繼承,只能在類的內部調用基類的公有或是保護函數,不能在外面調用。和類內部聲明的私有變量一樣
下章見(以上全是個人觀點,如有不當,敬請提出)