C++模板:template
1.引子
类的继承和包含并不总是能够满足重用代码的需要。
比如在一些类当中,仅有其中几个成员的类型发生变化,其他的部分都完全一样,因此我们需要为这样细小的不同而创建好几个这样的类。例如下面这段代码,两个test类当中的成员类型不同,因此可选的做法便是创建两个类。
class test1 { int m_member; }; class test2 { double m_member; };
这中做法会带来一个严重的问题:假如需求发生变化,这个变量类型再次发生改变,我们还需重新增加一个类。
C++的类模板(template)提供了一种更好的方法,模板提供参数化类型,能够将类型名作为参数传递到类当中,并以此来建立新的类或者函数。
template<typename T> class test1 { T m_member; };
2.函数模板
先介绍一下函数模板,函数也可以使用模板,格式方法和类模板一样。
举个例子,使用模板类来创建比较大小的函数max,再分别使用不同类型的输入。
template<typename T> const T& max(const T& a,const T& b) { return a > b ? a : b; } class Test { }; int main() { cout << max<int>(3, 2) << endl; //1.显式转化和隐式转化 cout << max('a', 'e') << endl; //相当于max<char> //cout << max<float>(3.314, 1.2) << endl; //2.输入类型为double,与模板函数不匹配 //Test t1, t2; //cout << max(t1, t2) << endl; // 3.Test类当中未重载<和>操作符,因此比较大小出错 //cout << max("aaa","zzz") << endl; //4.比较值不正确,aaa应当小于zzz,应输出zzz,实际输出aaa return 0; }
2.1.模板函数可以显式也可以隐式使用
可以不写类型,调用模板函数时,系统会自动地填入对应的类型进行转化。
2.2.函数参数和模板参数必须保持一致
程序默认的小数是double类型,调用模板函数时使用float就会导致类型不一致,程序无法编译通过。
当然,如果输入类型是可以转化的情况下,也不会报错。
max<int>('a', 3);
2.3.模板输入的类型,必须能够支持模板函数运行
例如Test类当中不存在比较大小的重载,因此如果对Test进行比大小的话,系统就不支持这种操作。解决方案只能是更改当前类当中的成员或函数,以支持模板函数的运行,例如:
class Test { public: friend bool operator>(const Test& t1,const Test& t2) { return true; } friend bool operator<(const Test& t1,const Test& t2) { return false; } };
需要注意的是,必须要以友元的方式重载<和>符号,否则const Test& a就无法调用比较符号,会提示无法找到const T的左值运算符。
2.5.模板的参数不应当是指针
传入指针到模板类当中,很容易导致无法对指针进行赋值,从而产生内存问题,通常都不建议使用指针,对于模板类也同样适用。
3.类模板
类当中包含有函数,因此类模板要稍微复杂一些,有以下几点需要注意
3.1.类模板参数在整个类当中都能使用
例如下面这个例子,生成一个简单的栈,类型T在类当中都可以使用。
3.3.模板嵌套
模板作为成员时可以进行嵌套,例如下面这个例子,创建一个栈,这个栈的每一个成员都是一个向量,向量本身包含int元素。可以使用typename A = B<T>的方式进行嵌套,同时在使用时也输入对应的模板参数。
template<typename T> class Stack { public: explicit Stack(int maxSize = 10); ~Stack(); void Push(const T& elem); void Pop(); const T& Top() const; bool isEmpty() const; private: T* elems_; int maxSize_; int top_; }; template<typename T> Stack<T>::Stack(int maxSize) :maxSize_(maxSize), top_(-1) { elems_ = new T[maxSize_]; } template<typename T> Stack<T>::~Stack() { delete[] elems_; } template<typename T> void Stack<T>::Push(const T& elem) { if (top_ + 1 >= maxSize_) throw out_of_range("Stack<>::Push() stack full"); elems_[++top_] = elem; } template<typename T> void Stack<T>::Pop() { if (top_ + 1 <= 0) throw out_of_range("Stack<>::Pop() stack empty"); --top_; } template<typename T> const T& Stack<T>::Top() const { if (top_ + 1 == 0) throw out_of_range("Stack<>::Top() stack empty"); return elems_[top_];; } template<typename T> bool Stack<T>::isEmpty() const { return top_ + 1 == 0; } int main() { Stack<int> s; for (int i = 0; i < 5; ++i) { s.Push(i + 1); } while (!s.isEmpty()) { cout << s.Top() << endl; s.Pop(); } return 0; }
3.2.类模板可以传递参数
同理,传入的模板参数也可以在整个模板类当中使用,例如在构造时,自动将大小传递给maxSize。
template<typename T,int SIZE> class Test{ public: explicit Stack(int maxSize = SIZE); ~Stack(); void Push(const T& elem); void Pop(); bool isEmpty() const; private: T* elems_; int maxSize_; int top_; };
3.3.模板嵌套
模板作为成员时可以进行嵌套,例如下面这个例子,创建一个栈,这个栈的每一个成员都是一个向量,向量本身包含int元素。可以使用typename A = B<T>的方式进行嵌套,同时在使用时也输入对应的模板参数。
template<typename T,typename CONT = deque<T> > class Stack{ public: Stack() : c_() { } ~Stack3() {} void Push(const T& elem) { c_.push_back(elem); } void Pop() { c_.pop_back(); } T& Top() { return c_.back(); } const T& Top() const { return c_.back(); } bool isEmpty() const { return c_.empty(); } private: CONT c_; }; Stack<int, vector<int> > s
3.4.成员模板
在类模板的使用当中,不是所有的时候只用类的模板就可以达成效果的,例如下面这个例子,对模板类进行类型转换,不允许直接将int类型的模板类转化为double类型的模板类。
template<typename T> class MmemberTemplate{ private: T value_; public: void Assign(const MmemberTemplate<T>& x) { value_ = x.GetValue(); } T GetValue() const { return value_; } }; int main(){ MmemberTemplate<double> d1; MmemberTemplate<int> i1; d1.Assign(d1); // ok //d1.Assign(i1); // 不允许不同类型的转换 return 0; }
此时,可以使用成员模板,即便是类的成员,其也可以是模板函数。利用成员模板的特性,就可以解决不同模板之间赋值转换的问题。
template<typename T> class MmemberTemplate{ private: T value_; public: template<typename X> void Assign(const MmemberTemplate<X>& x) { value_ = x.GetValue(); } T GetValue() const { return value_; } }; int main(){ MmemberTemplate<double> d1; MmemberTemplate<int> i1; d1.Assign(d1); // ok //d1.Assign(i1); // 不允许不同类型的转换 return 0; }