小古銀的官方網站(完整教程):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_condition
和count_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
的第二個參數傳入地址。
抽象類
有純虛函數的類就叫做抽象類。由於有純虛函數,那麼類和成員函數都應該理解爲:只供繼承而不能使用。根據這個規定,所以抽象類是不能創建出對象的。