C++模板详解
參考:C++ 模板詳解(一)
?
模板:對類型進行參數化的工具;通常有兩種形式:?
- 函數模板:僅參數類型不同;
- 類模板: ??僅數據成員和成員函數類型不同。
目的:讓程序員編寫與類型無關的代碼。
注意:模板的聲明或定義只能在全局、命名空間、類范圍內進行。即不能在局部范圍、函數內進行,比如不能在main函數中聲明或定義一個模板。
?
一 函數模板
1 函數模板的格式:?
template <class 類型名1,class 類型名2,......>
返回類型 函數名(參數列表)
{函數體
} - template、class是關鍵字,class可以用關鍵字typename代替,在這里typename和class沒區別。
- <>中的參數叫模板形參,不能為空。模板形參用傳入的實參類型來初始化。
- 模板形參可以在 返回類型、參數列表、函數體內使用。一旦編譯器確定了模板實參類型,就稱他實例化了函數模板的一個實例。
?
例1:swap函數的模板:
template <class T>
void swap(T& a, T& b) //參數列表使用模板形參
{
T tmp = a; //函數體內使用模板形參
a = b;
b = tmp;
} - 當調用這個函數模板時,類型T就會被被調用時的類型所代替。
- 比如swap(a,b),其中a和b是int?型,模板函數就變為swap(int &a, int &b)。
- 而當swap(c,d),其中c和d是double型時,模板函數會被替換為swap(double &a, double &b)。
?
例2:max函數的模板?
1 #include<iostream>
2
3 template<typename T>
4 const T& myMax(const T &a, const T &b)
5 {
6 return a > b ? a : b;
7 }
8
9 int main()
10 {
11 cout << myMax(2.1, 2.2) << endl; //輸出2.2 模板實參被隱式推演成double
12 cout << myMax<double>(2.1, 2.2) << endl; //輸出2.2 顯示指定模板參數。
13 cout << myMax<int>(2.1, 2.2) << endl; //輸出2 顯示指定的模板參數,會將參數轉換為int。
14
15 return 0;
16 } ??
2、注意:
- 不存在swap(int, int)這樣的調用! 即不能用類型初始化,只能用實參推演來進行。即根據2來推出int型。
- 即只能進行swap(2, 3); ?或者 ?int a, b; swap(a,b); ? 這樣的調用。
?
二 類模板
1 類模板的格式為:
template<class 形參名1, class 形參名2, ...>
class 類名
{...
}; - 與函數模板一樣,以template開始,后接模板形參列表,模板形參不能為空。
- 類的數據成員和函數成員可以使用模板形參。
?
例:一個類模板的例子:
template<class T>
class A
{
public: T a; //類成員使用模板形參T b; T func(T c, T &d);
}; ?
2 類模板對象的創建:
- 方法:A<int> m; ?類A中用到模板形參的地方都會被int?所代替。
- 兩個模板形參:A<int, double> m; ?類型之間用逗號隔開。
3 類模板形參必須指定類型而不是實參:
- 必須這樣指定 A<int> m;??明確指定類型。
- 不能這樣使用A<2> m;??類模板形參不存在實參推演的問題。
4 在類模板外部定義成員函數的方法為:
template<模板形參列表>
函數返回類型 類名<模板形參名>::函數名(參數列表)
{函數體
} ?
例:比如模板類A,有兩個模板形參T1,T2,有一個成員函數 void func(),則外部定義該函數的語法為:
template<class T1, class T2> //與類一致
void A<T1, T2>::func() //類名也要加上模板參數列表
{
} 注意:當在類外面定義類的成員時,template后面的模板形參應與所定義的類的模板形參一致。
?
三 模板的形參
包括 類型形參、非類型形參、默認形參。
1 類型形參?
類型形參由關鍵字class或typename后接說明符構成,如
template<class T>
void func(T a)
{
}; - 其中?T?就是一個類型形參,名字由用戶確定。
?
函數模板,同一個類型形參,必須用相同類型的實參來初始化,比如
template<class T>
void func(T a, T b)
{
} - 調用?func(2, 3.2);?將編譯出錯,因為模板形參T同時被指定為int 和?double,不一致,會出錯。
?
類模板,其內部成員函數,則沒有上述的限制,比如?
template<class T> class A { public:T func(T a, T b); //或者T func(const T &a, const T &b); 普通引用會編譯報錯 };
- 聲明 A<int> a;? 調用?a.func(2, 3.2);??在編譯時不會出錯。
- 第二個實參3.2類型為double,在運行時,會強制類型轉換為3。
?
例:模板類的對象調用成員函數:?
1 #include <iostream>
2 using namespace std;
3
4 template<class T>
5 class A
6 {
7 public:
8 A();
9 T func(T a, T b);
10 };
11
12 template<class T> //類外定義構造函數
13 A<T>::A()
14 {
15 }
16
17 template<class T> //類外定義成員函數
18 T A<T>::func(T a, T b)
19 {
20 return a + b;
21 }
22
23 int main(int argc, char *argv[])
24 {
25 A<int> ia; //模板實參為int類型的對象
26 cout << ia.func(3, 2.1) << endl; //輸出5
27
28 A<double> da;
29 cout << da.func(3, 2.1) << endl; //輸出5.1
30
31 return 0;
32 } ??
2 非類型形參
也就是內置類型形參,如
template<class T, int a> //int a 就是非類型形參 class B { };
非類型形參有幾點要注意的:
- 在模板定義的內部是常量值,也就是說,上面的a在類B內部是一個常量。
- 形參只能是整型、指針、引用,像double、string、string **是不允許的,但是double &、double *、對象的引用或指針是正確的。
- 實參必須是一個常量表達式,即必須能在編譯時計算出結果。注意:局部對象/變量和其地址,全局指針/變量/對象,都不是常量表達式;全局變量/對象地址或引用,const類型變量,sizeof的結果,都是常量表達式。
- 形參是整型時,實參也必須是整型的,且在編譯期間是常量,比如
template <class T, int a>
class A
{
}; 如果有int b; ?這時?A<int, b> m;?將出錯,因為b不是常量;如果有?const int b; ?這時 A<int, b> m; ?正確,因為這時b是常量。
- 非類型形參一般不應用于函數模板中,比如有函數模板
template<class T, int a>
void func(T b)
{
} 若用func(2)調用,會出現無法為非類型形參a推演出參數的錯誤;可以用顯示模板實參來解決,如用func<int, 3>(2); 把非類型形參a設置為整數3。
- 形參和實參間所允許的轉換:?
//1 數組到指針,函數到指針的轉換
template<int *a>
class A { };
int b[10];
A<b> m; //數組轉換成指針 //2 const修飾符的轉換
template<const int *a>
class A { };
int b;
A<&b> m; //從int*轉換成const int *//3 提升轉換
template<int a>
class A { };
const short b = 2;
A<b> m; //short到int提升//4 整數轉換
template<unsigned int a>
class A { };
A<3> m; //int到unsigned int轉換//5 常規轉換 ??
例:由用戶指定棧的大小,并實現棧的相關操作。?
1 #include <iostream>
2 #include <string>
3 #include <stdexcept> //std::out_of_range
4 #include <cstdlib> //EXIT_FAILURE
5 using namespace std;
6
7 /*********模板類,聲明開始,一般都是放在頭文件的*********/
8
9 template<class T, int MAXSIZE>
10 class myStack
11 {
12 public:
13 myStack();
14 void push(T const &); //入棧
15 void pop(); //出棧
16 T top() const; //返回棧頂
17
18 bool empty() const //判斷是否為空
19 {
20 return size == 0;
21 }
22
23 bool full() const //判斷棧是否已滿
24 {
25 return size == MAXSIZE;
26 }
27
28 private:
29 T elems[MAXSIZE]; //使用數組存放棧元素,由于非類型形參MAXSIZE在類內是一個常量,所以可以用來聲明數組大小
30 int size; //棧已使用空間
31 };
32
33 /**********模板類,聲明結束,定義成員函數開始********/
34
35 template<class T, int MAXSIZE>
36 myStack<T, MAXSIZE>::myStack(): size(0) //構造函數,初始化為空
37 {
38 }
39
40 template<class T, int MAXSIZE>
41 void myStack<T, MAXSIZE>::push(T const &new_elem) //入棧
42 {
43 if(size == MAXSIZE)
44 {
45 throw out_of_range("myStack::push(): stack is full");
46 }
47 elems[size++] = new_elem;
48 }
49
50 template<class T, int MAXSIZE>
51 void myStack<T, MAXSIZE>::pop() //棧頂出棧
52 {
53 if(size <= 0)
54 {
55 throw out_of_range("myStack::pop(): stack is empty");
56 }
57 --size;
58 }
59
60 template<class T, int MAXSIZE>
61 T myStack<T, MAXSIZE>::top() const //返回棧頂元素
62 {
63 if(size <= 0)
64 {
65 throw out_of_range("myStack::top(): stack is empty");
66 }
67 return elems[size - 1];
68 }
69
70 /***********成員函數定義結束**********************/
71
72 int main(int argc, char *argv[])
73 {
74 try
75 {
76 myStack<int, 20> int20Stack; //顯示模板實參
77 myStack<int, 40> int40Stack;
78 myStack<string, 40> stringStack;
79
80 int20Stack.push(7);
81 cout << int20Stack.top() << endl; //輸出7
82 int20Stack.pop();
83
84 for(int i = 0; i < 40; ++i)
85 int40Stack.push(i);
86 cout << int40Stack.top() << endl; //輸出39
87 //int40Stack.push(41); //繼續添加元素,會拋出異常,輸出Exception: myStack::push(): stack is full
88
89 stringStack.push("hello");
90 cout << stringStack.top() << endl; //輸出hello
91 stringStack.pop();
92 //stringStack.pop(); //繼續出棧,會拋出異常,輸出Exception: myStack::push(): stack is empty
93
94 return 0;
95 }
96 catch(out_of_range const &ex)
97 {
98 cerr << "Exception: " << ex.what() << endl;
99 return EXIT_FAILURE;
100 }
101 } ??
3 默認形參
1?類模板可以有默認值,函數模板不能有默認值。
2 類模板,類型形參,默認值形式為:
template<class T1, class T2 = int> //為第二個模板類型形參提供int型的默認值
class A
{
}; 3 類模板,類型形參,默認值和普通函數的默認參數一樣,如果有多個類型形參,則從第一個設定了默認值的形參之后,所有模板形參都要設定默認值。
template<class T1 = int, class T2> //錯誤!如果T1有默認值,T2也必須有
class A
{
}; 4 類模板,類型形參,外部定義類的成員函數。template?后的形參列表應省略掉默認值。
template<class T1, class T2 = int>
class A
{
public: void func();
}; template<class T1,class T2> //定義方法,省略掉默認值
void A<T1,T2>::func()
{
} ?
例:有默認值的類模板
1 #include <iostream>
2 using namespace std;
3
4 template<typename T1, typename T2 = double, int abc = 5> //第二個類型形參和非類型形參有默認值
5 class A
6 {
7 public:
8 void print(T1 a, T2 b);
9 };
10
11 template<typename T1, typename T2, int abc> //類外定義時不能有默認值,畢竟類的聲明是作為接口給別人看的
12 void A<T1, T2, abc>::print(T1 a, T2 b)
13 {
14 cout << a << ' ' << b << endl;
15 cout << abc << endl;16 }
17
18 int main(int argc, char *argv[])
19 {
20 A<int> a;
21 a.print(2.2, 2.1); //輸出 2 2.1 5, 輸出2是因為指定為int型,進行了類型轉換,輸出5是默認值
22
23 return 0;
24 } ?
轉載于:https://www.cnblogs.com/qieerbushejinshikelou/p/3961964.html
總結
- 上一篇: GoPro9香港现在卖多少钱?
- 下一篇: bootstrap3中关于布局的两种样式