目錄
1. 泛型編程
2. 函數模板
2.1 函數模板的概念及格式
?2.2 函數模板的原理
?2.3 模板的實例化
2.4 模板參數的匹配原則
?3. 類模板
3.1 類模板格式
3.2 類模板的實例化
1. 泛型編程
? ? ? ? 什么是泛型編程?泛型編程是避免使用某種具體類型而去使用某種通用類型來進行程序編寫的方式,依次來復用某段代碼而避免大規模功能相似重復冗余的代碼。下面的代碼如果想用泛型編程該如何實現。
int add(int a,int b)
{return a + b;
}
double add(double a, int b)
{return a + b;
}
double add(int a, double b)
{return a + b;
}
????????馬云有次說了這么一句話:“世界是懶人創造的,懶不是傻懶,如果你想少干,就要想出懶的方法。 要懶出風格,懶出境界。”C++必然也是有風格有境界的,所以C++中設計了模板實現了泛型編程。
2. 函數模板
2.1 函數模板的概念及格式
? ? ? ? 模板就是一種模具,通過給這個模具中放不同的材料(類型),來獲得不同材料的產品,以此來提高我們的工作效率。而函數模板就是某個函數的模具,與類型無關,在使用的時候參數化,在我們給出特定類型就會生成特定類型的版本。
? ? ? ? 函數模板的格式:
template<typename T1, typename T2 ...typename Tn>
返回值類型 函數名(形參列表)
{
?? ?//函數體
}
? ? ? ? ?根據這個函數的模板我們就可以實現上述add函數的模板:
template<typename L, typename R,typename RET>
RET add(L l, R r)
{return l + r;
}
注:typename是用來定義模板參數的關鍵字,也可以用class代替。
?2.2 函數模板的原理
? ? ? ? 函數模板本身不是函數,而是一個模具,當我們傳入實際類型的時候編譯器會根據我們傳入的實參類型來推演生成對應類型的函數進行調用。
?2.3 模板的實例化
? ? ? ? 當我們將類型傳入模板來生產函數的時候,稱之為模板的實例化,模板的實例化分為隱式實例化和顯式實例化。
????????1. 隱式實例化:隱式實例化就是我們不去指定類型,讓編譯根據我們傳入的實參判斷其類型傳入模板參數列表進行實例化。如下:
template<typename L, typename R>
int add(L l, R r)
{return l + r;
}
int main()
{int a = 0, b = 1;cout << add(a, b) << endl; //根據我傳入的a和b自動判斷類型進行模板實例化。
}
? ? ? ? 細心的同學發現,這里我將原來的代碼中?typename RET?刪掉了,原因就是模板不會對函數的返回值類型進行自動判斷,而需要我們手動指定(也就是顯式實例化)。
????????2. 顯式實例化:顯式實例化就是我們手動向模板傳遞類型,然后由編譯器進行實例化。顯式實例化的格式為:
函數名<類型1,類型2,...>(實參列表);
????????如:?
template<typename L, typename R, typename RET>
RET add(L l, R r)
{return l + r;
}
int main()
{int a = 0, b = 1;cout << add<int,int,int>(a, b) << endl;
}
? ? ? ? 上述代碼我們傳進去的 a和b的類型與模板參數類型是匹配的,如果不匹配的話編譯器會進行隱式類型轉化,如果轉化失敗就報錯。如下:
? ? ? ? 類型轉換成功:
template<typename L, typename R, typename RET>
RET add(L l, R r)
{return l + r;
}
int main()
{double a = 0.1;int b = 1;cout << add<int,int,int>(a, b) << endl; //a:double類型隱式轉換成int類型
}
輸出:
? ? ? ? 類型轉化失敗:?
template<typename L, typename R, typename RET>
RET add(L l, R r)
{return l + r;
}class Date
{//
};
int main()
{int a = 0, b = 1;cout << add<int,Date,int>(a, b) << endl; //a:類型轉化失敗
}
輸出:
E0304?? ?沒有與參數列表匹配的 函數模板 "add" 實例
2.4 模板參數的匹配原則
? ? ? ? 一個函數的模板函數可以與非模板函數同時存在,并且在沒有顯式實例化且類型匹配的情況下會優先匹配非模板函數,如果是顯式實例化才會調用模板,如下。
template<class T>
void swap(T& a, T& b)
{std::swap(a, b);std::cout << "我是模板 ";std::cout << a << ' ' << b << std::endl;}
void swap(int& a, int& b)
{std::swap(a, b);std::cout << "我是非模板 ";std::cout << a << ' ' << b << std::endl;}
int main()
{int a = 1, b = 2;std::cout << a << ' ' << b << std::endl;swap(a, b); //類型匹配優先調用非模板函數swap<int>(a, b); //調用模板實例化
}
輸出:
? ? ? ? 當我們沒有去寫模板的時候,函數發生類型不匹配可能會進行隱式類型轉換,但是有了模板之后就不會發生隱式類型轉化,而是使用模板實例化出來一個更為合適的函數。如下:
? ? ? ? 沒有模板時會發生隱式實例化:
void test(int a, int b)
{std::cout << "我不是模板" << std::endl;
}int main()
{test(2, 2.0);//2.0發生隱式類型轉化成int;
}
輸出:
? ? ? ? 存在模板不會發生隱式類型轉化,而是使用模板實例化出來一個更為合適的函數:
template<class T1,class T2>
void test(T1 a, T2 b)
{std::cout << "我是模板" << std::endl;
}
void test(int a, int b)
{std::cout << "我不是模板" << std::endl;
}
int main()
{test(2, 2.0);//不發生隱式類型轉化而是隱式實例化模板
}
輸出:
?3. 類模板
3.1 類模板格式
? ? ? ? 和函數模板一樣,類模板是類的一個模具,類模板格式如下:
template<class T1,class T2,class T3,...class Tn>
class 類模板名
{
?? ?//類體
};
? ? ? ? 我們普通類是可以聲明和定義分離的,如果類模板要實現聲明和定義分離,那么在定義的時候也要加上模板聲明。如:
template<class T1, class T2>
class A
{~A(); //聲明
};template<class T1,class T2>
A<T1, T2>::~A() //定義
{//...
}
3.2 類模板的實例化
?????????類模板的實例化和函數模板相同,需要在類名后加上尖括號并且指定類型。注:模板名不是類,實例化后才是一個類。
template<class T>
class Date
{//..
};
int main()
{Date<int>;
}