函數模板
函數模板,是可以創(chuàng)建一個通用的函數,可以支持多種形參。
用關鍵字 template 來定義,
在函數模板中,數據的值和類型都被參數化了,發(fā)生函數調用時編譯器會根據傳入的實參來推演形參的值和類型。
template <class 類型名1,class 類型名2…>
返回值 函數名(形參表列) 模板參數表
{
// 函數體
}
第一行的template<class 類型名1,class 類型名2…>是一句聲明語句
template 是定義模板函數的關鍵字
尖括號里可以有多個類型
用class(或者typename來定義)。
然后后面跟定義的函數模板,切記中間不可以加其他的語句,不然會報錯!
#include <iostream>
#include <stdlib.h>
using namespace std;
template <typename T>
void print(T);
int main()
{
print(55);
print('Y');
print("Hello");
system("pause");
return 0;
}
template <typename T>
void print(T x)
{
cout
<< x
<< endl;
}
函數模板也可以提前聲明,不過聲明時需要帶上模板頭,并且模板頭和函數定義(聲明)是一個不可分割的整體,它們可以換行,但中間不能有分號。
類模板
類的定義格式:
template<typename 類型參數1 , typename 類型參數2 , …> class 類名{
//TODO:
};
例:
template<class T1,class T2>
class Student
{
// 成員變量 成員函數
};
在類外定義成員函數時仍然需要帶上模板頭
成員函數定義模板格式:
template<typename 類型參數1 , typename 類型參數2 , …>
返回值類型 類名<類型參數1 , 類型參數2, ...>::函數名(形參列表){
//TODO:
}
例:
template<class T1, class T2>
void Student<T1, T2>::say(){
};
構造函數 以及 析構函數 的模板 ,兩者類似,析構函數只需要 多添加一個 ~ 符,且沒有參數
template<typename 類型參數1 , typename 類型參數2 , …>
類名<類型參數1 , 類型參數2, ...>::類名(形參列表) : 變量初始化{
//TODO:
}
例:
// 構造函數
template<class T1, class T2>
Student<T1, T2>::Student(T1 a, T2 b) :m_a(a), m_b(b) {};
// 析構函數
template<class T1, class T2>
Student<T1, T2>::~Student() {
};
指針對象 注意事項:
- 賦值號兩邊的數據類型必須一致
- 賦值號右邊需要指明數據類型
格式:
類名<類型參數1,類型參數2 ····> 對象名 = new 類名<類型參數1,類型參數2 ····>(初始化參數1,參數2 ·····);
例:
Student<string, string> *Man = new Student<string, string>("渾元形意太極門掌門人", "馬保國");
#include <iostream>
#include <string>
using namespace std;
template<class T1,class T2>
class Student
{
private:
T1 m_a;
T2 m_b;
public:
Student(T1 a, T2 b);
void say();
~Student();
};
// 構造函數
template<class T1, class T2>
Student<T1, T2>::Student(T1 a, T2 b) :m_a(a), m_b(b) {};
// 成員函數
template<class T1, class T2>
void Student<T1, T2>::say(){
cout << this->m_a << this->m_b << endl;
};
// 析構函數
template<class T1, class T2>
Student<T1, T2>::~Student() {
cout << this->m_a << this->m_b << endl;
};
int main()
{
Student<string,string> Sir("渾元形意太極門掌門人","馬保國");
Sir.say();
Student<int,char> c(2,'B');
c.say();
Student<string, string> *Man = new Student<string, string>("練習兩年半的偶像練習生", "坤坤");
Man->say();
delete Man;
return 0;
}
結果:
渾元形意太極門掌門人馬保國
2B
練習兩年半的偶像練習生坤坤
練習兩年半的偶像練習生坤坤
2B
渾元形意太極門掌門人馬保國
類模板的靜態(tài)成員
靜態(tài)模板成員變量
static 數據類型 變量名;
初始化格式:
template<typename 類型參數1 , typename 類型參數2 , …>
數據類型 類名<類型參數1 , 類型參數2, ...>::變量名 = 數據;
注意: 數據類型定義與初始化要一致
不使用模板定義靜態(tài)變量:
static T1 count1;
// 模板靜態(tài)成員變量初始化
template<class T1, class T2>
T1 Student<T1, T2>::count1 = 0;
使用固定類型定義靜態(tài)變量:
static int count2;
// 模板靜態(tài)成員變量初始化
template<class T1, class T2>
int Student<T1, T2>::count2 = 0;
注意:
靜態(tài)成員變量,可以在類的內部使用,可以在外部進行操作
靜態(tài)成員變量使用模板定義時,當傳入的模板類型不是可以操作的類型就會報錯
原本希望傳入的是整型,然后進行++操作
結果傳入了string類型,由于不匹配所以會出現以下錯誤提示:
錯誤 C2676 二進制“++”:“T1”不定義該運算符或到預定義運算符可接收的類型的轉換
#include <iostream>
#include <string>
using namespace std;
template<class T1, class T2>
class Student
{
public:
static T1 count; // 計數
private:
T1 m_a;
T2 m_b;
public:
Student(T1 a, T2 b);
void say();
};
// 構造函數
template<class T1, class T2>
Student<T1, T2>::Student(T1 a, T2 b) :m_a(a), m_b(b) {
//count++; //傳入的類型T1 不為整數可操作類型,就會報錯, C2676
};
// 成員函數
template<class T1, class T2>
void Student<T1, T2>::say() {
cout << ". " << this->m_a << this->m_b << endl;
// 靜態(tài)成員變量初始化之后,在類中使用時,會引發(fā)異常 此位置的異常 : return (_CSTD strlen(_First));
//cout << this->count << endl;
};
template<class T1, class T2>
T1 Student<T1, T2>::count = 0;// 僅初始化,不在類中使用,就不會出現問題,但也就好無意義了
int main()
{
Student<string, string> Sir("渾元形意太極門掌門人", "馬保國");
// 因為傳入了string類型,count可以手動賦值來計數,但無法通過,會引發(fā)異常, 此位置的異常 : return (_CSTD strlen(_First));
// Sir.count = "1";
Sir.say();
return 0;
}
引起一系列問題的原因可能為:
1. 設置的類型初始化問題
不能匹配確定是否合理初始化
2. 設置類型與想要操作的類型不匹配
希望使用 ++等一些列運算符 操作的 整數或浮點數類型
時間類型為 string
傳入類型合理類型,修改上例的相關區(qū)域:
// 構造函數
template<class T1, class T2>
Student<T1, T2>::Student(T1 a, T2 b) :m_a(a), m_b(b) {
count++;
};
// 成員函數
template<class T1, class T2>
void Student<T1, T2>::say() {
cout << ". " << this->m_a << this->m_b << endl;
cout << this->count << endl;
};
// 初始化
template<class T1, class T2>
T1 Student<T1, T2>::count = 0;
// 創(chuàng)建對象
Student<int, string> Sir(2,"B" );
Sir.count++;
Sir.say();
結果:
. 2B
2
因此解決此類問題,就可以為static類型變量,創(chuàng)建 特定的構造函數 以及 模板
#include <iostream>
#include <string>
using namespace std;
// T3專門用來給靜態(tài)變量傳遞模板數據類型
template<class T1, class T2,class T3>
class Student
{
public:
static T3 count; // 計數
private:
T1 m_a;
T2 m_b;
public:
Student(T1 a, T2 b);
void say();
};
// 構造函數,可以設置空的構造函數,之后通過接口傳值,此處就省略
template<class T1, class T2,class T3>
Student<T1, T2, T3>::Student(T1 a, T2 b) :m_a(a), m_b(b) {
count++;
};
// 成員函數
template<class T1, class T2, class T3>
void Student<T1, T2, T3>::say() {
cout << this->count << ". " << this->m_a << this->m_b << endl;
};
// 靜態(tài)成員變量的初始化
template<class T1, class T2, class T3>
T3 Student<T1, T2, T3>::count = 0;
int main()
{
Student<string, string,int> Sir("渾元形意太極門掌門人", "馬保國");
Sir.say();
Student<string, string,int> *Man = new Student<string, string,int>("練習兩年半的偶像練習生", "坤坤");
Man->say();
delete Man;
Student<int, char,int> c(2, 'B');
c.say();
Student<int, char,int> l(3, 'B');
l.say();
Student<int, char,int> s(6, 'B');
s.say();
return 0;
}
結果:
1-1. 渾元形意太極門掌門人馬保國
2-2. 練習兩年半的偶像練習生坤坤
1-1. 2B
2-2. 3B
3-3. 6B
- 通過靜態(tài)變量可以知道,每次類的模板不同,就會產生新的類,且不互通
上例產生了兩個類的格式
Student<int, char,int>Student<string, string,int>
類模板與友元函數
// 模板類
template<typename T>
class Student
{
private:
T m_x;
T m_y;
public:
// 構造函數
Student(T x, T y) :m_x(x), m_y(y) {};
// 友元函數
friend T sum(Student<T> s);
};
// 函數模板
template<typename T>
T sum(Student<T> s)
{
return s.m_x + s.m_y;
}
嚴重性 代碼 說明 項目 文件 行 禁止顯示狀態(tài)
錯誤 LNK2019 無法解析的外部符號 "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl sum(class Student<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >)" (?sum@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$Student@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@@Z),該符號在函數 "void __cdecl Study(void)" (?Study@@YAXXZ) 中被引用 學習 E:\C++\學習\學習\Study.obj 1
類模板 不能 引入函數模板,雖然程序沒有錯誤,但是在編譯時就會出錯
友元函數 需要指明 具體的參數類型 ,可以通過 重載 去定義多個 友元函數
注意: 友元函數 無法直接 訪問類模板的參數,需要傳遞 類的對象 或 對象指針
#include <iostream>
#include <string>
using namespace std;
// 模板類
template<typename T>
class Student
{
private:
T m_x;
T m_y;
public:
// 構造函數
Student(T x, T y) :m_x(x), m_y(y) {};
// 友元函數的聲明
friend string sum(Student<string> s);
friend int sum(Student<int> s);
};
// 友元函數的定義
string sum(Student<string> s)
{
return s.m_x + s.m_y;
}
int sum(Student<int> s)
{
return s.m_x + s.m_y;
}
int main()
{
Student<string> Sir("渾圓形意太極門掌門人", "馬保國");
cout << sum(Sir) << endl;
Student<int> Age(6,9);
cout << sum(Age) << endl;
return 0;
}
類模板與 友元 類的成員函數
friend 表示 出現 friend 的類
member 表示 成員函數的類
定義的一般順序:
第一步:
class friend;
第二步:
template <class T1, ···· >
class member{
// 成員變量 , 成員函數
// 需要 友元的成員函數 聲明
}
第三步:
class friend{
// 成員變量 , 成員函數
// friend member類的 友元成員函數
}
第四步:
member類的 友元成員函數 的實現
例子:
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class Student; // 同樣很有必要的聲明
// 第一個類, member類
class Introduct
{
public:
// 成員函數
string sum(Student<string>* s); // 對象指針
int sum(Student<int> s); // 對象
};
// 第二個類,友元類
template<typename T>
class Student
{
private:
T m_x;
T m_y;
public:
// 構造函數
Student(T x, T y) :m_x(x), m_y(y) {};
// 友元函數的聲明
friend string Introduct::sum(Student<string>* s);
friend int Introduct::sum(Student<int> s);
};
// 友元函數的定義
string Introduct::sum(Student<string>* s)
{
return s->m_x + s->m_y;
}
int Introduct::sum(Student<int> s)
{
return s.m_x + s.m_y;
}
int main()
{
Introduct Mk;
Student<string> Sir("渾圓形意太極門掌門人", "馬保國");
cout << Mk.sum(&Sir) << endl;
Student<int> Age(6,9);
cout << Mk.sum(Age) << endl;
return 0;
}
結果:
渾圓形意太極門掌門人馬保國
15
通過 重載 來擴展, member類的使用
同樣需要傳遞 實例對象 或 實例對象指針,訪問類的成員
類模板與 友元 模板類的成員函數
兩個類模板之間
- 提前聲明 member類
- 定義 friend類, 引入友元的成員函數
- 定義 member類
- 定義 模板成員函數
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class Introduct; // 有必要的提前定義
// 第一個類, 友元類
template<typename T>
class Student
{
private:
T m_x;
T m_y;
public:
Student(T x, T y) :m_x(x), m_y(y) {};
// 友元類
friend T Introduct<T>::sum(Student<T> s);
};
// 第二個類, member類
template<typename S>
class Introduct
{
public:
// 成員函數
S sum(Student<S> s);
};
// 模板成員函數
template<typename S>
S Introduct<S>::sum(Student<S> s)
{
return s.m_x + s.m_y;
}
int main()
{
Introduct<string> Mk;
Student<string> Sir("渾圓形意太極門掌門人", "馬保國");
cout << Mk.sum(Sir) << endl;
Introduct<int> M;
Student<int> Age(6, 9);
cout << M.sum(Age) << endl;
return 0;
}
注意:
在使用模板時,前后要對應
類模板 與 友元 類
優(yōu)先定義 友元類
#include <iostream>
#include <string>
using namespace std;
// 第一個類, 友元類
template<typename T>
class Student
{
private:
T m_x;
T m_y;
public:
Student(T x, T y) :m_x(x), m_y(y) {};
// 友元類
friend class Introduct;
};
// 第二個類, member類
class Introduct
{
public:
// 成員函數
string sum(Student<string>* s); // 對象指針
int sum(Student<int> s); // 對象
};
// Introduct成員函數
string Introduct::sum(Student<string>* s)
{
return s->m_x + s->m_y;
}
int Introduct::sum(Student<int> s)
{
return s.m_x + s.m_y;
}
int main()
{
Introduct Mk;
Student<string> Sir("渾圓形意太極門掌門人", "馬保國");
cout << Mk.sum(&Sir) << endl;
Student<int> Age(6,9);
cout << Mk.sum(Age) << endl;
return 0;
}
結果:
渾圓形意太極門掌門人馬保國
15
類模板 與 友元類 和 類模板與 類的友元成員函數 類似,不同點就是前者無需過多聲明 成員函數,后者需要依次 聲明 需要用到的 成員函數
類模板 與 友元 類模板
兩個 類模板
- 提前聲明 member類
- 定義 friend類 , 引入 友元類
- 定義 member類
- 定義 模板成員函數
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class Introduct; // 很有必要的聲明
// 第一個類, 友元類
template<typename T>
class Student
{
private:
T m_x;
T m_y;
public:
Student(T x, T y) :m_x(x), m_y(y) {};
// 友元類
friend class Introduct<T>;
};
// 第二個類, member類
template<typename S>
class Introduct
{
public:
// 成員函數
S sum(Student<S> s);
};
template<typename S>
S Introduct<S>::sum(Student<S> s)
{
return s.m_x + s.m_y;
}
int main()
{
Introduct<string> Mk;
Student<string> Sir("渾圓形意太極門掌門人", "馬保國");
cout << Mk.sum(Sir) << endl;
Introduct<int> M;
Student<int> Age(6, 9);
cout << M.sum(Age) << endl;
return 0;
}
結果:
渾圓形意太極門掌門人馬保國
15
類模板與非類型參數
在定義模板的時候,可以引入參數
不能使用 結構體 、string 等定義非類型參數
template <class T, 數據類型 參數名, ······ >
例如:
template <class T1 , int count , class T2>
class Student
{
}
在傳遞參數時,需要傳遞相對應的數據類型的數據
Student<int, 2 ,char> Sir;
例如:
#include <iostream>
#include <string>
using namespace std;
// 模板類
template<typename T, int count>
class Student
{
private:
T m_n;
public:
// 構造函數
Student(T n) :m_n(n){};
T say()
{
m_n += count;
return m_n;
}
};
//
void Study()
{
Student<int,2> Sir(1);
cout << Sir.say() << endl;
cout << Sir.say() << endl;
cout << Sir.say() << endl;
cout << Sir.say() << endl;
}
結果:
3
5
7
9
類模板的繼承
基類模板 與 子類
#include <iostream>
#include <string>
using namespace std;
// 基類
template <class T>
class People
{
public:
T m_num;
public:
// 構造函數
People(T num) : m_num(num) {};
// 成員變量
void show()
{
cout << "num = " << this->m_num << endl;
}
};
// 子類
class Student : public People<int> {
private:
int m_code;
public:
// 構造函數
Student(int code, int num) :m_code(code), People<int>(num) {};
// 成員變量
void show()
{
cout << "code = " << this->m_code << ";"
<< "num = " << this->m_num << endl;
}
};
int main()
{
People<int> sir_1(10);
sir_1.show();
Student sir_2(996, 777);
sir_2.show();
return 0;
}
結果:
num = 10
code = 996;num = 777
基類模板 與 子類模板
#include <iostream>
#include <string>
using namespace std;
// 基類
template <class T>
class People
{
public:
T m_num;
public:
// 構造函數
People(T num) : m_num(num) {};
// 成員變量
void show()
{
cout << "num = " << this->m_num << endl;
}
};
// 派生類
template <class S>
class Student : public People<S> {
private:
S m_code;
public:
// 構造函數
Student(S code, S num) :m_code(code), People<S>(num) {};
// 成員變量
void show()
{
cout << "code = " << this->m_code << ";"
<< "num = " << this->m_num << endl;
}
};
int main()
{
People<int> sir_1(10);
sir_1.show();
Student<int> sir_2(996, 777);
sir_2.show();
return 0;
}
結果:
num = 10
code = 996;num = 777
注意:構造函數,基類要指明類型,繼承是也要指明類型,且前后照應
模板實參推斷
從函數實參來確定模板實參的過程被稱為模板實參推斷(template argument deduction)
使用函數模板顯示實參,可以覆蓋實參推斷機制
#include <iostream>
#include <string>
using namespace std;
template<typename T1, typename T2>
int max(T1 a, T2 b)
{
cout
<< "T1 = " << sizeof(T1) << endl
<< "T2 = " << sizeof(T2) << endl;
if (a > b) return a;
else if (a == b) return 0;
else return b;
}
int main()
{
short sh = 6;
// 系統(tǒng)推斷
cout << max(sh, 3) << endl;
// 顯式定義
cout << max<int>(sh, 3) << endl;
cout << max<int,int>(sh, 3) << endl;
return 0;
}
結果:
T1 = 4
T2 = 2
6
T1 = 4
T2 = 2
6
T1 = 4
T2 = 4
6
模板實例化
模板實例化是生成采用特定模板參數組合的具體類或函數(實例)
編譯器生成一個采用 Array<int> 的類,另外生成一個采用 Array<double> 的類。通過用模板參數替換模板類定義中的模板參數,可以定義這些新的類。
隱式實例化
隱式實例化:通過編譯器自己推測判斷要實例化的類型。
編譯器會根據實參推斷類型
顯式實例化
模板顯式實例化(extern template)可以用來確保模板只被編譯器實例化一次
- 使用模板生成的編譯單元不會重復實例化,會加快編譯速度,并減小編譯單元的尺寸
要顯式實例化模板函數,在 template 關鍵字后接函數的聲明(不是定義),且函數標識符后接模板參數。
template 返回值類型 函數名 <數據類型>
例如:
template<typename T1, typename T2>
T1 max(T1 a, T2 b)
{
if (a > b) return a;
else if (a == b) return 0;
else return b;
}
template int max<int,int>(int a, int b); //顯式實例化,只需聲明
模板類的顯式實例化
template class 類名<數據類型>
例如:
template<typename T>
class Student
{
private:
T m_x;
T m_y;
public:
Student(T x,T y) :m_x(x), m_y(y) {};
void prin()
{
cout << m_x << m_y << endl;
}
};
// 顯式實例化 類模板
template class Student<char>;
template class Student<string>;
模板類靜態(tài)數據成員的顯式實例
template 返回值類型 類名<數據類型>::函數名(參數列表);
例如:
template<typename T>
class Student
{
private:
T m_x;
T m_y;
public:
Student(T x,T y) :m_x(x), m_y(y) {};
T sum(T a);
};
template<typename T>
T Student<T>::sum(T a)
{
return this->m_x + this->m_y + a;
}
// 顯式實例化 類模板
template string Student<string>::sum(string a);
template int Student<int>::sum(int a);
注意:類型的規(guī)范,使用相同模板在帶入數據類型時,也要同步,不要隨心所欲,想使用不同類型就分開定義,一句話:早知現在何必當初
模板顯式具體化
讓模板能夠針對某種具體的類型使用不同的算法(函數體或類體不同),稱為模板的顯示具體化(Explicit Specialization)
利用對函數模板的顯式具體化,對于 數組 和 結構體 數據類型進行操作
C++98標準 : 原型和定義以 template<> 開頭,并通過名稱指出類型。函數調用優(yōu)先級是 非模板函數 > 具體化模板函數 > 常規(guī)模板函數。
在函數模板 或 類模板 的基礎上,新添加一個專門針對 特定類型 的、實現方式不同 的 具體化 函數或類
模板函數顯式具體化
template<>
數據類型 函數名<復雜數據類型>(參數列表){
// 函數體
}
例如:
template <class T>
T stripling(T a, T b)
{
return a < b ? a : b;
};
// 只有定力了原型模板,才能定義如下的顯式具體化,原型模板如上 template <class T> T stripling(T a, T b)
// People 為定義的結構體類型
template <>
People stripling<People>(People a, People b)
{
return a.age < b.age ? a : b;
};
#include <iostream>
#include <string>
using namespace std;
typedef struct{
string name;
int age;
}People;
// 輸出 較大值
template <class T>
T stripling(T a, T b)
{
return a < b ? a : b;
};
// 需要有原型,如上 template <class T> T stripling(T a, T b)
template <>
People stripling<People>(People a, People b)
{
return a.age < b.age ? a : b;
};
void prin(People sir)
{
cout << sir.name << "," << sir.age << "歲,不講武德" << endl;
}
int main()
{
People young1 = { "八十公斤的年輕人",35 };
People young2 = { "九十公斤的年輕人",30 };
People leader = { "馬保國",69 };
prin(stripling(young1,leader));
prin(stripling(young2,leader));
return 0;
}
模板類的顯式具體化
在類模板的具體化中,成員方法的實例化是不能帶模板頭template<>的。
template<>
class 類名<復雜數據類型>
{
// 成員變量
// 成員函數
}
例如:
template <class T1, class T2>
class People {
// 成員變量 成員函數
}
// msg 為 結構體
template<>
class People<msg , msg>
{
// 成員變量 成員函數
}
#include <iostream>
#include <string>
using namespace std;
template <class T1, class T2>
class People
{
private:
// 成員變量
T1 m_x;
T2 m_y;
public:
// 構造函數
People(T1 x, T2 y) :m_x(x), m_y(y) {};
// 成員函數
void show();
};
template <class T1, class T2>
void People<T1,T2>::show()
{
cout << m_x << "," << m_y << endl;
}
// 類模板顯式具體化(針對 結構體 的顯式具體化)
typedef struct
{
string name;
int age;
} msg ;
template<>
class People<msg , msg>
{
private:
// 成員變量
msg m_leader;
msg m_young;
public:
// 構造函數
People(msg leader,msg young) :m_leader(leader), m_young(young){};
// 成員函數
void show();
};
// 注意!這里不能帶模板頭template<>
void People<msg, msg>::show()
{
// 判斷 年輕人 與 掌門人 年齡
msg stripling = this->m_leader.age > this->m_young.age ? m_leader : m_young;
// 輸出年齡大的老同志
cout << stripling.name << ":" << stripling.age << endl << "馬家功夫名不虛傳" << endl;
}
int main()
{
People<string, string> Sir("渾圓形意太極門掌門人","馬保國");
Sir.show();
msg young = { "八十公斤的年輕人",35 };
msg leader = { "馬保國",69 };
People<msg, msg> Man(leader,young);
Man.show();
return 0;
}
結果:
渾圓形意太極門掌門人,馬保國
馬保國:69
馬家功夫名不虛傳
部分顯式具體化
部分顯式具體化只能用于類模板,不能用于函數模板
template<class 模板類型>
class 類名<復雜數據類型 , 模板類型> // 模板類型 要 對應,位置不固定,根據情況而定
{
// 成員變量
// 成員函數
}
例如:
template <class T1, class T2>
class People {
// 成員變量 成員函數
}
// msg 為 結構體
template<class T>
class People<msg , T>
{
// 成員變量 成員函數
}
#include <iostream>
#include <string>
using namespace std;
template <class T1, class T2>
class People
{
private:
// 成員變量
T1 m_x;
T2 m_y;
public:
// 構造函數
People(T1 x, T2 y) :m_x(x), m_y(y) {};
// 成員函數
void show();
};
template <class T1, class T2>
void People<T1,T2>::show()
{
// 輸出數據
cout << m_x << "," << m_y << endl;
}
// 部分顯式具體化(針對 結構體 的部分顯式具體化)
typedef struct
{
string name;
int age;
}msg;
template<typename T>
class People<msg , T>
{
private:
// 成員變量
msg m_leader;
T m_sure_age;
public:
// 構造函數
People(msg leader,T sure_age) :m_leader(leader), m_sure_age(sure_age){};
// 成員函數
void show();
};
// 注意!需要帶模板頭
template<typename T>
void People<msg, T>::show()
{
// 修改數據,并輸出
this->m_leader.age = this->m_sure_age;
cout << this->m_leader.name << ":" << this->m_leader.age << endl << "馬家功夫名不虛傳" << endl;
}
int main()
{
People<string, string> Sir("渾圓形意太極門掌門人","馬保國");
Sir.show();
msg leader = { "馬保國",35 };
People<msg, int> Man(leader,69);
Man.show();
return 0;
}
結果:
渾圓形意太極門掌門人,馬保國
馬保國:69
馬家功夫名不虛傳
模板 用于多文件編程
在將函數用于多文件編程時,我們通常是將 函數定義 放在 源文件(.cpp 文件) 中,將 函數聲明 放在 頭文件(.h文件)中,使用函數時 引入(#include 命令) 對應的 頭文件 即可
編譯是針對單個源文件的,只要有函數聲明,編譯器就能知道函數調用是否正確;而將函數調用和函數定義對應起來的過程,可以延遲到鏈接時期。正是有了連接器的存在,函數聲明和函數定義的分離才得以實現。
模板并不是真正的函數或類,它僅僅是用來生成函數或類的一張 “圖紙”,在這個生成過程中有三點需要明確:
- 模板的實例化是按需進行的,用到哪個類型就生成針對哪個類型的函數或類,不會提前生成過多的代碼
- 模板的實例化是由編譯器完成的,而不是由鏈接器完成的
- 在實例化過程中需要知道模板的所有細節(jié),包含聲明和定義(只有一個聲明是不夠的,可能會在鏈接階段才發(fā)現錯誤)
頭文件:s.h ,存放類模板的定義
#ifndef s
#define s
template<typename T>
class Student
{
private:
T m_x;
T m_y;
public:
Student(T x, T y) :m_x(x), m_y(y) {};
T sum();
};
#endif
cpp文件,定義類的成員函數
#include <iostream>
#include <string>
#include "s.h"
using namespace std;
template<typename T>
T Student<T>::sum()
{
return this->m_x + this->m_y ;
}
main函數調用
#include "s.h"
#include <iostream>
#include <stdlib.h>
using namespace std;
int main()
{
Student<int> Sir(1, 3);
cout << Sir.sum() << endl;
system("pause");
return 0;
}
直接就報錯
嚴重性 代碼 說明 項目 文件 行 禁止顯示狀態(tài)
錯誤 LNK2019 無法解析的外部符號 "public: int __thiscall Student<int>::sum(void)" (?sum@?$Student@H@@QAEHXZ),該符號在函數 _main 中被引用 學習 E:\C++\學習\學習\學習.obj 1
錯誤 LNK1120 1 個無法解析的外部命令 學習 E:\C++\學習\Debug\學習.exe 1
但如果將 類的成員函數 定義放到 頭文件:s.h ,這些錯誤就沒有了
不能將模板的聲明和定義分散到多個文件中的根本原因是:模板的實例化是由編譯器完成的,而不是由鏈接器完成的,這可能會導致在鏈接期間找不到對應的模板實例。
參考:
由于參考內容過多,在這里就不一一列舉,如若有問題,請聯系我,會及時修改,添加?。?!