1. 程式人生 > >【C++】—多型(上)動態多型

【C++】—多型(上)動態多型

一、多型
1、 概念:同一事物表現出的多種形態,同一操作作用於不同的物件,可以有不同的解釋,產生不同的執行結果。在執行時,可以通過指向基類的指標,來呼叫實現派生類中的方法。
2、 舉例子:

#include<windows.h>
class WashRoom
{
public:
    void GoToManWashRoom()
    {
        cout << "Man ---> Please Left" << endl;
    }
    void GoToWomanWashRoom()
    {
        cout << "Woman ------> Please Right"
<< endl; } }; class Person { public: virtual void GoToWashRoom(WashRoom& wc) = 0; }; class Man :public Person { public: virtual void GoToWashRoom(WashRoom& wc) { wc.GoToManWashRoom(); } }; class Woman :public Person { public: virtual void GoToWashRoom
(WashRoom& wc) { wc.GoToWomanWashRoom(); } }; void TestWashRoom() { WashRoom wc; Person* p = NULL; for (int i = 0; i <10; ++i) { if ((rand()) & 0x01) p = new Man; else p = new Woman; p->GoToWashRoom(wc); delete p; Sleep(1000
); } } int main() { TestWashRoom(); return 0; }

3、 多型的分類
(1)靜態多型:編譯器在編譯期間來確定程式的行為(確定具體呼叫哪個函式)

A:函式過載

B:泛型程式設計

(2)動態多型:程式執行時來確定程式的行為(確定具體呼叫哪個函式)
4、 動態多型實現條件
(1) 基類中必須包含虛擬函式,在派生類中必須對基類的虛擬函式進行重寫
(2) 必須通過基類指標或引用呼叫虛擬函式

//1、基類中必須包含虛擬函式,且派生類一定要對基類中的虛擬函式進行重寫(原型一致)
class Base
{
public:
    virtual void TestFunc1()
    {
        cout << "Base::TestFunc1()" << endl;
    }
    virtual void TestFunc2()
    {
        cout << "Base::TestFunc2()" << endl;
    }
    void TestFunc3()//基類無虛擬關鍵字,派生類有
    {
        cout << "Base::TestFunc3()" << endl;
    }
    virtual void TestFunc4(int)//派生類中是char,引數型別不一樣
    {
        cout << "Base::TestFunc4()" << endl;
    }
    /*virtual int TestFunc5()
    {
        cout << "Base::TestFunc5()" << endl;
        return 0;
    }*/
    //協變—基類(派生類)虛擬函式返回基類(派生類)指標或引用
    virtual Base* TestFunc5()
    {
        cout << "Base::TestFunc5()" << endl;
        return this;
    }
    virtual Base& TestFunc6()
    {
        cout << "Base::TestFunc6()" << endl;
        return *this;
    }

    ~Base()//解構函式
    {
        cout << "Base::~Base()" << endl;
    }
};

class Derived : public Base
{
public:
    virtual void TestFunc1()
    {
        cout << "Derived::TestFunc1()" << endl;
    }
    void TestFunc2()//派生類無虛擬關鍵字,基類有
    {
        cout << "Derived::TestFunc2()" << endl;
    }
    virtual void TestFunc3()
    {
        cout << "Derived::TestFunc3()" << endl;
    }
    //沒有構成重寫(引數型別不同)
    virtual void TestFunc4(char)//基類中是int 引數型別不同
    {
        cout << "Derived::TestFunc4()" << endl;
    }
    //virtual char TestFunc5()//不能重寫,返回型別不同
    //{
    //  cout << "Derived::TestFunc5()" << endl;
    //  return 0;
    //}
    //協變—基類(派生類)虛擬函式返回基類(派生類)指標或引用
    virtual Derived* TestFunc5()
    {
        cout << "Derived::TestFunc5()" << endl;
        return this;
    }
    virtual Derived& TestFunc6()
    {
        cout << "Derived::TestFunc6()" << endl;
        return *this;
    }

    virtual ~Derived()
    {
        cout << "Derived::~Derived()" << endl;
    }
};

//2、通過基類物件的指標或引用呼叫虛擬函式
void TestVirtualFunc(Base& b)
{
    b.TestFunc1();
    b.TestFunc2();
    b.TestFunc3();
    b.TestFunc4(10);
    b.TestFunc4('a');
}

int main()
{
    Base* pb = new Derived;//基類的指標,指向派生類的物件
    delete pb;//如果呼叫基類裡的析構,則沒有重寫;反之

    Base b;
    Derived d;
    //如果引用的是基類的物件,呼叫基類裡對應的虛擬函式
    //如果引用的是派生類的物件,則呼叫派生類裡對應的虛擬函式
    TestVirtualFunc(b);
    TestVirtualFunc(d);
    return 0;
}

返回值不同不能重寫

5、 重寫
(1)基類中的函式必須為虛擬函式
(2)派生類中重寫的虛擬函式必須與基類的虛擬函式型別保持一致(返回值、函式名字(引數列表))
例外:

協變:基類中虛擬函式返回基類物件的指標或引用,派生類中虛擬函式返回派生類物件的指標或引用——返回值型別不同
解構函式——基類和派生類中函式的名字不同

(3)基類中虛擬函式和派生類虛擬函式的訪問限定符可以不同

——過載、重寫、重定義

6、程式碼實現多型

class Base
{
public:
    virtual void TestFunc1()
    {
        cout << "Base::TestFunc1()" << endl;
    }
    virtual void TestFunc2()
    {
        cout << "Base::TestFunc2()" << endl;
    }
    virtual void TestFunc3()
    {
        cout << "Base::TestFunc3()" << endl;
    }

    int _b;
};

class Derived :public Base
{
public:
    virtual void TestFunc2()
    {
        cout << "Derived::TestFunc2()" << endl;
    }
    virtual void TestFunc3()
    {
        cout << "Derived::TestFunc3()" << endl;
    }


    int _d;
};

      void TestVirtualFunc(Base& b)
{
b. TestFunc1();
b. TestFunc2();
}
int main()
{
   Base b;
   Derived d;

   TestVirtualFunc(b);
   TestVirtualFunc(d);
   return 0;
}

7、動態多型呼叫原理

多型的條件已經完全滿足
1)從物件的前4個位元組中取虛表地址
2) 傳參(虛擬函式形參+this)
3) 從虛表中取虛擬函式地址
4) 呼叫該虛擬函式

二、抽象類
1、概念
在成員函式(必須為虛擬函式)的形參列表後面寫上=0,則成員函式為純虛擬函式。包含純虛擬函式的類叫做抽象類(也叫介面類),抽象類不能例項化出物件。純虛擬函式在派生類中重新定義以後,派生類才能例項化出物件。

class WC;

class Person
{
public:
    virtual void GoToWC(WC& c) = 0;//純虛擬函式
};

class Man :public Person
{
public:
    virtual void GoToWC(WC& c)
    {
        //
    }
};
int main()
{
    Person* p;
    Man m;
    return 0;
}

2、總結
(1)派生類重寫基類的虛擬函式實現多型,要求函式名、引數列表、返回值完全相同(協變除外)
(2)基類中定義了虛擬函式,在派生類中該函式始終保持虛擬函式的特性
(3)只有類的非靜態成員函式才能定義為虛擬函式,靜態成員函式不能定義為虛擬函式
(4)如果在類外定義虛擬函式,只能在宣告函式時加virtual關鍵字,定義時不用加
(5)建構函式不能定義為虛擬函式,雖然可以將operator=定義為虛擬函式,但最好不要這麼做,使用時容易混淆(?)
(6)不要在建構函式和解構函式中呼叫虛擬函式,在建構函式和解構函式中,物件是不完整的,可能會出現未定義的行為
(7)最好將基類的解構函式宣告為虛擬函式。(解構函式比較特殊,因為派生類的解構函式跟基類的解構函式名稱不一樣,但是構成覆蓋,這裡編譯器做了特殊處理)
(8)虛表是所有類物件例項共用的

class Base
{
public:
    virtual void TestFunc1()
    {
        cout << "Base::TestFunc1()" << endl;
    }

    int _b;
};

int main()
{
    cout << sizeof(Base) << endl;//8
    Base b;
    b._b = 1;
    return 0;
}