C++入門之模板基礎講解

前言

今天博主將要介紹的內容是–模板,他在C++中具有非常重要的位置.至於什麼是模板呢?我們請看下面的章節.

引入

我們對交換函數Swap已經非常熟悉瞭,但是我們經常會遇到這樣的一些事,比如,很多不同的數據類型進行交換,那麼我們就需要寫不同的重載Swap,如下:

#include <iostream>
using namespace std;
void Swap(int& a,int& b){
    int t  = a;
    a = b;
    b = t;
}
void Swap(double& a,double& b){
    double t = a;
    a = b;
    b = t;
}
int main()
{
    int a = 10,b = 20;
    double c = 1.2,d = 3.4;
    Swap(a,b);
    Swap(c,d);
    return 0;
}

可以看到,如果有必要,我們需要交換幾種類型的數據,就必須寫上幾種重載Swap,這就導致非常的繁瑣,因為我們對其交換邏輯太熟悉瞭,隻是換瞭變量類型,那有什麼辦法可以解決呢?沒錯,這就是我們今天要講的模板.

模板

概念:在生活中,博主舉一個例子,假設你是一個手辦廠傢,現在你需要售出各種材料和顏色做的悟空手辦,首先你需要的就是悟空的模型,然後按照這個模型使用不同的材料.這個模型就是我們在程序中的模板.

模板種類:

  • 函數模板
  • 類模板

函數模板

函數模板的格式:

template<class T1,class T2,...> 
    return_val function_name (para1,para1,...)
{
}

打省略號的都是形參列表,表示參數量自由,我們現在知道瞭怎樣使用函數模板,那試試寫一個Swap模板:

template<class T> 
void Swap (T& a,T& b)
{
    T t = a;
    a = b;
    b = a;
}

按照模板規范,我們寫出來瞭Swap的模板,現在測試一下結果是否正確呢?

image-20211027124124726

發現測試結果完全正確.

然後這裡博主有個疑問,就是我們調用Swap時,編譯器是執行的上面模板呢?還是執行的通過模板推演出來的函數呢?

答:通過模板推演出來的函數,因為C++提出模板是為瞭節省程序員的時間,我們所省略的工作,隻是編譯器替我們完成瞭.

也就是說,比如我們這樣調用:

int main()
{
    int a = 10,b = 20;
    Swap(a,b);
    return 0;
}

那麼編譯器在底部會推演出一個如下函數,並執行:

void Swap(int& a,int& b){
    int t  = a;
    a = b;
    b = t;
}

模板的匹配原則

什麼叫做模板的匹配原則呢?

就是說當既有定義出來的明確函數,同時又有模板,那麼調用函數時候,執行的是哪一個?以下面為例:

void Swap(int& a,int& b){
    int t  = a;
    a = b;
    b = t;
}
template<class T> 
void Swap (T& a,T& b)
{
    T t = a;
    a = b;
    b = a;
}
int main()
{
    int a = 10,b = 20;
    Swap(a,b);
    return 0;
}

匹配順序為:

  • 如果有定義出來的函數,且類型完全匹配調用時實參類型,則執行定義出來的函數.
  • 如果定義出來的函數,不符合,則執行模板推演.

也就是說,上面的例子中,Swap調用的是我們定義出來的Swap,而不是模板.

模板的顯示調用

上面講解的模板使用,其實被稱做隱式調用,現在,博主介紹一下顯示調用.

顯示調用格式:

function<Type1,Type2,..>(para1,para1,...);

也就是說,我們明確知道需要使用的什麼類型,並且就想告訴編譯器,我傳給你的是什麼類型,然後讓編譯器推演出該類型的函數.比如下面使用:

int a = 10,b = 20;
char c = 'a',d = 'b';
Swap<int>(a,b);  //告訴編譯器我傳的是int類型
Swap<char>(c,d);  //告訴編譯器我傳的是char類型

大傢可能會問瞭,這有什麼用呢?既然編譯器會根據我們的調用情況進行推演,還進行顯示調用不是多此一舉嗎?大傢請看下面的代碼:

template<class T> 
void Swap (T& a,T& b)
{
    T t = a;
    a = b;
    b = a;
}
int main()
{
    int a = 10;
    double b = 23.22;
    Swap(a,b);         //這樣調用編譯器就會報錯
    return 0;
}

像上面的調用方式,是不允許的,因為模板中隻有一個T,但是我們傳瞭兩個類型,編譯器根據模板將不知道T應該是啥類型,而解決上面的問題隻有兩種

  • 一是強制性轉換類型,比如Swap(a,(int)b);
  • 二是顯示使用模板,比如Swap<int>(a,b);

其次,博主講解模板的顯示調用還有一個目的就是為瞭引出下面的類模板.

類模板

類模板和函數模板相似,定義框架如下:

template <class T1,class T2,...>
    class class_name
    {
    };

我們對數據結構—棧,應該算比較瞭解,而對於經常刷力扣的夥伴來說,可能會發現棧不隻是用來存儲int類型,比如還有ListNode*等,那我們大概寫一下其stack模板吧.

template <class T>
class Stack
{
public:
    Stack():data(new T*[10]),top(0),capacity(10) {}
    ~Stack() 
    {
        delete[] data; 
        top = capacity = 0;
    }
    void Push(T& a)
    {}
private:
    T* data;
    int top;
    int capacity;
};

然後我們定義Stack對象,但是對象應該存儲的類型是什麼呢?如果我們繼續用最開始的隱式模板方法,發現完全實現不瞭,這也就是博主上面為何要講解模板的顯示調用,因為類模板隻能通過顯示調用實現,例子使用如下:

Stack <char> st1;      //定義一個存儲char類型的棧
Stack <int> st2;       //定義一個存儲int類型的棧
Stack <double> st3;    //定義一個存儲double類型的棧

Stack並不是類,其隻是一個模板,Stack <int>等才是類

註意1

類模板隻是一個模板,他並不屬於類.

註意2

當我們的模板類中的成員函數,在模板中聲明,而在模板外定義時,需要加上模板參數列表,如下:

template <class T>
class Stack
{
public:
    void Push(T& a);
    bool empty();
private:
    T* data;
    int top;
    int capacity;
};
template <class T> 
void Stack<T>:: Push(T& a)     //需要加上template <class T> ,且在Stack後面加上<T>
{}
template <class T>   
void Stack<T>:: empty()        //需要加上template <class T> ,且在Stack後面加上<T>
{}

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: