加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

適合具備 C 語(yǔ)言基礎(chǔ)的 C++ 教程(十)

2021/02/25
204
閱讀需 3 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

前言

在上一則教程中,敘述了抽象類以及動(dòng)態(tài)鏈接庫(kù)的相關(guān)內(nèi)容,本節(jié)來(lái)敘述一下抽象類界面的相關(guān)內(nèi)容,以及本節(jié)即將引入一個(gè)新的概念,模板。

抽象類界面

何為抽象類界面呢?要說(shuō)清楚這個(gè)概念,需要回顧上一則教程中所述的類編程和應(yīng)用編程兩個(gè)概念,為了實(shí)現(xiàn)應(yīng)用編程和類編程,引入了動(dòng)態(tài)鏈接庫(kù)的概念,要達(dá)到的效果就是當(dāng)更改類的代碼的時(shí)候,而不更改應(yīng)用程序的代碼的時(shí)候,只需要重新生成動(dòng)態(tài)鏈接庫(kù),而不需要重新生成可執(zhí)行文件。那么我們回顧之前的代碼,看應(yīng)用編程里面的內(nèi)容,也就是主函數(shù)里面的內(nèi)容:

#include "Human.h"
#include "Englishman.h"
#include "Chinese.h"

void test_eating(Human *h)
{
    h->eating();
}


int main(int argc, char **argv)
{
    Englishman e;
    Chinese c;

    Human* h[2] = {&e, &c};
    int i;
    for (i = 0; i < 2; i++)
        test_eating(h[i]);

    return 0;
}

在上述代碼中,我們看到第一行代碼和第二行代碼包含了頭文件Englishman.h和 Chinese.h,那么這個(gè)時(shí)候,如果更改了類中代碼,比如說(shuō)我們更改了Englishman.h或者是Chinese.h的代碼,這個(gè)時(shí)候在編譯的時(shí)候,如果只編譯動(dòng)態(tài)鏈接庫(kù),而不編譯應(yīng)用程序,那么必然會(huì)導(dǎo)致程序出現(xiàn)問(wèn)題。那要如何解決這個(gè)問(wèn)題呢,所采取的一種思路便是使用抽象類界面的思路來(lái)進(jìn)行解決。

下面是抽象類界面的一個(gè)示意圖:

image-20210224101410895

 

通過(guò)這張示意圖也可以明白,這個(gè)時(shí)候,APP也就是應(yīng)用程序的代碼只和Human.h相關(guān),而 Human.h又和EnglishmanChinese有關(guān),這樣一來(lái),如果改變的是Englishman或者是Chinese類的代碼,那么就不會(huì)影響到應(yīng)用程序,仍然只需要重新編譯動(dòng)態(tài)鏈接庫(kù)就好。

說(shuō)了那么多,該如何做呢,我們先從主函數(shù)看起,下面是更改之后的主函數(shù):

#include "Human.h"

void test_eating(Human *h)
{
    h->eating();
}


int main(int argc, char **argv)
{
    Human& e = CreateEnglishman("Bill", 10, "sfwqerfsdfas");
    Human& c = CreateChinese("zhangsan", 11, "beijing");

    Human* h[2] = {&e, &c};
    int i;
    for (i = 0; i < 2; i++)
        test_eating(h[i]);

    return 0;
}

看到上述代碼,第一,頭文件中,Englishman.hChinese.h不見(jiàn)了,只剩下一個(gè)Human.h,正如上面所說(shuō),APP的代碼只和Human.h有關(guān)聯(lián);第二,之前有EnglishmanChinese的實(shí)例化對(duì)象,現(xiàn)在改為了使用函數(shù)調(diào)用生成Human類的引用,來(lái)替代之前的實(shí)例化對(duì)象。

那自然,這兩個(gè)函數(shù)調(diào)用是在Human.h中聲明的了,Human.h的代碼如下所示:

#ifndef _HUMAN_H
#define _HUMAN_H

#include 
#include 
#include 

using namespace std;

class Human {
private:
    char *name;

public:
    void setName(char *name);
    char *getName(void);
    virtual void eating(void) = 0;
    virtual void wearing(void) = 0;
    virtual void driving(void) = 0;

};

Human& CreateEnglishman(char *name, int age, char *address);
Human& CreateChinese(char *name, int age, char *address);

#endif

為了使得應(yīng)用編程和類編程相互分離,那么這兩個(gè)函數(shù)的定義自然分別為了EnglishmanChinese了,代碼分別如下所示:

/* 當(dāng)前處于Englishman.cpp中 */
Human& CreateEnglishman(char *name, int age, char *address)
{
    return *(new Englishman(name, age, address));
}

下面是CreateChinese的函數(shù)定義,代碼如下所示:

Human& CreateChinese(char *name, int age, char *address)
{
    return *(new Chinese(name, age, address));
}

這樣一來(lái),就實(shí)現(xiàn)了抽象類界面,在更改EnglishmanChinese的代碼的時(shí)候,不需要重新生成可執(zhí)行文件,只需要重新生成動(dòng)態(tài)鏈接庫(kù)就可以了。

模板

C++中的模板定義中,模板有兩類,一個(gè)是函數(shù)模板,一個(gè)是類模板,在本節(jié)的教程中,主要是講述函數(shù)模板的相關(guān)內(nèi)容。

函數(shù)模板的引入

為什么要引入函數(shù)模板呢,我們來(lái)看一下如下所示的代碼:

int& max(int& a, int& b)
{
    return (a < b)? b : a;
}

double& max(double& a, double& b)
{
    return (a < b)? b : a;
}

float& max(float& a, float& b)
{
    return (a < b)? b : a;
}

上述的代碼是max函數(shù)的一個(gè)重載,觀察這個(gè)重載函數(shù),可見(jiàn),每個(gè)重載函數(shù)的兩個(gè)形參是相同的,并且形參和返回值一樣,基于此,我們也就可以定義一個(gè)函數(shù)模板來(lái)替代這些函數(shù)重載,函數(shù)模板定義如下:

template
T& max(T&a,t&B)
{
   return (a < b)? b : a;
}

如何理解上述模板函數(shù)呢,實(shí)際上也就是說(shuō),把類型用T來(lái)替換了。

基于模板函數(shù),我們?cè)賮?lái)實(shí)現(xiàn)上述使用重載而實(shí)現(xiàn)的功能,代碼如下所示:

#include 
#include 
#include 

using namespace std;

template
T& mymax(T& a, T& b)
{
    cout<<__PRETTY_FUNCTION__<    return (a < b)? b : a;
}

int main(int argc, char **argv)
{
    int ia = 1, ib = 2;
    float fa = 1, fb = 2;
    double da = 1, db = 2;

    mymax(ia, ib);
    mymax(fa, fb);
    mymax(da, db);

    return 0;
}

上述代碼執(zhí)行的結(jié)果如下所示:

image-20210224135710100

 

可見(jiàn)上述的運(yùn)行結(jié)果顯示,雖然是使用的一個(gè)函數(shù)模板,但是在執(zhí)行的時(shí)候,mymax(ia, ib);、mymax(fa, fb);以及mymax(da, db);實(shí)際上是執(zhí)行了三個(gè)不同的函數(shù),這也正是函數(shù)模板執(zhí)行的一個(gè)機(jī)制,函數(shù)模板其特點(diǎn)主要是以下兩點(diǎn):

函數(shù)模板只是編譯指令,一般寫(xiě)在頭文件中;

編譯程序的時(shí)候,編譯器根據(jù)函數(shù)的參數(shù)來(lái)“推導(dǎo)”模板的參數(shù);然后生成具體的模板函數(shù)

模板函數(shù)參數(shù)推導(dǎo)過(guò)程

模板函數(shù)參數(shù)的推導(dǎo)過(guò)程是一個(gè)重要的內(nèi)容,它主要可以分為如下幾個(gè)方面:

有限的類型轉(zhuǎn)換

函數(shù)模板只支持兩種隱式轉(zhuǎn)換

其他隱式轉(zhuǎn)換都不支持

苛刻的類型匹配

參數(shù)類型必須完全匹配;如果不能直接匹配,則可以進(jìn)行”有限的類型轉(zhuǎn)換“,如果還是不匹配,那么就推導(dǎo)失敗

  • const 轉(zhuǎn)換:函數(shù)參數(shù)為非 const 引用/指針,它可以隱式地轉(zhuǎn)換為const引用/指針

    數(shù)組或者函數(shù)指針轉(zhuǎn)換:

數(shù)組可以隱式的轉(zhuǎn)換為”指向第一個(gè)元素的指針“

參數(shù)為”函數(shù)的名字“,它隱式地轉(zhuǎn)化為函數(shù)指針

基于上述所述的這些特點(diǎn),接下來(lái)通過(guò)實(shí)例進(jìn)行闡述,現(xiàn)在基于剛才那個(gè)函數(shù)模板,我們來(lái)編寫(xiě)下面的例子:

using namespace std;

int main(int argc,char **argv)
{
    int a = 1;
    double b = 2.1;
    mymax(a,b);

    return 0;
}

代碼編譯結(jié)果如下所示:

image-20210224142730144

 

通過(guò)上述錯(cuò)誤信息,可以看到所給出的信息是沒(méi)有匹配的函數(shù),只是因?yàn)槲覀儌魅氲膮?shù)是intdouble,傳入這兩個(gè)參數(shù)是函數(shù)模板是無(wú)法進(jìn)行推導(dǎo)的,無(wú)法進(jìn)行隱式轉(zhuǎn)換。

針對(duì)于上述來(lái)講,函數(shù)模板只支持兩種隱式轉(zhuǎn)換,那分別是哪兩種呢,我們來(lái)看具體的例子,我們將函數(shù)模板也進(jìn)行一些更改,更改之后的代碼如下所示:

#include 
#include 
#include 

using namespace std;

template
const T& mymax(const T& a, const T& b)
{
    cout<<__PRETTY_FUNCTION__<    return (a < b)? b : a;
}

int main(int argc, char **argv)
{
    int ia = 1;
    int ib = 2;

    mymax(ia, ib);

    return 0;
}

當(dāng)前這個(gè)函數(shù)是可以執(zhí)行通過(guò)的,也就是說(shuō)當(dāng)函數(shù)模板中的形參和返回值帶有const的時(shí)候,那么對(duì)于實(shí)參是可以不含const修飾的,也就是說(shuō)可變的參數(shù)可以傳入到形參不可變的函數(shù)里,但是反過(guò)來(lái)是不行的,除非兩個(gè)傳進(jìn)去的變量都是const的。比如如下所示的代碼:

#include 
#include 
#include 

using namespace std;

template
T& mymax(T& a, T& b)
{
    cout<<__PRETTY_FUNCTION__<    return (a < b)? b : a;
}

int main(int argc,char** argv)
{
    int ia = 1;
    const int ib = 2;
    mymax(ia,ib);    /* 錯(cuò)誤,const 不能隱式轉(zhuǎn)換為非 const */

    const int isa = 1;
    const int isb = 2;
    mymax(isa,isb);  /* 正確 */

    return 0;
}

除了上述的 非const 轉(zhuǎn) const的例子以外,還有一個(gè)是數(shù)組和指針的隱式轉(zhuǎn)換,數(shù)組可以隱式地轉(zhuǎn)換為“指向第一個(gè)元素的指針”,下面是一個(gè)關(guān)于數(shù)組和指針的代碼:

#include 
#include 
#include 

using namespace std;

template
const T& mymax(const T& a, const T& b)
{
    cout<<__PRETTY_FUNCTION__<    return (a < b)? b : a;
}

template
const T* mymax2(const T* a, const T* b)
{
    cout<<__PRETTY_FUNCTION__<    return (a < b)? b : a;
}

基于上述編寫(xiě)的兩個(gè)模板函數(shù),我們依次測(cè)試上述所說(shuō)的指針和數(shù)組之間的隱式轉(zhuǎn)換,代碼如下所示:

int main(int argc, char** argv)
{
    char a[] = "ab";
    char b[] = "cd";

    mymax(a,b);
    mymax2(a,b);

    return 0;
}

下面是代碼執(zhí)行的結(jié)果:

image-20210224145329671

 

注:cout<<__PRETTY_FUNCTION__< 可以在函數(shù)模板內(nèi)打印匹配結(jié)果

根據(jù)打印出來(lái)的匹配結(jié)果,可以看到mymax匹配的T是數(shù)組類型,而mymax2匹配的Tchar類型,這也證實(shí)了上述所說(shuō)的指針和數(shù)組之間的隱式轉(zhuǎn)換。

基于上述的函數(shù)模板,我們繼續(xù)來(lái)看一個(gè)例子:

int main(int argc, char** argv)
{
    char a2[] = "abc";
    char b2[] = "cd";

    mymax(a2, b2); /* mymax(char[4], char[3]),無(wú)法推導(dǎo)出T:mymax(char& [4], char& [3]),因?yàn)閮蓚€(gè)參數(shù)不一樣*/
    mymax(a2, b2); /* mymax2(char[4], char[3]),可以推導(dǎo)出T:mymax2(const char*,const char*) */

    return 0;
}

通過(guò)上述的注釋我們可以知道,第6行代碼是不能編譯通過(guò)的,但是第七行代碼可以編譯通過(guò),因?yàn)樗褂玫哪0宓膮?shù)是指針,而對(duì)于數(shù)組來(lái)說(shuō),可以隱式轉(zhuǎn)換為指針,數(shù)組名可以隱式轉(zhuǎn)換為指向第一個(gè)元素的指針。

上述的幾個(gè)例子已經(jīng)說(shuō)了const非const的,以及數(shù)組和指針的,接下來(lái)就是說(shuō)的是函數(shù)指針的,且看下面這個(gè)例子:

template
void test_func(T f)
{
    cout<<__PRETTY_FUNCTION__<}

void f1(int a, int b)
{
    return 0;
}

int main(int argc, char **argv)
{
    test_func(f1);
    test_func(&f1);

    return 0;
}

代碼執(zhí)行結(jié)果如下所示:

image-20210224151130109

 

可見(jiàn)對(duì)于函數(shù)名稱來(lái)說(shuō),上述的兩種傳入方式都是將 T推導(dǎo)為函數(shù)指針的形式。

小結(jié)

上述就是本期分享的內(nèi)容,涉及的代碼可以通過(guò)百度云鏈接的方式獲取到:

鏈接:https://pan.baidu.com/s/13_g0L9KBTSVJWDvOrksrfQ
 

提取碼:gfsb

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

在讀碩士研究生,喜歡鉆研嵌入式相關(guān)技術(shù),熱衷于寫(xiě)文章分享知識(shí),不定期輸出關(guān)于單片機(jī), RTOS,信號(hào)處理等相關(guān)內(nèi)容