C++三大特性- - -多態

多態

多態:通俗說就是多種形態,具體點就是去完成某個行爲,當不同的對象去完成時會產生出不同的狀態。

1. 多態的定義及實現

1.1 多態構成條件

多態是在不同繼承關係的類對象,去調用同一函數,產生了不同的行爲。比如Student繼承了Person。Person對象買票全價,Student對象買票半價。

構成多態的兩個條件

  • 1.必須通過基類的指針或者引用調用虛函數;
  • 2.被調用的函數必須是虛函數,且派生類必須對基類的虛函數進行重寫;

1.2 虛函數

虛函數:被 virtual 修飾的類成員函數。

class Person {
public:
 virtual void BuyTicket() { cout << "買票-全價" << endl;}
};

1.3 虛函數的重寫

虛函數的重寫(覆蓋):派生類中有一個跟基類完全相同的虛函數(即派生類虛函數與基類虛函數的返回值類
型、函數名字、參數列表完全相同
),稱子類的虛函數重寫了基類的虛函數。

class Person {
public:
 virtual void BuyTicket() { cout << "買票-全價" << endl; }
};
class Student : public Person {
public:
 virtual void BuyTicket() { cout << "買票-半價" << endl; }
 
 /*注意:在重寫基類虛函數時,派生類的虛函數在不加virtual關鍵字時,雖然也可以構成重寫(因爲繼
承後基類的虛函數被繼承下來了在派生類依舊保持虛函數屬性),但是該種寫法不是很規範,不建議這樣使用
*/
 /*void BuyTicket() { cout << "買票-半價" << endl; }*/
};
void Func(Person& p)  //必須是指針或者引用
{ p.BuyTicket(); }
int main()
{
 Person ps;
 Student st;
 
 Func(ps);
 Func(st);
 return 0; }

虛函數重寫的兩個例外

  • 1、協變(基類與派生類虛函數返回值類型不同)
    派生類重寫基類虛函數時,與基類虛函數返回值類型不同。即基類虛函數返回基類對象的指針或者引用,派生類虛函數返回派生類對象的指針或者引用時,稱爲協變。
class A{};
class B : public A {};
class Person {
public:
 virtual A* f() {return new A;}
};
class Student : public Person {
public:
 virtual B* f() {return new B;}
};
  • 2、析構函數的重寫(基類與派生類析構函數的名字不同)
    如果基類的析構函數爲虛函數,此時派生類析構函數只要定義,無論是否加virtual關鍵字,都與基類的析構函數構成重寫,雖然基類與派生類析構函數名字不同。雖然函數名不相同,看起來違背了重寫的規則,其實不然,這裏可以理解爲編譯器對析構函數的名稱做了特殊處理,編譯後析構函數的名稱統一處理成destructor。
    只有派生類Student的析構函數重寫了Person的析構函數,下面的delete對象調用析構函數,才能構成多態,才能保證p1和p2指向的對象正確的調用析構函數。
class Person {
public:
 virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:
 virtual ~Student() { cout << "~Student()" << endl; }
};
// 只有派生類Student的析構函數重寫了Person的析構函數,下面的delete對象調用析構函數,才能構成多態,才能保證p1和p2指向的對象正確的調用析構函數。
int main()
{
 Person* p1 = new Person;
 Person* p2 = new Student;
 delete p1;
 delete p2;
 return 0;
}

下列代碼輸出的結果是什麼?

class A {
public:
 virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}
 virtual void test(){ func();}
};
class B : public A {
public:
 void func(int val=0){ std::cout<<"B->"<< val <<std::endl; }
};
int main(int argc ,char* argv[])
{
 B*p = new B;
 p->test();
 return 0; }

答案:: B->1,B是A的重寫,但是缺省參數用的仍然是A中的。

1.4 c++11中的 override 和 final

1、final:修飾虛函數,表示該虛函數不能再被繼承

class Car
{
public:
 virtual void Drive() final {}
};
class Benz :public Car
{
public:
 virtual void Drive() {cout << "Benz-舒適" << endl;}
};

2、 override: 檢查派生類虛函數是否重寫了基類某個虛函數,如果沒有重寫編譯報錯。

class Car{
public:
 virtual void Drive(){}
};
class Benz :public Car {
public:
 virtual void Drive() override {cout << "Benz-舒適" << endl;}
}

1.5 重載、覆蓋(重寫)、隱藏(重定義)的區別

在這裏插入圖片描述

2、抽象類

2.1 純虛函數

在虛函數的後面寫上 =0 ,則這個函數爲純虛函數。包含純虛函數的類叫做抽象類(也叫接口類),抽象類不能實例化出對象。派生類繼承後也不能實例化出對象,只有重寫純虛函數,派生類才能實例化出對象。純虛函數規範了派生類必須重寫,另外純虛函數更體現出了接口繼承。

純虛函數的作用

  • 1、強制子類去完成重寫;
  • 2、表示抽象的類型。抽象就是現實中沒有對應的實體。
class Car
{
public:
 virtual void Drive() = 0; //不需要實現,純虛函數
};

//還是一個抽象類
class Benz :public Car
{
public:
 virtual void Drive()
 {
 cout << "Benz-舒適" << endl;
 }
};
int main()
{
 	Car car;
 	Benz bz;
 	
 	return 0;
 }
 

2.2 接口繼承和實現繼承

普通函數的繼承是一種實現繼承,派生類繼承了基類函數,可以使用函數,繼承的是函數的實現。虛函數的繼承是一種接口繼承,派生類繼承的是基類虛函數的接口,目的是爲了重寫,達成多態,繼承的是接口。所以如果不實現多態,不要把函數定義成虛函數

3、多態的原理

3.1 虛函數表

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