亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深入理解C++模板如何實現(xiàn)多態(tài)思想

 更新時間:2023年03月17日 14:22:50   作者:余識-  
這篇文章主要為大家詳細介紹了C++模板如何實現(xiàn)多態(tài)的相關(guān)資料,文中的示例代碼講解詳細,對我們深入了解C++有一定的幫助,感興趣的可以了解一下

一、模板與多態(tài)基礎(chǔ)

再進一步了解如何用模板來實現(xiàn)多態(tài)前,我們還是來看一看這兩個概念的基礎(chǔ)理解

1.模板

首先是模板,其主要用途在于讓我們程序員少寫代碼

比如像下面兩個函數(shù)類似的一系列函數(shù):

int add(int a, int b) {
    return a + b;
}
double add(double a, double b) {
    return a + b;
}

就可以用模板簡寫為:

template<class T>
T add(T a, T b) {
    return a + b;
}

使用的方式如下:

    add<int>(1,3);
    add<double>(1.1, 3.4);
    add<char>('s','a');

但由于C++編譯器可以自動推斷參數(shù)類型,所以中間的<int>是可以省略的

這里要注意一個非常重要的問題,雖然我們只寫了一個模板函數(shù),但實際上并不止有一個函數(shù)

比如這里我們用了三種類型的add函數(shù),那么編譯器就會為我們分別生成三個函數(shù)

也就是說,這是編譯器根據(jù)我們寫的模板。幫我們自動生成的函數(shù)

進一步來說,模板是完全給編譯器看的,并不會參與到最終的可執(zhí)行文件中

上面這一點便是模板的精髓!

為了更加直觀的理解,我們來看一下最終生成的三個函數(shù)的內(nèi)存地址:

#include<iostream>
template<class T>
T add(T a, T b) {
	return a + b;
}

int main() {
	printf("%p\n", add<int>);
	printf("%p\n", add<double>);
	printf("%p\n", add<char>);
}

這樣我們就能實際的看到最終確實是生成了三個函數(shù),因為三個函數(shù)的地址完全不同,分別就代表著三個版本的add函數(shù)

總結(jié)來說就是,模板并沒有減少最終的代碼量,它僅僅只是減少了我們程序員需要寫的代碼量

并且這個過程是在編譯期間就完成了的,這一點很重要!

之所以要用模板來實現(xiàn)多態(tài),就是看重了它是在編譯期間就完成的,而不會去影響最終的可執(zhí)行文件的執(zhí)行時間、大小

2.多態(tài)

然后便是多態(tài)了,多態(tài)是類中一個很重要的概念,其主要用途就是使得函數(shù)接口統(tǒng)一化

比如下面這段代碼:

#include <iostream> 
using namespace std;
class A {
public:
    virtual void area() {
        cout << "這是基類A" << endl;
    }
};
class B : public A {
public:
    void area()
    {
        cout << "這是子類B" << endl;
    }
};
class C : public A {
public:
    void area()
    {
        cout << "這是子類C" << endl;
    }
};
// 程序的主函數(shù)
int main()
{
    A* a;
    a = new B();
    a->area();
    a = new C();
    a->area();
    return 0;
}

邏輯并不復(fù)雜,就是B,C兩個類都繼承于A,

并且在基類中我們用到了關(guān)鍵字virtual 定義area為虛函數(shù),還在兩個子類里面都分別重寫了這個函數(shù)

因為B,C類都繼承于A類,所以我們可以用A類指針來接收B,C對象

從占用內(nèi)存上考慮,子類是繼承父類的,所以子類所占用的內(nèi)存量肯定大于或等于父類占用內(nèi)存,那么子類申請一塊內(nèi)存,賦值給父類的指針,父類就不可能會內(nèi)存訪問越界,而反過來,如果用子類指針存儲父類對象,由于子類訪問的內(nèi)存大于等于父類,就可能造成內(nèi)存訪問越界,因此一般禁止這樣使用

此時我們發(fā)現(xiàn),我們只用了一個A調(diào)用同一個函數(shù)area,卻可以完成兩個類的調(diào)用!

所以很多時候,當(dāng)我們使用別人的提供給我們的類時,只要知道了它的父類有哪些函數(shù),那么其子類就必然有對應(yīng)的函數(shù)

這可以極大方便類的管理、升級以及使用

雖然它的好處很多,但同樣也有壞處,那就是它是動態(tài)綁定函數(shù)的,依靠了一個叫做虛函數(shù)表的東西,導(dǎo)致其內(nèi)存占用更大,運行時間更長

比如上面的代碼我們就可以在調(diào)試窗口中看到其虛函數(shù)表:

就是這個名為 _vfptr的變量名稱,他就是指向虛函數(shù)表的函數(shù)指針,而虛函數(shù)表中就存有我們的虛函數(shù)

父類指針想要正確使用子類重寫的函數(shù),就必須要在這個虛函數(shù)表中進行遍歷查詢對應(yīng)的函數(shù)地址

所以一旦你的類中有虛函數(shù),那么你的類就肯定會多出一個指針大小的內(nèi)存用于存儲虛函數(shù)表的地址,并且最終生成的可執(zhí)行文件也會變大很多字節(jié)

這取決于你的虛函數(shù)個數(shù),每多一個虛函數(shù),那么虛函數(shù)表就需要多一個指針大小的內(nèi)存來存儲

如果依舊不太懂的,可以自行在瀏覽器中搜索一下,有很多優(yōu)秀的文章對此有解釋

總結(jié)來說就是:使用傳統(tǒng)類的多態(tài)特性,會導(dǎo)致程序效率變低,最終生成的可執(zhí)行文件體積變大

原因就是它生成了虛函數(shù)表、虛函數(shù)指針,在程序運行過程中執(zhí)行查詢函數(shù)的操作

MFC就是因為大量使用的這種多態(tài),公共控件都繼承于基本窗口類,一般都有數(shù)十上百個虛函數(shù),所以這就導(dǎo)致即使你什么都沒干,一個MFC程序都至少有數(shù)兆大小,并且運行效率還較低

二、模板實現(xiàn)多態(tài)

了解了上面所說的兩個基本概念的優(yōu)缺點之后,現(xiàn)在我們就可以來到如何使用模板來實現(xiàn)多態(tài)了

因為模板就是編譯期間就完成的操作,如果讓模板來實現(xiàn)多態(tài),那么就不存在運行期間去遍歷虛函數(shù)表來找對應(yīng)的函數(shù),也不需要開辟一個虛函數(shù)表來存儲虛函數(shù)地址

既能節(jié)約內(nèi)存,又能提高程序運行效率,是不是非常的完美!

下面我們就來看一看模板實現(xiàn)多態(tài)的基本流程

#include <iostream> 
using namespace std;
template<class T>
class A {
public:
    void Show() {
        T* p=static_cast<T*>(this);
        p->area();
    }
    void area() {
        cout << "這是基類A" << endl;
    }
};
class B : public A<B> {
public:
    void area()
    {
        cout << "這是子類B" << endl;
    }
};
class C : public A<C> {
public:
    void area()
    {
        cout << "這是子類C" << endl;
    }
};
// 程序的主函數(shù)
int main()
{
    B b;
    b.Show();
    C c;
    c.Show();
    return 0;
}

這里同樣是B,C兩個類都繼承自A類,但不同點就在于A類帶了一個模板變量

所以B,C類在繼承A的時候,就需要將自己這個類型傳遞進去

此時三個類都寫了area函數(shù),但只有基類寫了show方法對吧

但由于B,C類都是繼承自A類,所以它們其實也已經(jīng)含有了show方法

然后便是最重要的一步,在基類的show方法中,我將this指針轉(zhuǎn)化為T類型指針

static_cast與強制轉(zhuǎn)化基本等價,唯一很大一點的區(qū)別就是,強制轉(zhuǎn)換可以任意使用,比如B沒有繼承自A類,強制轉(zhuǎn)換仍然可以將兩者指針進行轉(zhuǎn)換,而static_cast無法轉(zhuǎn)換兩個毫不相干的東西,這樣就保證了傳入的類型是繼承自基類的,否則編譯會直接報錯

此時這里的p指針

T* p=static_cast<T*>(this);

實際就轉(zhuǎn)化為了調(diào)用者的指針,以B舉例子:

B b;
b.Show(); //調(diào)用Show方法后,完成了指針的轉(zhuǎn)換,指代的B,那么B調(diào)用area函數(shù),也就是調(diào)用自己重寫的area函數(shù)

如果現(xiàn)在再多出一個子類D繼承于A,但里面什么都沒有:

class D : A<D>{
}

那么當(dāng)你使用D時:

D d;
d.Show(); //將指針轉(zhuǎn)換為D類型,由于D類型沒有重寫area方法,所以將調(diào)用繼承下來的基類area方法

同樣是一個show函數(shù),能夠卻能根據(jù)情況選擇出不同的函數(shù)調(diào)用,而且還是在編譯期間就完成了的

這便是模板實現(xiàn)多態(tài)的基本原理

三、實際應(yīng)用

由于上面說的都是實現(xiàn)原理,例子比較奇怪,下面我們來直接看一看ATL中的代碼:

WTL是基于ATL之上開發(fā)的,而ATL庫則是vs開發(fā)環(huán)境中自帶,WTL庫需要自己去下載

可以輸入以下代碼

#include<atlwin.h>
class MyWindow :public CWindowImpl<MyWindow> 
{

};

然后右鍵速覽CWindowImpl類,接著在跳出的文件中搜索static_cast:

就能看到很多像上圖這樣的調(diào)用

1.先將this指針還原為子類

2.然后再調(diào)用對應(yīng)的函數(shù)

3.如果子類重寫了這個函數(shù),那么就調(diào)用子類的函數(shù),否則就調(diào)用父類的函數(shù)

當(dāng)然這并不完全如此,比如上圖中的那一出,是將其轉(zhuǎn)化為子類后,傳給某個函數(shù)進行處理

不過總體邏輯是一致的:在父類中操作子類,以實現(xiàn)靜態(tài)多態(tài)的目的

到此這篇關(guān)于深入理解C++模板如何實現(xiàn)多態(tài)思想的文章就介紹到這了,更多相關(guān)C++模板 多態(tài)思想內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺析C和C++函數(shù)的相互引用

    淺析C和C++函數(shù)的相互引用

    C++是一種面向?qū)ο蟮某绦蛟O(shè)計語言,為了支持函數(shù)的重載,C++對全局函數(shù)的處理方式與C有明顯的不同
    2013-10-10
  • 麻將游戲算法深入解析及實現(xiàn)代碼

    麻將游戲算法深入解析及實現(xiàn)代碼

    這篇文章主要介紹了麻將游戲算法深入解析及實現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • 學(xué)生成績管理系統(tǒng)C語言代碼實現(xiàn)

    學(xué)生成績管理系統(tǒng)C語言代碼實現(xiàn)

    這篇文章主要為大家詳細介紹了C語言代碼實現(xiàn)學(xué)生成績管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • C++選擇文件夾代碼的封裝

    C++選擇文件夾代碼的封裝

    這篇文章主要介紹了C++選擇文件夾代碼的封裝,實例展示了將選擇文件夾功能代碼封裝為一個類并對其進行實例化調(diào)用的過程,對于學(xué)習(xí)C++程序設(shè)計有不錯的參考價值,需要的朋友可以參考下
    2014-10-10
  • C語言冒泡排序超全面實現(xiàn)流程

    C語言冒泡排序超全面實現(xiàn)流程

    算法中排序是十分重要的,而每一個學(xué)習(xí)計算機的都會在初期的時候接觸到這種排序,下面這篇文章主要給大家介紹了關(guān)于c語言冒泡排序的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • 方陣順時針旋轉(zhuǎn)的實現(xiàn)代碼

    方陣順時針旋轉(zhuǎn)的實現(xiàn)代碼

    以下是關(guān)于方陣順時針旋轉(zhuǎn)的實現(xiàn)代碼。需要的朋友參考下
    2013-05-05
  • 詳解如何在code block創(chuàng)建一個C語言的項目

    詳解如何在code block創(chuàng)建一個C語言的項目

    這篇文章主要介紹了詳解如何在code block創(chuàng)建一個C語言的項目,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • 基于重啟后消失的注冊表鍵值的詳細介紹

    基于重啟后消失的注冊表鍵值的詳細介紹

    本篇文章是對重啟后消失的注冊表鍵值進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • c語言大小端(數(shù)據(jù)在內(nèi)存中的存儲)

    c語言大小端(數(shù)據(jù)在內(nèi)存中的存儲)

    大小端是內(nèi)存存儲字節(jié)的兩種方式,一個是大端存儲,一個是小端存儲,本文主要介紹了c語言大小端,具有一定的參考價值,感興趣的可以了解一下
    2023-09-09
  • C++回溯算法深度優(yōu)先搜索舉例分析

    C++回溯算法深度優(yōu)先搜索舉例分析

    回溯在迷宮搜索中使用很常見,就是這條路走不通,然后返回前一個路口,繼續(xù)下一條路。回溯算法說白了就是窮舉法,下面讓我們一起來看看回溯算法深度優(yōu)先搜索吧
    2022-03-03

最新評論