C++模板的注意事項
阿新 • • 發佈:2020-12-27
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;
}