C++入門教程(六十五):純虛函數和抽象類

小古銀的官方網站(完整教程):http://www.xiaoguyin.com/
C++入門教程視頻:https://www.bilibili.com/video/av20868986/

目錄

回顧

基礎示例

以之前教程的統計字符數量的函數爲例:

#include <iostream> // std::cout std::endl
#include <string> // std::u32string

class basic_count_condition
{
public:
    // 由於該類需要被繼承, 所以需要將析構函數定義成虛函數
    // 由於該類不需要進行釋放操作, 所以使用默認析構函數即可
    virtual ~basic_count_condition(void) = default;

    // 由派生類繼承, 用於統計函數的判斷函數
    virtual bool is_true(char32_t ch);
};

// 以下是統計函數的使用說明
// 功能: 統計函數
// 參數: 需要被統計的字符串
// 參數: 統計函數需要的判斷函數, 需要繼承basic_count_condition並且重寫
//       虛函數is_true. 統計函數將會向判斷函數傳入每一個字符,
//       然後統計函數會根據判斷函數的返回值進行統計, 如果判斷函數返回
//       true, 統計函數則會處理; 如果判斷函數返回false, 統計函數則忽略
// 返回值: 統計判斷函數返回true的數量
unsigned int count_if(std::u32string text, basic_count_condition *obj);

// 以上是統計的統計函數聲明和類聲明, 以下是統計的函數定義和類成員函數定義

bool basic_count_condition::is_true(char32_t ch)
{
    return false;
}

unsigned int count_if(std::u32string text, basic_count_condition *obj)
{
    unsigned int count = 0; // 用來保存統計數量

    // 遍歷字符串中所有字符, 如果符合條件則統計
    for (auto ch : text)
    {
        if (obj->is_true(ch))
        {
            ++count;
        }
    }

    // 返回統計數量
    return count;
}

// 以上是統計函數提供的功能和接口,下面是使用統計函數需要寫的代碼

class gu_condition : public basic_count_condition
{
public:
    virtual ~gu_condition(void) = default;
    virtual bool is_true(char32_t ch) override; // 如果字符是'古'則返回true
};

int main(void)
{
    // 統計字符串中一共有多少個古字
    gu_condition gu;
    std::cout << count_if(U"小古銀小古銀我是美美噠小古銀", &gu) << std::endl;
    return 0;
}

bool gu_condition::is_true(char32_t ch)
{
    return ch == U'古';
}

輸出結果:

3

基礎講解

可以看出無論從設計還是使用都是函數式編程比較簡單,不過現在不是講函數式編程,所以不要在意這些細節。這篇教程重點講的是如何設計一個類,但是現在先簡單看一下使用方法。

現在假設basic_count_conditioncount_if都是別人設計給我們用的,要使用統計函數就必須先繼承類basic_count_condition,然後實現重寫成員函數is_true。然後還需要創建派生類gu_condition的對象,在調用函數count_if的時候作爲第二個參數傳入它的地址。然後就可以求出在字符串中的數量。

關鍵還是在於如何設計一個類。由於我們的count_if需要一個參數去接收判斷條件,這裏就需要一個給count_if用的基類,將它命名爲basic_count_condition。由於這個基類是專門用來繼承的,所以必須將析構函數定義爲虛函數,而基類不需要釋放資源,所以採用默認析構函數即可。接着是關鍵,這個基類必須要統一出一個虛函數作爲接口,一是給count_if調用,二是給調用者繼承,這樣,count_if使用基類指針或引用時就可以使用多態了。因爲count_if需要的判斷函數是可以接收一個字符並且返回布爾,所以這個虛函數應該聲明爲virtual bool is_true(char32_t ch);,而這個函數一般情況是不會改變成員變量,但也不排除少數情況派生類需要改變,所以這個函數不用const修飾。那麼這個虛函數就確定下來了,後面只需要調用者繼承這個類並且重寫這個虛函數就可以了。由於作爲接口的虛函數已經確定下來,所以接下來就可以實現函數count_if了。由於前面已經說過count_if的實現,所以現在就不重複了,只是判斷函數改變了。

純虛函數

從上面示例代碼可以看出,基類basic_count_condition的虛函數is_true只返回了false,沒有其他作用。再從基類虛函數is_true的功能來看,它只是用來給派生類重寫的,自身不應該有功能,也就是不應該被定義實現。

當虛函數的功能只是給派生類重寫而沒有其他更具體的作用時,那麼就不應該爲該函數寫定義並且應該將它設置爲純虛函數。這樣純虛函數就徹底成爲一個接口專門供派生類重寫出功能。

純虛函數只需要在虛函數聲明後面加上= 0;即可,以上面的基類虛函數is_true爲例子,函數聲明應該改成:virtual bool is_true(char32_t ch) = 0;。將虛函數定義成純虛函數後,就不能爲純虛函數寫定義了,否則將會編譯報錯。

基礎示例

接下來看一下上面代碼修改後的完整代碼:

#include <iostream> // std::cout std::endl
#include <string> // std::u32string

class basic_count_condition
{
public:
    virtual ~basic_count_condition(void) = default;
    virtual bool is_true(char32_t ch) = 0; // 純虛函數
};

unsigned int count_if(std::u32string text, basic_count_condition *obj);
unsigned int count_if(std::u32string text, basic_count_condition *obj)
{
    unsigned int count = 0;
    for (auto ch : text)
    {
        if (obj->is_true(ch))
        {
            ++count;
        }
    }
    return count;
}

class gu_condition : public basic_count_condition
{
public:
    virtual ~gu_condition(void) = default;
    virtual bool is_true(char32_t ch) override;
};

int main(void)
{
    // 統計字符串中一共有多少個古字
    gu_condition gu;
    std::cout << count_if(U"小古銀小古銀我是美美噠小古銀", &gu) << std::endl;
    return 0;
}

bool gu_condition::is_true(char32_t ch)
{
    return ch == U'古';
}

輸出結果:

3

基礎講解

我們把基類的is_true聲明爲純虛函數:

virtual bool is_true(char32_t ch) = 0;

那麼就不需要爲它寫函數定義,寫了也會報錯。當有派生類繼承basic_count_condition時,就必須重寫is_true或者將is_true再設爲純虛函數,否則將會編譯報錯提醒你:你設計的派生類必須實現基類的純虛函數。而且有純虛函數的類是不能用來創建對象的,否則將會編譯報錯。這樣就可以保證,設計出的派生類必須重寫純虛函數才能創建出對象,而使用多態必須先有對象,這樣就可以正常使用多態。即上面代碼count_if永遠能夠正常通過指針obj使用多態,而派生類必須重寫is_true才能創建對象,從而才能爲函數count_if的第二個參數傳入地址。

抽象類

有純虛函數的類就叫做抽象類。由於有純虛函數,那麼類和成員函數都應該理解爲:只供繼承而不能使用。根據這個規定,所以抽象類是不能創建出對象的。

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