C++中的模板(上)
模板参数和函数参数是很像的,函数参数定义的形参对象,而模板参数定义的是类型.
模板分为函数模板和类模板
函数模板
一个交换两个数的函数模板:
template<class T> // 此处typename和class是等价的
void Swap(T &a, T &b)
{T temp = a;a = b;b = temp;
}
可以给这里的函数传递任何类型的参数.
任何类型的值都是可以传递的:
void Test1()
{int a = 1, b = 2;Swap(a, b);cout << a << " " << b << endl;double c = 1.1, d = 2.2;Swap(c, d);cout << c << " " << d << endl;
}
这里的ab和cd变量表面上调用的是同一个函数,实际上调用的函数是不同的,因为编译器会根据类型自动推断,如果推断出不同的类型就会在编译的时候生成不同的函数.
编译器这个生成函数的过程称之为模板的实例化.
用不同的参数实例化函数模板时,成为函数模板的实例化.模板参数实例化分为:隐式实例化和显式实例化.
函数模板可以显式的实例化也可以通过传递参数自动推出模板参数的类型,但是类模板都是显式实例化.
函数模板的显式实例化:
template<class T>
T Add(T a, T b)
{return a + b;
}
void Test2()
{cout<<Add(1,2)<<endl; // 隐式实例化,通过参数传递自动推出模板参数的类型cout<<Add<int>(1,2)<<endl; // 显式实例化.cout<<Add<double>(1.1,2)<<endl;
}
注意:
普通函数和模板函数是可以同时存在的.例如:
// 普通函数 int Add(int a,int b) {return a + b; } // 函数模板 template<class T> int Add(const T& a,const T& b) {return a + b; }
这样函数在调用的时候,会自动根据最佳类型进行匹配.
类模板
类模板的写法与函数模板类似的:
template<class T>
class Stack
{
public:Stack(int capacity = 3):_capacity(capacity){cout<<"Stack<T>::Stack(int capacity)"<<endl;_a = new T[_capacity];_top = 0;}~Stack();
private:T* _a;int _top;int _capacity;
};
-
对于没有使用模板语法的类的类名就是就是它的类型.例如以下的例子:
class A { private: int _a; public:} int main() {A a; }
A类就是一个没有使用任何类模板语法的普通类,a对象对应的类型就是A.
-
而对于使用了类模板的类来讲,类的类型不仅仅是类名,还需要包含类模板中的模板类型,例如:
template<class T> class Stack { public:Stack(int capacity = 3):_capacity(capacity){cout<<"Stack<T>::Stack(int capacity)"<<endl;_a = new T[_capacity];_top = 0;}~Stack(); private:T* _a;int _top;int _capacity; }; int main() {Stack<int> stack1;Stack<double> stack2;return 0; }
例如这个stack1对象的类型就是
Stack<int>
,这个stack2对象的类型就是Stack<double>
,而Stack<int>
和Stack<double>
是不同的类型.
模板类的声明与定义分离
模板类中方法的声明与普通类中方法的声明是一样的.
但是在方法的定义的前面要指定类的类型(注意:模板类的类型是类名<类型>),同时需要在方法定义的上面一排声明模板参数.例如要将Stack类的构造方法的声明与定义分离的做法:
template<class T>
class Stack
{
public:// 方法声明Stack(int capacity = 3);~Stack();void Print();
private:T* _a;int _top;int _capacity;
};// 方法定义
template<class T> // 声明模板参数
Stack<T>::Stack(int capacity )
:_capacity(capacity)
{ cout<<"Stack<T>::Stack(int capacity)"<<endl;_a = new T[_capacity];_top = 0;
}
// 方法定义
template<class T>
Stack<T>::~Stack()
{delete[] _a;_a = nullptr;_top = 0;_capacity = 0;cout<<"Stack<T>::~Stack()"<<endl;
}
// 方法定义
template<class T>
void Stack<T>::Print()
{cout<<"top = "<<_top<<endl;
}
对于普通类而言,类中方法的声明与定义是可以在不同的文件中的.
但是对于模板类,方法的声明与定义是不支持在不同文件中的.不能像普通类那样将类的声明放到头文件中,将方法的定义放在源文件中.