北京大學MOOC C++學習筆記(七)函式模板和類模板
函式模板:
交換兩個整型變數的值的Swap函式: void Swap(int & x,int & y) { int tmp = x; x = y; y = tmp; } 交換兩個double型變數的值的Swap函式: void Swap(double & x,double & y) { double tmp = x; x = y; y = tmp; }
能否只寫一個Swap,就能交換各種型別的變數?
用函式模板解決: template <class 型別引數1,class 型別引數2,……> 返回值型別 模板名 (形參表) { 函式體 };
template <class T> void Swap(T & x,T & y) { T tmp = x; x = y; y = tmp; } int main() { int n = 1,m = 2; Swap(n,m); //編譯器自動生成 void Swap(int & ,int & )函式 double f = 1.2,g = 2.3; Swap(f,g); //編譯器自動生成 void Swap(double & ,double & )函式 return 0; }
函式模板中可以有不止一個型別引數。
template <class T1, class T2>
T2 print(T1 arg1, T2 arg2)
{
cout<< arg1 << " "<< arg2<<endl;
return arg2;
}
求陣列最大元素的MaxElement函式模板
template <class T> T MaxElement(T a[], int size) //size是陣列元素個數 { T tmpMax = a[0]; for( int i = 1;i < size;++i) if( tmpMax < a[i] ) tmpMax = a[i]; return tmpMax; }
不通過引數例項化函式模板
#include <iostream>
using namespace std;
template <class T>
T Inc(T n)
{
return 1 + n;
}
int main()
{
cout << Inc<double>(4)/2; //輸出 2.5
return 0;
}
函式模板可以過載,只要它們的形參表或型別引數表不同即可。
template<class T1, class T2>
void print(T1 arg1, T2 arg2) {
cout<< arg1 << " "<< arg2<<endl;
}
template<class T>
void print(T arg1, T arg2) {
cout<< arg1 << " "<< arg2<<endl;
}
template<class T,class T2>
void print(T arg1, T arg2) {
cout<< arg1 << " "<< arg2<<endl;
}
函式模板和函式的次序:
在有多個函式和函式模板名字相同的情況下,編譯器如下處理一條函式呼叫語句:
- 先找引數完全匹配的普通函式(非由模板例項化而得的函式)。
- 再找引數完全匹配的模板函式。
- 再找實引數經過自動型別轉換後能夠匹配的普通函式。
- 上面的都找不到,則報錯。
匹配模板函式時,不進行型別自動轉換
函式模板示例:Map
#include <iostream>
using namespace std;
template<class T,class Pred>
void Map(T s, T e, T x, Pred op)
{
for(; s != e; ++s,++x) {
*x = op(*s);
}
}
int Cube(int x) { return x * x * x; }
double Square(double x) { return x * x; }
int a[5] = {1,2,3,4,5}, b[5];
double d[5] = { 1.1,2.1,3.1,4.1,5.1} , c[5];
int main() {
Map(a,a+5,b,Square);
for(int i = 0;i < 5; ++i) cout << b[i] << ",";
cout << endl;
Map(a,a+5,b,Cube);
for(int i = 0;i < 5; ++i) cout << b[i] << ",";
cout << endl;
Map(d,d+5,c,Square);
for(int i = 0;i < 5; ++i) cout << c[i] << ",";
cout << endl;
return 0;
}
輸出: 1,4,9,16,25, 1,8,27,64,125, 1.21,4.41,9.61,16.81,26.01,
類模板
在定義類的時候,加上一個/多個型別引數。在使用類模板時,指定型別引數應該如何替換成具體型別,編譯器據此生成相應的模板類。
template <class 型別引數1,class 型別引數2,……> //型別引數表 class 類模板名 { 成員函式和成員變數 };
類模板裡成員函式的寫法: template <class 型別引數1,class 型別引數2,……> //型別引數表 返回值型別 類模板名<型別引數名列表>::成員函式名(引數表) { …… } 用類模板定義物件的寫法: 類模板名 <真實型別引數表> 物件名(建構函式實參表);
類模板示例: Pair類模板
template <class T1,class T2>
class Pair
{
public:
T1 key; //關鍵字
T2 value; //值
Pair(T1 k,T2 v):key(k),value(v) { };
bool operator < ( const Pair<T1,T2> & p) const;
};
template<class T1,class T2>
bool Pair<T1,T2>::operator < ( const Pair<T1,T2> & p) const//Pair的成員函式 operator <
{
return key < p.key;
}
int main()
{
Pair<string,int> student("Tom",19);//例項化出一個類 Pair<string,int>
cout << student.key << " " << student.value;
return 0;
}
輸出:Tom 19
編譯器由類模板生成類的過程叫類模板的例項化。由類模板例項化得到的類,叫模板類。
同一個類模板的兩個模板類是不相容的
Pair<string,int> * p; Pair<string,double> a; p = & a; //wrong
函式模版作為類模板成員:
#include <iostream>
using namespace std;
template <class T>
class A
{
public:
template<class T2>
void Func( T2 t) { cout << t; } //成員函式模板
};
int main()
{
A<int> a;
a.Func('K'); //成員函式模板 Func被例項化
a.Func("hello"); //成員函式模板 Func再次被例項化
return 0;
} //輸出:KHello
類模板的“<型別引數表>”中可以出現非型別引數:
template <class T, int size>
class CArray{
T array[size];
public:
void Print( )
{
for( int i = 0;i < size; ++i)
cout << array[i] << endl;
}
};
CArray<double,40> a2;
CArray<int,50> a3; //a2和a3屬於不同的類
類模板與派生
1 類模板從類模板派生
template <class T1,class T2>
class A {
T1 v1; T2 v2;
};
template <class T1,class T2>
class B:public A<T2,T1> {
T1 v3; T2 v4;
};
template <class T>
class C:public B<T,T> {
T v5;
};
int main() {
B<int,double> obj1;
C<int> obj2;
return 0;
}
2 類模板從模板類派生
template <class T1,class T2>
class A {
T1 v1; T2 v2;
};
template <class T>
class B:public A<int,double> {
T v;
};
int main() {
B<char> obj1; //自動生成兩個模板類 :A<int,double> 和 B<char>
return 0;
}
3 類模板從普通類派生
class A {
int v1;
};
template <class T>
class B:public A { //所有從B例項化得到的類 ,都以A為基類
T v;
};
int main() {
B<char> obj1;
return 0;
}
4 普通類從模板類派生
template <class T>
class A {
T v1;
int n;
};
class B:public A<int> {
double v;
};
int main() {
B obj1;
return 0;
}
類模板與友元
- 函式、類、類的成員函式作為類模板的友元
void Func1() { }
class A { };
class B
{
public:
void Func() { }
};
template <class T>
class Tmpl
{
friend void Func1();
friend class A;
friend void B::Func();
}; //任何從Tmp1例項化來的類 ,都有以上三個友元
- 函式模板作為類模板的友元
#include <iostream>
#include <string>
using namespace std;
template <class T1,class T2>
class Pair
{
private:
T1 key; //關鍵字
T2 value; //值
public:
Pair(T1 k,T2 v):key(k),value(v) { };
bool operator < ( const Pair<T1,T2> & p) const;
template <class T3,class T4>
friend ostream & operator<< ( ostream & o,const Pair<T3,T4> & p);
};
template<class T1,class T2>
bool Pair<T1,T2>::operator < ( const Pair<T1,T2> & p) const
{ //"小"的意思就是關鍵字小
return key < p.key;
}
template <class T1,class T2>
ostream & operator<< (ostream & o,const Pair<T1,T2> & p)
{
o << "(" << p.key << "," << p.value << ")" ;
return o;
}
int main()
{
Pair<string,int> student("Tom",29);
Pair<int,double> obj(12,3.14);
cout << student << " " << obj;
return 0;
}
輸出: (Tom,29) (12,3.14) 任意從 template <class T1,class T2> ostream & operator<< (ostream & o,const Pair<T1,T2> & p) 生成的函式,都是任意Pair摸板類的友元
- 函式模板作為類的友元
#include <iostream>
using namespace std;
class A
{
int v;
public:
A(int n):v(n) { }
template <class T>
friend void Print(const T & p);
};
template <class T>
void Print(const T & p)
{
cout << p.v;
}
int main() {
A a(4);
Print(a);
return 0;
}
輸出: 4 所有從 template <class T> void Print(const T & p) 生成的函式,都成為 A 的友元 但是自己寫的函式void Print(int a) { }不會成為A的友元
- 類模板作為類模板的友元
#include <iostream>
using namespace std;
template <class T>
class B {
T v;
public:
B(T n):v(n) { }
template <class T2>
friend class A;
};
template <class T>
class A {
public:
void Func( ) {
B<int> o(10);
cout << o.v << endl;
}
};
int main()
{
A< double > a;
a.Func ();
return 0;
}
輸出: 10 A< double>類,成了B<int>類的友元。任何從A模版例項化出來的類,都是任何B例項化出來的類的友元
類模板與靜態成員變數
類模板與static成員 • 類模板中可以定義靜態成員 ,那麼從該類模板例項化得到的所有類 ,都包含同樣的靜態成員 。
#include <iostream>
using namespace std;
template <class T>
class A
{
private:
static int count;
public:
A() { count ++; }
~A() { count -- ; };
A( A & ) { count ++ ; }
static void PrintCount() { cout << count << endl; }
};
template<> int A<int>::count = 0;
template<> int A<double>::count = 0;
int main()
{
A<int> ia;
A<double> da;
ia.PrintCount();
da.PrintCount();
return 0;
}
輸出: 1 1