1. 程式人生 > 其它 >C++模板的注意事項

C++模板的注意事項

技術標籤:C++基礎c++

1 函式模板

template <typename T>        //把typename換成class也可以

使用過程中, 有兩種方法呼叫, 如下:

  • 自動型別推導:
#include <iostream>

template <class T>
void swap(T &first, T &second) {
    T temp;
    temp = first;
    first = second;
    second = temp;
}

int main() {
    int a = 10;
    int b = 20;
    swap(a, b);
    return 0;
}
  • 顯示指定型別
#include <iostream>

template <class T>
void swap(T &first, T &second) {
    T temp;
    temp = first;
    first = second;
    second = temp;
}

int main() {
    int a = 10;
    int b = 20;
    swap<int>(a, b);
    return 0;
}

注意, 兩者的區別在於呼叫時是否傳入模板型別.

1.1 函式模板呼叫規則

  • 普通函式呼叫時可以發生隱式型別轉換
  • 函式模板, 用自動型別推導, 不能發生隱式型別轉換
  • 函式模板, 用顯示指定型別, 可以發生隱式型別轉換

1.2 普通函式和函式模板呼叫順序規則

假設有以下例子:

#include <iostream>
using namespace std;

void mSwap(int &first, int &second) {
    cout << "call normal function" << endl;
    int temp;
    temp = first;
    first = second;
    second = temp;
}

template <class T>
void mSwap(T &first, T &second) {
    cout << "call template function" << endl;
    T temp;
    temp = first;
    first = second;
    second = temp;
}
  • 如果函式模板和普通函式都可以呼叫, 優先呼叫普通函式

示例程式碼:

int main() {
    int a = 10;
    int b = 20;
    mSwap(a, b);
    return 0;
}

結果:

  • 可以通過空模板引數列表強制呼叫函式模板

示例:

int main() {
    int a = 10;
    int b = 20;
    mSwap<>(a, b);
    return 0;
}

結果:

  • 函式模板可以發生函式過載

比如又加了一個函式:

template <class T>
void mSwap(T &first, T &second) {
    cout << "call template function" << endl;
    T temp;
    temp = first;
    first = second;
    second = temp;
}

template <class T>
void mSwap(T &first, T &second, T &third) {
    cout << "call template function" << endl;
    T temp;
    temp = first;
    first = second;
    second = temp;
}

現在就有兩個同名函式模板了, 這樣傳入三個引數就會呼叫下面的.

  • 如果函式模板可以產生更好的匹配(不需要發生隱式型別轉換), 優先呼叫函式模板

比如有如下呼叫:

int main() {
    char a = 'a';
    char b = 'b';
    mSwap(a, b);
    return 0;
}

執行結果為:

1.3 模板侷限性

比如有如下程式碼:

class People {
public:
    People(string name, int age) : m_name(name), m_age(age) {}
    string m_name;
    int m_age;
};

template <class T>
bool mCompare(const T &a, const T &b) {
    if (a == b)
        return true;
    else
        return false;
}

當有如下呼叫時:

int main() {
    People p1("michael", 18);
    People p2("michael", 18);
    mCompare(p1, p2);
    return 0;
}

會發生如下編譯錯誤:

出現以上情況我們當然可以在類中實現" == "的運算子過載, 也可以用如下的方法, 在以上程式碼的基礎上增加以下程式碼:

template<> bool mCompare(const People &a, const People &b) {
    if (a.m_name == b.m_name &&
        a.m_age  == b.m_age)
        return true;
    else
        return false;
}

這樣即對以上的函式模板進行people定製化過載.

2 類模板

在類的宣告按照如下宣告:

template <class nameType, class ageType>
class People {
public:
    People(nameType name, ageType age) : m_name(name), m_age(age) {}
    nameType m_name;
    ageType m_age;
};

這樣使用的時候按照如下使用:

int main() {
    People<string, int> p1("michael", 18);
    return 0;
}

2.1 類模板中成員函式建立時機

類模板中成員函式在呼叫時才建立.

如下測試程式碼:

class People1 {
public:
    void showPerple1(){
        cout << "this is class People1" << endl;
    }
};

class People2 {
public:
    void showPerple2(){
        cout << "this is class People2" << endl;
    }
};

template <class T>
class mClass {
public:
    void show1() {
        m_obj.showPerple1();
    }
    void show2() {
        m_obj.showPerple2();
    }
    T m_obj;
};

如上程式碼是可以編譯通過的.

如下測試程式碼:

int main() {
    mClass <People1> p1;
    mClass <People2> p2;
    p1.show1();
    p2.show2();
    return 0;
}

編譯時候確定了T的型別之後, 如果發現T中沒有成員函式中的函式, 就會報錯.

2.2 類模板的物件作函式引數

假設有如下模板類:

template <class nameType, class ageType>
class People {
public:
    People(const nameType &name, const ageType &age) : m_name(name), m_age(age) {}
    void show() {
        cout << "name: " << m_name << endl;
        cout << "age: " << m_age << endl;
    }
    nameType m_name;
    ageType  m_age;
};

如果想要呼叫People例項化物件的show函式, 可以有以下方法:

  • 指定傳入型別

方法如下:

void showPeople1(People<string, int> &p) {
    p.show();
}

int main() {
    People<string, int> p1("michael", 18);
    showPeople1(p1);
    return 0;
}
  • 引數模板化

方法如下:

template <class nameType, class ageType>
void showPeople2(People<nameType, ageType> &p) {
    p.show();
}

int main() {
    People<string, int> p2("michael", 18);
    showPeople1(p2);
    return 0;
}
  • 整個類模板化

方法如下:

template <class P>
void showPeople3(P &p) {
    p.show();
}

int main() {
    People<string, int> p3("michael", 18);
    showPeople3(p3);
    return 0;
}

2.3 類模板繼承

  • 當父類是模板類時, 子類在繼承的時候需要指定父類中的模板型別, 當有如下程式碼:
template <class T>
class Father {
public:
    T m_T;
};

class Son : public Father {
public:
};

這樣編譯會報錯, 需要改為:

template <class T>
class Father {
public:
    T m_T;
};

class Son : public Father<int> {
public:
};

這樣就可以了

  • 也可以把子類也變成模板類, 可以按照如下操作:
template <class T>
class Father {
public:
    T m_T;
};

template <class T1, class T2>
class Son : public Father<T2> {
public:
    T1 m_T1;
};

2.4 類外實現

當有如下類:

template <class nameType, class ageType>
class People {
public:
    People(nameType name, ageType age);
    void showPeople();
    nameType m_name;
    ageType m_age;
};

當我們在類外實現其建構函式和成員函式時, 需要按照如下方式:

//constructor
template <class nameType, class ageType>
People<nameType, ageType>::People(nameType name, ageType age) {
    m_name = name;
    m_age = age;
}

//member function
template <class nameType, class ageType>
void People<nameType, ageType>::showPeople() {
    cout << "name: " << m_name << endl;
    cout << "age: " << m_age << endl;
}

3 函式模板和類模板的區別

  • 類模板是不存在型別推導的, 所以例項化物件的時候必須傳入型別.
  • 類模板是可以有預設引數的

可參考如下程式碼:

template <class nameType, class ageType = int>
class People {
public:
    People(nameType name, ageType age) : m_name(name), m_age(age) {}
    nameType m_name;
    ageType m_age;
};

int main() {
    People<string> p1("michael", 18);
    return 0;
}

這樣是可以的.

如果兩個引數都有預設型別, 例項化物件的時候也必須要有尖括號, 如下:

template <class nameType = string, class ageType = int>
class People {
public:
    People(nameType name, ageType age) : m_name(name), m_age(age) {}
    nameType m_name;
    ageType m_age;
};

int main() {
    People <>p1("michael", 18);
    return 0;
}