天天看點

多态性與虛函數(筆記)一、C++多态的概念以及用途二、C++虛函數詳解三、C++純虛函數和抽象類

多态性與虛函數(筆記)

  • 一、C++多态的概念以及用途
    • 簡而言之:(指向基類的指針,指向派生類時,無法通路派生類的成員函數,而借助聲明虛函數,可以實作基類指針指向派生類時,通路派生類的成員函數,進而有了多态)
  • 二、C++虛函數詳解
  • 三、C++純虛函數和抽象類

一、C++多态的概念以及用途

簡而言之:(指向基類的指針,指向派生類時,無法通路派生類的成員函數,而借助聲明虛函數,可以實作基類指針指向派生類時,通路派生類的成員函數,進而有了多态)

  • 基類的指針也可以指向派生類對象,請看下面的例子:
#include <iostream>
using namespace std;

//基類People
class People{
public:
    People(char *name, int age);
    void display();
protected:
    char *m_name;
    int m_age;
};
People::People(char *name, int age): m_name(name), m_age(age){}
void People::display(){
    cout<<m_name<<"今年"<<m_age<<"歲了,是個無業遊民。"<<endl;
}

//派生類Teacher
class Teacher: public People{
public:
    Teacher(char *name, int age, int salary);
    void display();
private:
    int m_salary;
};
Teacher::Teacher(char *name, int age, int salary): People(name, age), m_salary(salary){}
void Teacher::display(){
    cout<<m_name<<"今年"<<m_age<<"歲了,是一名教師,每月有"<<m_salary<<"元的收入。"<<endl;
}

int main(){
    People *p = new People("王志剛", 23);
    p -> display();

    p = new Teacher("趙宏佳", 45, 8200);
    p -> display();

    return 0;
}
           

運作結果:

王志剛今年23歲了,是個無業遊民。

趙宏佳今年45歲了,是個無業遊民。

當基類指針 p 指向派生類 Teacher 的對象時,雖然使用了 Teacher 的成員變量,但是卻沒有使用它的成員函數,導緻輸出結果不倫不類(趙宏佳本來是一名老師,輸出結果卻顯示人家是個無業遊民),不符合我們的預期。

  • 換句話說,通過基類指針隻能通路派生類的成員變量,但是不能通路派生類的成員函數。
  • 為了消除這種尴尬,讓基類指針能夠通路派生類的成員函數,C++ 增加了虛函數(Virtual Function)。使用虛函數非常簡單,隻需要在函數聲明前面增加 virtual 關鍵字。

更改上面的代碼,将 display() 聲明為虛函數:

//基類People
class People{
public:
    People(char *name, int age);
    virtual void display();  //聲明為虛函數
protected:
    char *m_name;
    int m_age;
};
           

運作結果:

王志剛今年23歲了,是個無業遊民。

趙宏佳今年45歲了,是一名教師,每月有8200元的收入。

  • 有了虛函數,基類指針指向基類對象時就使用基類的成員(包括成員函數和成員變量),指向派生類對象時就使用派生類的成員。 換句話說,基類指針可以按照基類的方式來做事,也可以按照派生類的方式來做事,它有多種形态, 或者說有多種表現方式,我們将這種現象稱為多态(Polymorphism)。
  • C++提供多态的目的是:可以通過基類指針對所有派生類(包括直接派生和間接派生)的成員變量和成員函數進行“全方位”的通路,尤其是成員函數。如果沒有多态,我們隻能通路成員變量。

二、C++虛函數詳解

虛函數對于多态具有決定性的作用,有虛函數才能構成多态,重點說一下虛函數的注意事項。

  • 1.隻需要在虛函數的聲明處加上 virtual 關鍵字,函數定義處可以加也可以不加。
  • 2.為了友善,你可以隻将基類中的函數聲明為虛函數,這樣所有派生類中具有遮蔽(覆寫)關系的同名函數都将自動成為虛函數。
  • 3.當在基類中定義了虛函數時,如果派生類沒有定義新的函數來遮蔽此函數,那麼将使用基類的虛函數。
  • 4.隻有派生類的虛函數遮蔽基類的虛函數(函數原型相同)才能構成多态(通過基類指針通路派生類函數)。

    例如基類虛函數的原型為virtual void func();,派生類虛函數的原型為virtual void func(int);,那麼當基類指針 p 指向派生類對象時,語句p -> func(100);将會出錯,而語句p -> func();将調用基類的函數。

  • 5.構造函數不能是虛函數。 對于基類的構造函數,它僅僅是在派生類構造函數中被調用,這種機制不同于繼承。也就是說,派生類不繼承基類的構造函數,将構造函數聲明為虛函數沒有什麼意義。
  • 6.析構函數可以聲明為虛函數;

三、C++純虛函數和抽象類

在C++中,可以将虛函數聲明為純虛函數,文法格式為:

virtual 傳回值類型 函數名 (函數參數) = 0;

純虛函數沒有函數體,隻有函數聲明,在虛函數聲明的結尾加上=0,表明此函數為純虛函數。

最後的=0并不表示函數傳回值為0,它隻起形式上的作用,告訴編譯系統“這是純虛函數”。
  • 包含純虛函數的類稱為抽象類(Abstract Class)。之是以說它抽象,是因為它無法執行個體化,也就是無法建立對象。原因很明顯,純虛函數沒有函數體,不是完整的函數,無法調用,也無法為其配置設定記憶體空間。
  • 抽象類通常是作為基類,讓派生類去實作純虛函數。 派生類必須實作純虛函數才能被執行個體化。
  • 抽象基類除了限制派生類的功能,還可以實作多态。請注意第 51 行代碼,指針 p 的類型是 Line,但是它卻可以通路派生類中的 area() 和 volume() 函數,正是由于在 Line 類中将這兩個函數定義為純虛函數;如果不這樣做,51 行後面的代碼都是錯誤的。,
#include <iostream>
using namespace std;

//線
class Line{
public:
    Line(float len);
    virtual float area() = 0;
    virtual float volume() = 0;
protected:
    float m_len;
};
Line::Line(float len): m_len(len){ }

//矩形
class Rec: public Line{
public:
    Rec(float len, float width);
    float area();
protected:
    float m_width;
};
Rec::Rec(float len, float width): Line(len), m_width(width){ }
float Rec::area(){ return m_len * m_width; }

//長方體
class Cuboid: public Rec{
public:
    Cuboid(float len, float width, float height);
    float area();
    float volume();
protected:
    float m_height;
};
Cuboid::Cuboid(float len, float width, float height): Rec(len, width), m_height(height){ }
float Cuboid::area(){ return 2 * ( m_len*m_width + m_len*m_height + m_width*m_height); }
float Cuboid::volume(){ return m_len * m_width * m_height; }

//正方體
class Cube: public Cuboid{
public:
    Cube(float len);
    float area();
    float volume();
};
Cube::Cube(float len): Cuboid(len, len, len){ }
float Cube::area(){ return 6 * m_len * m_len; }
float Cube::volume(){ return m_len * m_len * m_len; }

int main(){
    Line *p = new Cuboid(10, 20, 30);
    cout<<"The area of Cuboid is "<<p->area()<<endl;
    cout<<"The volume of Cuboid is "<<p->volume()<<endl;
  
    p = new Cube(15);
    cout<<"The area of Cube is "<<p->area()<<endl;
    cout<<"The volume of Cube is "<<p->volume()<<endl;

    return 0;
}
           

繼續閱讀