1. 程式人生 > >北京大學MOOC C++學習筆記(七)函式模板和類模板

北京大學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;
}

函式模板和函式的次序:

在有多個函式和函式模板名字相同的情況下,編譯器如下處理一條函式呼叫語句:

  1. 先找引數完全匹配的普通函式(非由模板例項化而得的函式)。
  2. 再找引數完全匹配的模板函式。
  3.  再找實引數經過自動型別轉換後能夠匹配的普通函式。
  4. 上面的都找不到,則報錯。

匹配模板函式時,不進行型別自動轉換

函式模板示例: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