C++之模板
2016-06-17 16:31
411 查看
模板主要应用于泛型编程,现在主要就是非常火的STL(标准模板库),对于公司项目来说利用模板的可能性不大,一般一个项目是无法达到模板这个要求的。
一、函数模板
函数模板的要求是:
1、函数名一致
2、参数个数一致
3、函数体一致
4、参数类型不同
函数模板的格式:
举个例子:
输出结果:
利用同一个函数模板可以应用于不同的类型,上面的每一次Swap的调用其实本质都是:
这个过程就是从函数模板转换成模板函数的一个过程, 左边是一种函数模板的隐式实例化,右边是函数模板的显示实例化。
1、函数模板与普通函数重载
对于这样的代码,编译器采用完全匹配的方式调用参数为int类型的函数
因为函数模板的编译需要进行两次编译:
第一次:不考虑类型编译;
第二次:匹配类型,生成可调用的二进制文件
在第一次编译的时候编译器就找到了完全匹配的函数,因为不会再进行第二次编译了。
所以当函数模板与普通函数重载时,优先调用完全匹配的普通函数。
2、函数模板与函数模板重载
这段代码会调用下面这个模板函数,
当模板函数与模板函数重载时,根据参数列表进行匹配。
二、结构体模板
输出结果:
三、类模板
STL中常用的就是类模板,比如vector,stack都是类模板,这里就拿stack为例:
类模板的格式:
若成员函数在类内部定义则与普通类的成员函数的定义一样, 若成员函数在类外部定义则要这样写,以下面stack的构造函数为例,在函数前不仅要加
域名要写成这种形式:
自实现的Stack类模板
输出结果:
对于这句代码,是个从类模板转换成模板类,再生成对象的过程。
如果写类模板,最好是先用一种简单的类型写一遍,再把类型替换掉就可以了。
再粘一个自实现的vector的例子,个人认为还是蛮典型的:
输出结果:
四、模板特化
输出结果:
五、模板默认参数
1、类模板默认参数
输出结果:
2、函数模板默认参数
输出结果:
3、类模板中的成员函数模板
输出结果:
总结:
1、模板不是一个完整的类型,使用到类型时要用到尖括号<>。
2、结构体模板和类模板必须使用显示实例化,也就是必须加<>,函数模板可以隐式实例化。
一、函数模板
函数模板的要求是:
1、函数名一致
2、参数个数一致
3、函数体一致
4、参数类型不同
函数模板的格式:
template<class/typename T1, class/typename T2, ...> 返回类型 函数名(参数列表){ 函数体 }
举个例子:
template<typename T> void Swap(T &a, T &b) { T t = a; a = b; b = t; } int main() { int a = 5; int b = 3; cout << "a = " << a << ", b = " << b << endl; Swap(a, b); cout << "a = " << a << ", b = " << b << endl; double c = 5.6; double d = 3.2; cout << "c = " << c << ", d = " << d << endl; Swap(c, d); cout << "c = " << c << ", d = " << d << endl; string e = "aaa"; string f = "bbb"; cout << "e = " << e << ", f = " << f << endl; Swap(e, f); cout << "e = " << e << ", f = " << f << endl; system("pause"); return 0; }
输出结果:
利用同一个函数模板可以应用于不同的类型,上面的每一次Swap的调用其实本质都是:
Swap(a, b); ------> Swap<int>(a, b); Swap(c, d); ------> Swap<double>(c, d); Swap(e, f); ------> Swap<string>(e, f);
这个过程就是从函数模板转换成模板函数的一个过程, 左边是一种函数模板的隐式实例化,右边是函数模板的显示实例化。
1、函数模板与普通函数重载
template<typename T> T getmax(T a, T b) { return a > b ? a : b; } int getmax(int a, int b) { return a > b ? a : b; } int main(){ cout << getmax(8,5) << endl; system("pause"); return 0; }
对于这样的代码,编译器采用完全匹配的方式调用参数为int类型的函数
int getmax(int a, int b) { return a > b ? a : b; }
因为函数模板的编译需要进行两次编译:
第一次:不考虑类型编译;
第二次:匹配类型,生成可调用的二进制文件
在第一次编译的时候编译器就找到了完全匹配的函数,因为不会再进行第二次编译了。
所以当函数模板与普通函数重载时,优先调用完全匹配的普通函数。
2、函数模板与函数模板重载
template<typename T> T* const& getmax(T* const& a, T* const& b) { return *a > *b ? *a : *b; } template<typename T> T const& getmax(T const& a, T const& b) { return a > b ? a : b; } int main(){ cout << getmax(5,8) << endl; system("pause"); return 0; }
这段代码会调用下面这个模板函数,
template<typename T> T const& getmax(T const& a, T const& b) { return a > b ? a : b; }
当模板函数与模板函数重载时,根据参数列表进行匹配。
二、结构体模板
template<typename T> struct student { T num; string name; int age; }; int main(){ student<int> stu1 = {1239234, "郭靖", 18}; cout << stu1.num << " " << stu1.name << " " << stu1.age << endl; student<string> stu2 = {"xuesheng2", "黄蓉", 16}; cout << stu2.num << " " << stu2.name << " " << stu2.age << endl; system("pause"); return 0; }
输出结果:
三、类模板
STL中常用的就是类模板,比如vector,stack都是类模板,这里就拿stack为例:
类模板的格式:
template<typename T1, typename T2, ...> class 类名{ 类内部实现; };
若成员函数在类内部定义则与普通类的成员函数的定义一样, 若成员函数在类外部定义则要这样写,以下面stack的构造函数为例,在函数前不仅要加
template<typename T>
域名要写成这种形式:
MyStack<T>::函数名
自实现的Stack类模板
template<typename T> MyStack<T>::MyStack(int s) { size = s; arr = new T[size]; top = 0; }
#include<iostream>
#include<string>
using namespace std;
template<typename T>
class MyStack {
public:
MyStack(int s = 1024);
~MyStack();
bool isEmpty();
bool isFull();
void push(T num);
T pop();
private:
T *arr;
int top;
int size;
};
template<typename T> MyStack<T>::MyStack(int s) { size = s; arr = new T[size]; top = 0; }
template<typename T>
MyStack<T>::~MyStack() {
delete[]arr;
}
template<typename T>
bool MyStack<T>::isEmpty() {
return top == 0;
}
template<typename T>
bool MyStack<T>::isFull() {
return top == size;
}
template<typename T>
void MyStack<T>::push(T num) {
arr[top++] = num;
}
template<typename T>
T MyStack<T>::pop() {
return arr[--top];
}
int main() {
MyStack<int> ms;
ms.push(20);
cout << ms.pop() << endl;
ms.push(30);
cout << ms.pop() << endl;
ms.push(10);
cout << ms.pop() << endl;
cout << "-------------------" << endl;
MyStack<string> mstr;
mstr.push("aaa");
cout << mstr.pop() << endl;
mstr.push("bbb");
cout << mstr.pop() << endl;
mstr.push("ccc");
cout << mstr.pop() << endl;
cout << "-------------------" << endl;
MyStack<double> msdou;
msdou.push(1.1);
cout << msdou.pop() << endl;
msdou.push(2.2);
cout << msdou.pop() << endl;
msdou.push(3.3);
cout << msdou.pop() << endl;
system("pause");
return 0;
}
输出结果:
MyStack<int> ms
对于这句代码,是个从类模板转换成模板类,再生成对象的过程。
如果写类模板,最好是先用一种简单的类型写一遍,再把类型替换掉就可以了。
再粘一个自实现的vector的例子,个人认为还是蛮典型的:
#include<iostream> using namespace std; template<typename T> class MyVector { public: MyVector(int s = 1024) { arr = new T[s]; size = 0; } MyVector(MyVector<T> &another) { int len = another.getSize(); arr = new T[len]; for (int i = 0; i < len; i++) { arr[i] = another.arr[i]; size++; } } MyVector<T>& operator=(MyVector<T> & another) { if (this == &another) return *this; delete[]arr; int len = another.getSize(); arr = new T[len]; for (int i = 0; i < len; i++) { arr[i] = another.arr[i]; size++; } return *this; } T& operator[](int i) { if (i > size){ cerr << i << "不存在" << endl; } return arr[i]; } ~MyVector() { delete []arr; } int getSize() { return size; } void push_back(T num) { arr[size++] = num; } T pop_back() { return arr[--size]; } friend ostream& operator<<(ostream& os, const MyVector& mv) { for (int i = 0; i < mv.size; i++) os << mv.arr[i] << " "; return os; } private: T *arr; int size; }; int main() { MyVector<int> mv; for(int i = 0; i < 10; i++) mv.push_back(i); cout << mv << endl; cout << "------------------------" << endl; MyVector<double> v2; for (int i = 0; i < 10; i++) v2.push_back(i + 0.1); cout << v2 << endl; cout << "------------------------" << endl; MyVector<char> v3; char c = 'a'; for(int i = 0; i < 10; i++){ v3.push_back(c++); } cout << v3 << endl; cout << "------------------------" << endl; system("pause"); return 0; }
输出结果:
四、模板特化
template<typename T1, typename T2> class Temp { public: void func(T1 t1, T2 t2) { cout << "class Temp " << endl; } }; //完全特化 template<> //这里不能漏掉 class Temp<int, double> { public: void func(int t1, double t2) { cout << "class Temp<int, double>" << endl; } }; //局部特化 template<typename T> class Temp<T, double>{ public: void func(T t1, double t2) { cout << "class Temp<T, double>" << endl; } }; template<typename T> class Temp<T, T> { public: void func(T t1, T t2) { cout << "class Temp<T, T>" << endl; } }; int main(){ Temp<int, int> temp1; temp1.func(1, 2); Temp<int, double> temp2; temp2.func(1, 2.0); Temp<float, double> temp3; temp3.func(1.3, 2.5); Temp<char, string> temp4; temp4.func('a', "sfafsa"); system("pause"); return 0; }
输出结果:
五、模板默认参数
1、类模板默认参数
template<typename T1 = int, typename T2 = double> class test { public: void func() { cout << typeid(t1).name() << " " << typeid(t2).name() << endl; } private: T1 t1; T2 t2; }; int main(){ test<> test1; //虽然使用了默认参数,但是<>不能少 test1.func(); test<float> test2; test2.func(); test<char, int> test3; test3.func(); system("pause"); return 0; }
输出结果:
2、函数模板默认参数
template<typename T = int> void foo(T t) { cout << typeid(t).name() << endl; } int main(){ foo("strsqa"); foo((float)12); system("pause"); return 0; }
输出结果:
3、类模板中的成员函数模板
template<typename T1 = int, typename T2 = double> class test { public: template<typename T = int> void func(T t) { cout << typeid(t1).name() << " " << typeid(t2).name() << " " << typeid(t).name() << endl; } private: T1 t1; T2 t2; }; int main(){ test<> test1; test1.func(1); test<float> test2; test2.func((float)1); test<char, int> test3; test3.func((double)1); system("pause"); return 0; }
输出结果:
总结:
1、模板不是一个完整的类型,使用到类型时要用到尖括号<>。
2、结构体模板和类模板必须使用显示实例化,也就是必须加<>,函数模板可以隐式实例化。