欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一篇文章帶你了解C++模板編程詳解

 更新時(shí)間:2021年11月02日 11:50:52   作者:小趙小趙福星高照~  
這篇文章主要介紹了C++的模板,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

模板初階

泛型編程

在計(jì)算機(jī)程序設(shè)計(jì)領(lǐng)域,為了避免因數(shù)據(jù)類型的不同,而被迫重復(fù)編寫大量相同業(yè)務(wù)邏輯的代碼,人們發(fā)展的泛型及泛型編程技術(shù)。什么是泛型呢?實(shí)質(zhì)上就是不使用具體數(shù)據(jù)類型(例如 int、double、float 等),而是使用一種通用類型來進(jìn)行程序設(shè)計(jì)的方法,該方法可以大規(guī)模的減少程序代碼的編寫量,讓程序員可以集中精力用于業(yè)務(wù)邏輯的實(shí)現(xiàn)。泛型也是一種數(shù)據(jù)類型,只不過它是一種用來代替所有類型的“通用類型”

我們通常如何實(shí)現(xiàn)一個(gè)通用的交換函數(shù)呢?

void Swap(int& left, int& right)
{
    int temp = left;
    left = right;
    right = temp;
}
void Swap(double& left, double& right)
{
    double temp = left;
    left = right;
    right = temp;
}
void Swap(char& left, char& right)
{
    char temp = left;
    left = right;
    right = temp;
}
......

Swap函數(shù)能實(shí)現(xiàn)各種類型的變量交換,但是只要類型不同就需要重新寫一個(gè)

使用函數(shù)重載雖然可以實(shí)現(xiàn),但是有一下幾個(gè)不好的地方:

  • 重載的函數(shù)僅僅只是類型不同,代碼的復(fù)用率比較低,只要有新類型出現(xiàn)時(shí),就需要增加對(duì)應(yīng)的函數(shù)
  • 代碼的可維護(hù)性比較低,一個(gè)出錯(cuò)可能所有的重載均出錯(cuò),那能否告訴編譯器一個(gè)模版,讓編譯器根據(jù)不同的類型利用該模版來生成代碼呢?

可以的,C++語法中有了模板:

函數(shù)模板

函數(shù)模板概念

所謂函數(shù)模板,實(shí)際上是建立一個(gè)通用函數(shù),它所用到的數(shù)據(jù)的類型(包括返回值類型、形參類型、局部變量類型)可以不具體指定,而是用一個(gè)虛擬的類型來代替(實(shí)際上是用一個(gè)標(biāo)識(shí)符來占位),等發(fā)生函數(shù)調(diào)用時(shí)再根據(jù)傳入的實(shí)參來逆推出真正的類型。 這個(gè)通用函數(shù)就稱為 函數(shù)模板(Function Template) 。函數(shù)模板代表了一個(gè)函數(shù)家族,該函數(shù)模板與類型無關(guān),在使用時(shí)被參數(shù)化,根據(jù)實(shí)參類型產(chǎn)生函數(shù)的特定類型版本。

函數(shù)模板格式

template<typename T1, typename T2,…,typename Tn>
返回值類型 函數(shù)名(參數(shù)列表){}

template<typename T>
//或者 template<class T>
void Swap(T& x1, T& x2)
{
    T temp = left;
    left = right;
    right = temp;
}

T1,T2等等是什么類型現(xiàn)在也不確定,一會(huì)用的時(shí)候才能確定

注意:

typename是用來定義模板參數(shù)關(guān)鍵字,也可以使用class

函數(shù)模板的原理

函數(shù)模板本身并不是函數(shù),是編譯器根據(jù)調(diào)用的參數(shù)類型產(chǎn)生特定具體類型函數(shù)的模具,所以其實(shí)模板就是將本來應(yīng)該我們做的重復(fù)的事情交給了編譯器,我們看下面的例子:

template<class T>
void Swap(T& x, T& y)
{
	T temp = x;
	x = y;
	y = temp;
}
int main()
{
	int a = 1;
	int b = 2;
	Swap(a, b);
	char A = 'a';
	char B = 'b';
	Swap(A,B);
	return 0;
}

image-20211024163521112

在編譯器編譯階段,對(duì)于模板函數(shù)的使用,編譯器需要根據(jù)傳入的實(shí)參類型來推演生成對(duì)應(yīng)類型的函數(shù)以供調(diào)用。比如:當(dāng)用int類型使用函數(shù)模板時(shí),編譯器通過對(duì)實(shí)參類型的推演,將T確定為int類型,然
后產(chǎn)生一份專門處理int類型的代碼,對(duì)于字符類型也是如此。

然而當(dāng)我們?cè)趯懥撕瘮?shù)時(shí),不會(huì)進(jìn)入模板函數(shù)里,沒有寫具體的函數(shù)時(shí),就會(huì)進(jìn)入模板函數(shù)里,我們看下面的例子:

void Swap(int& x, int& y)
{
	int temp = x;
	x = y;
	y = temp;
}
template<class T>
void Swap(T& x, T& y)
{
	T temp = x;
	x = y;
	y = temp;
}
int main()
{
	int a = 1;
	int b = 2;
	Swap(a, b);
	char A = 'a';
	char B = 'b';
	Swap(A,B);
	return 0;
}

我們進(jìn)行調(diào)式:

模板

我們可以看到int類型的交換函數(shù)我們寫了,調(diào)用時(shí)調(diào)用的是我們寫的,而char類型的我們沒寫,就用了模板。

那么這里調(diào)用的是模板函數(shù)嗎?

不是的,實(shí)際上這里會(huì)有兩個(gè)過程

1、模板推演,推演T的具體類型是什么

2、推演出T的具體類型后實(shí)例化生成具體的函數(shù)

上面的代碼實(shí)例化生成了下面的函數(shù):

void Swap(char& x, char& y)
{
	char temp = x;
	x = y;
	y = temp;
}

真正調(diào)用的還是兩個(gè)函數(shù),但是其中的一個(gè)函數(shù)不是我們自己寫的,而是我們給了編譯器一個(gè)模板,然后編譯器進(jìn)行推演在編譯之前實(shí)例化生成三個(gè)對(duì)應(yīng)的函數(shù),模板是給編譯器用的,編譯器充當(dāng)了寫函數(shù)的工具:

image-20211024161726234

可以看到這里是調(diào)用了Swap<char>函數(shù)

在C++當(dāng)中,其實(shí)內(nèi)置類型也可以像自定義類型那樣這樣初始化:

int a(1);
int(2);//匿名

image-20211024162224522

void Swap(T& x1, T& x2)
{
    T temp(x1);
    x1 = x2;
    x2 = x1;
}

所以模板還可以這樣寫,可以使內(nèi)置類型和自定義類型兼容:

void Swap(T& x1, T& x2)
{
    T temp(x1);
    x1 = x2;
    x2 = x1;
}

我們來具體看一看函數(shù)模板的實(shí)例化:

函數(shù)模板的實(shí)例化

用不同類型的參數(shù)使用函數(shù)模板時(shí),稱為函數(shù)模板的實(shí)例化。模板參數(shù)實(shí)例化分為:隱式實(shí)例化和顯式實(shí)例化。

隱式實(shí)例化:讓編譯器根據(jù)實(shí)參推演模板參數(shù)的實(shí)際類型

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main()
{
    int a1 = 10, a2 = 20;
    double d1 = 10.0, d2 = 20.0;
    Add(a1, a2);
    Add(d1, d2);
// 此時(shí)有兩種處理方式:1. 用戶自己來強(qiáng)制轉(zhuǎn)化 2. 使用顯式實(shí)例化
    Add(a1, d2);
    return 0;
}

該語句是不能夠通過編譯的,因?yàn)樵诰幾g期間,當(dāng)編譯器看到該實(shí)例化時(shí),用a1去推T是int,而用d2去推是double,但是模板參數(shù)列表里只有一個(gè)T,編譯器不能明確該T是int還是double,T是不明確的,所以編譯器會(huì)報(bào)錯(cuò)

那么怎么處理呢?

解決方式:

1、調(diào)用者自己強(qiáng)制轉(zhuǎn)換

//實(shí)參去推演形參的類型
Add(a1, (int)d2);
Add((double)a1,d2);

這里可以將d2先強(qiáng)制類型轉(zhuǎn)換,然后再進(jìn)行推演;或者將a1先強(qiáng)制類型轉(zhuǎn)換再進(jìn)行推演

2、使用顯式實(shí)例化

//實(shí)參不需要去推演形參的類型,顯式實(shí)例化指定T的類型
Add<int>(a1, d2);
Add<double>(a1,d2);

這種方式是顯式實(shí)例化指定T的類型

顯式實(shí)例化在哪種場(chǎng)景可用呢?看下面的這種場(chǎng)景:

class A
{
    A(int a=0):_a(a)
    {}
private:
    int _a;
};
template<class T>
T func(int x)
{
    T a(x);
    return a;
}
int main()
{
    func<A>(1);
    func<int>(2);
    return 0;
}

有些函數(shù)模板里面參數(shù)中沒用模板參數(shù),函數(shù)體內(nèi)才有用到模板參數(shù),此時(shí)就無法通過參數(shù)去推演T的類型,這時(shí)只能顯示實(shí)例化

上面我們提了一點(diǎn)模板參數(shù)的匹配原則,下面我們具體看看模板參數(shù)的匹配原則:

模板參數(shù)的匹配原則

 一個(gè)非模板函數(shù)可以和一個(gè)同名的函數(shù)模板同時(shí)存在,此時(shí)如果調(diào)用地方參數(shù)與非模板函數(shù)完全匹配,則會(huì)調(diào)用非模板函數(shù)

int Add(int left, int right)
{
	return left + right;
}
// 通用加法函數(shù)
template<class T>
T Add(T left, T right)
{
	return left + right;
}
int main()
{
    Add(1,2);//調(diào)用自己的函數(shù)
    return 0;
}

Add(1,2)參數(shù)是int類型,而我們有現(xiàn)成的int參數(shù)的Add函數(shù),所以有現(xiàn)成的就用現(xiàn)成的,編譯器也會(huì)偷懶

那么如果我們想讓這里調(diào)用必須用模板呢?顯式實(shí)例化:

Add<int>(1,2);

這樣編譯器就強(qiáng)制會(huì)用模板去實(shí)例化函數(shù)

一個(gè)非模板函數(shù)可以和一個(gè)同名的函數(shù)模板同時(shí)存在,此時(shí)如果調(diào)用地方參數(shù)與非模板函數(shù)不完全匹配,則會(huì)優(yōu)先使用模板實(shí)例化函數(shù)

int Add(int left, int right)
{
	return left + right;
}
// 通用加法函數(shù)
template<class T>
T Add(T left, T right)
{
	return left + right;
}
int main()
{
    Add(1.1,2.2);//使用模板實(shí)例化函數(shù)
    return 0;
}

模板匹配原則總結(jié):

有現(xiàn)成完全匹配的,那就直接調(diào)用,沒有現(xiàn)成調(diào)用的,實(shí)例化模板生成,如果有需要轉(zhuǎn)換類型才能匹配的函數(shù)(也就是不完全匹配),那么它會(huì)優(yōu)先選擇去實(shí)例化模板生成。

優(yōu)先級(jí):

完全匹配>模板>轉(zhuǎn)換類型匹配

類模板

類模板的定義格式

template<class T1, class T2, ..., class Tn>
class 類模板名
{
	//類內(nèi)成員定義
};

我們來看一個(gè)類模板的使用場(chǎng)景:

typedef int STDateType;
class Stack
{
private:
    STDateType* _a;
    int _top;
    int _capacity;
};
int main()
{
    Stack st1;
    Stack st2;
    return 0;
}

這是我們定義的棧數(shù)據(jù)結(jié)構(gòu),我們創(chuàng)建了兩個(gè)棧對(duì)象,但是現(xiàn)在st1和st2的存儲(chǔ)數(shù)據(jù)的類型都是int,要是想轉(zhuǎn)換數(shù)據(jù)類型呢?

typedef double STDateType;

我們這樣就轉(zhuǎn)換了,但是我們要是想st1為int,st2為double呢:

Stack st1;//int
Stack st2;//double

此時(shí)需要寫多個(gè)類,名字還得不一樣,如下:

typedef int STDateType1;
typedef double STDateType2;
class IntStack
{
private:
    STDateType1* _a;
    int _top;
    int _capacity;
};
class DoubleStack
{
private:
    STDateType2* _a;
    int _top;
    int _capacity;
};

這樣太麻煩了,那么什么辦法可以解決呢?類模板可以解決:

//類模板
template<class T>
class Stack
{
private:
    T* _a;
    int _top;
    int _capaticy;
};
int main()
{
    //類模板的使用都是顯式實(shí)例化
    Stack<double> st1;
    Stack<int> st2;
    return 0;
}

注意:Stack不是具體的類,是編譯器根據(jù)被實(shí)例化的類型生成具體類的模具

類模板的實(shí)例化

//類模板
template<class T>
class Stack
{
public:
    Stack(int capacity = 4)
        :_a(new T(capacity))
         ,_top(0)
         ,_capacity(capacity)
        {}
    ~Stack()
    {
        delete[] _a;
        _a = nullptr;
        _top = _capacity = 0;
    }
    void Push(const T& x)
    {
        //...
    }
private:
    T* _a;
    int _top;
    int _capaticy;
};
int main()
{
    //類模板的使用都是顯式實(shí)例化
    Stack<double> st1;
    Stack<int> st2;
    return 0;
}

注意:類模板的使用都是顯式實(shí)例化

假設(shè)我們想類里面聲明和類外面定義成員函數(shù)呢?

//類模板
template<class T>
class Stack
{
public:
    Stack(int capacity = 4)
        :_a(new T(capacity))
         ,_top(0)
         ,_capacity(capacity)
        {}
    ~Stack()
    {
        delete[] _a;
        _a = nullptr;
        _top = _capacity = 0;
    }
    //假設(shè)我們想類里面聲明和定義分離呢?
    void Push(const T& x);
private:
    T* _a;
    int _top;
    int _capaticy;
};
//在類外面定義
template<class T>
void Stack<T>::Push(const T& x);
{
    //...
}
int main()
{
    //類模板的使用都是顯式實(shí)例化
    Stack<TreeNode*> st1;
    Stack<int> st2;
    return 0;
}
//在類外面定義
template<class T>
void Stack<T>::Push(const T& x);
{
    //...
}

在類外面定義我們必須要加模板的關(guān)鍵字,以及需要在實(shí)現(xiàn)的函數(shù)前面表明域Stack<T>。普通類,類名就是類型,對(duì)于類模板,類名不是類型,類型是Stack<T>,需要寫指定

注意:

模板不支持把聲明寫到.h,定義寫到.cpp,這種聲明和定義分開實(shí)現(xiàn)的方式,會(huì)出現(xiàn)鏈接錯(cuò)誤

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

最新評(píng)論