形式:template
例如:
template
class CMath
{
public:A m_a;B func() { ... };
};
如果在类模板外实现成员函数
template
返回值类型 类模板名<类型形参1, ...>::函数名(调用形参1, ...)
{函数体实现;
}
例如:
template
B Cmath::func()
{....;
}
实例:
#include
using namespace std;
//类模板
template
class CMath
{
public:CMath(T const& t1, T const& t2) : m_t1(t1), m_t2(t2) {}// T add();//声明实现写一起T add() {return m_t1 + m_t2;}
private:T m_t1;T m_t2;
};
//声明实现分开写
// template
// T CMath::add()
// {
// return m_t1 + m_t2;
// }int main()
{return 0;
}
使用类模板必须对类模板进行实例化(产生真正的类)
类模板本身并不代表一个确定的类型(即不能用与定义对象),只有通过类型实参实例化成真正的类后才具备类的语义(即可以定义对象)。
例如:CMath
#include
using namespace std;
//类模板
template
class CMath
{
public:CMath(T const& t1, T const& t2) : m_t1(t1), m_t2(t2) {}// T add();//声明实现写一起T add() {return m_t1 + m_t2;}
private:T m_t1;T m_t2;
};
//声明实现分开写
// template
// T CMath::add()
// {
// return m_t1 + m_t2;
// }int main()
{int nx = 10, ny = 20;CMath m1(nx, ny);cout << m1.add() << endl;double dx = 12.3, dy = 45.6;CMath m2(dx, dy);cout << m2.add() << endl;string sx = "hello", sy = " world";CMath m3(sx, sy);cout << m3.add() << endl;return 0;
}
类模板被实例化时类模板中的成员函数并没有实例化,成员函数只有在被调用时才会被实例化(即产生真正成员函数)注意:成员虚函数除外。
某些类型虽然并没有提供类模板所需要的全部功能但照样可以实例化类模板,只要不调用那些未提供功能的成员函数即可。
#include
using namespace std;template
class A
{
public:static void print() {cout << "&m_i:" << &m_i;cout << ", &m_t:" << &m_t << endl;}static int m_i;static T m_t;
};
templateint A::m_i; // = 0;
templateT A::m_t; //= ??int main()
{A x, y, z;x.print();y.print();z.print();A::print();cout << endl;A m, n, t;m.print();n.print();t.print();A::print();return 0;
}
#include
using namespace std;template
class Array
{
public:T& operator[](size_t i) {return m_arr[i];}
private:T m_arr[10];
};int main()
{Array< Array > m;for (int i = 0; i < 10; i++) {for (int j = 0; j < 10; j++) {m[i][j] = i + j;}}for (int i = 0; i < 10; i++) {for (int j = 0; j < 10; j++) {cout << m[i][j] << '\t';}cout << endl;}return 0;
}
全类特化:特化一个类模板可以特化该类模板所有的成员函数,相当于重新写了一个针对某种特定数据类型的具体类
声明形式:template<>class 类模板名<类型参数1, ...> {...}
例如:template<>class CMath
成员特化:类模板特化除了可以对整个类进行特化以外,可以只针对某部分成员函数进行特化。
声明形式:template<>返回值类型 类模板名<类型参数1, ...>::成员函数名(调用参数1, ...) {...}
例如:template<> char* const CMath
#include
#include
using namespace std;template
class CMath
{
public:CMath(const T& t1, const T& t2) : m_t1(t1), m_t2(t2) {}T add() {return m_t1 + m_t2;}
private:T m_t1;T m_t2;
};//成员特化
template<>
char* const CMath::add() {return strcat(m_t1, m_t2);
}/*
//全类特化
template<>
class CMath
{
public:CMath(char* const& t1, char* const& t2) : m_t1(t1), m_t2(t2) {}char* const add() {return strcat(m_t1, m_t2);}
private:char* const m_t1;char* const m_t2;
};
*/int main()
{char cx[256] = "hello", cy[256] = " world";CMath m(cx, cy);cout << m.add() << endl;return 0;
}
何时使用:当一个模板类中的某些成员函数中的操作针对某些数据类型无法执行时,使用全局特化来特殊处理这种数据类型实例化出来的具体类。其中全局特化的工作量较大,成员特化可以只针对那些无法执行的成员函数进行重写。
#include
using namespace std;templateclass CMath {
public:static void foo() {cout << "1:CMath::foo" << endl;}
};//局部特化1
templateclass CMath {
public:static void foo() {cout << "2:CMath::foo" << endl;}
};//局部特化2
templateclass CMath {
public:static void foo() {cout << "3:CMath::foo" << endl;}
};//局部特化3
templateclass CMath {
public:static void foo() {cout << "4:CMath::foo" << endl;}
};int main()
{CMath::foo(); //1CMath::foo(); //2//CMath::foo(); //匹配歧义CMath::foo(); //1CMath::foo(); //匹配歧义return 0;
}
类模板的类型形参可以带缺省值。
实例化类模板时,如果提供了类型实参则用所提供的类型实参来实例化类模板,如果没有提供类型实参则用相应的类型形参的缺省类型来实例化类模板。
如果类模板的某个类型形参带有缺省值,那么它后面的类型形参都必须带缺省值。
#include
#include
using namespace std;templateclass CMath {
public:void print() {cout << "m_t:" << typeid(m_t).name() << ", "<< "m_d:" << typeid(m_d).name() << endl;}
private:T m_t;D m_d;
};int main()
{CMath m;m.print();CMath<> m2;m2.print();return 0;
}
#include
using namespace std;templateclass Array {
public:T& operator[](size_t i) {return m_arr[i];}size_t size() {return S;}
private:T m_arr[S];
};int main()
{Array a;for (int i = 0; i < a.size(); i++) {a[i] = i + 1;}for (int i = 0; i < a.size(); i++) {cout << a[i] << " ";}cout << endl;return 0;
}
成员变量,但其类型是由一个类模板实例化的未知类,那么它才可以称之为模板型成员变量
例如:
templateclass Array {...};
templateclass Sum {
public:Array m_s; //模板型成员变量
};
#include
using namespace std;templateclass Array {
public:T& operator[](size_t i) {return m_arr[i];}
private:T m_arr[10];
};
templateclass Sum { //求和器
public:Sum(Array& s) : m_s(s) {}D add() {D d = 0;for (int i = 0; i < 10; i++) {d += m_s[i];}return d;}
private:Array m_s; //模板型成员变量
};int main()
{Array a;for (int i = 0; i < 10; i++) {a[i] = i + 1;}Sum s(a);cout << s.add() << endl;return 0;
}
类模板的成员函数模板
例如:
templateclass CMath {
public:templatevoid foo() {...} //成员函数模板
};
如果在类外实现:
template
#include
using namespace std;templateclass CMath {
public:templatevoid foo();// templatevoid foo() { //成员 函数模板// cout << "CMath::foo()" << endl;// }
};template
templatevoid CMath::foo()
{cout << "CMath::foo()" << endl;
}int main()
{CMath m;m.foo();return 0;
}
类模板中嵌套的类模板
例如:
templateclass A {
public:templateclass B {...}; //模板型成员类型
};
#include
using namespace std;templateclass A {
public:templateclass B {public:templateclass C;};
};template
template
templateclass A::B::C {
public:templatevoid foo() {cout << "foo()" << endl;}
};int main()
{A::B::C c;c.foo();return 0;
}
类模板的模板形参也可以是类模板,可以有缺省值
例如:
templateclass Array {...};
template< templateclass C=Array >class Sum {...
};
#include
using namespace std;
templateclass Array {
public:T& operator[](size_t i) {return m_arr[i];}
private:T m_arr[10];
};
templateclass C>class Sum {
public:Sum(C& s) : m_s(s) {}D add() {D d = 0;for (int i = 0; i < 10; i++) {d += m_s[i];}return d;}
private:C m_s;
};int main()
{Array a;for (int i = 0; i < 10; i++) {a[i] = i + 1;}Sum s(a);cout << s.add() << endl;return 0;
}
问题:
由于模板要经过两次编译,在第一次编译模板的代码时,类型形参的具体类型尚不明确,编译器将把类型形参的嵌套类型理解为某个未知类型的静态成员变量,因此编译器看到使用这样的标识符声明变量时会报告错误,这就叫嵌套依赖。
解决方法:
在类型形参的前面增加一个typename标识符,意在告诉编译器其后时一个类模板的嵌套使用。
#include
using namespace std;
class A {
public:class B {public:void foo() {cout << "A::B::foo()" << endl;}};
};
templatevoid Func() {typename T::B b; //嵌套依赖b.foo();
}int main()
{Func();return 0;
}
问题:
利用未知类定义的对象来访问成员函数模板时,编译器在第一次编译时无法解析成员函数模板的类型参数列表的 <> 而报告编译错误。
解决方法:
在成员函数模板之前增加template关键字,意在告诉编译器其后是一个函数模板实例,编译器就可以正确理解 <> 了。
#include
using namespace std;
class A {
public:templatevoid foo() {cout << "A::foo()" << endl;}
};
templatevoid Func() {D d;d.template foo(); //依赖模板参数访问成员函数模板
}int main()
{Func();return 0;
}
问题:
在子类模板中访问基类模板的成员,编译器第一次编译时只在子类模板和全局域中搜索使用的标识符号,不会到基类模板中搜索。
解决方法:
在子类模板中可以通过使用作用域限定符或显示使用 this
指针。
#include
using namespace std;
templateclass Base {
public:int m_i;void foo() {cout << "Base::foo()" << endl;}
};templateclass Derived : public Base {
public:void bar() {Base::m_i = 100;Base::foo();// this->m_i = 100;// this->foo();}
};int main()
{Derived d;d.foo();return 0;
}
问题:
基本类型不存在缺省构造函数,未被初始化的局部变量都具有一个不确定的值(int a;//值不确定)
类类型由于存在缺省构造函数,在未被初始化的情况下可以有一个确定的缺省初始化状态。(Integer a;//值确定)
基于以上两点,就会在模板实现中产生不一致的语法语义。
解决方法:
如果希望模板中,所有类型参数的变量,无论是类类型还是基本类型都以缺省方式获得初始化,就必须对其进行显示缺省构造 T()
。
#include
using namespace std;class Integer {
public:Integer() : m_i(0) {}
private:int m_i;friend ostream& operator<<(ostream& os, const Integer& that);
};
ostream& operator<<(ostream& os, const Integer& that) {return os << that.m_i;
}templatevoid Func() {T t = T(); //Integer() / int()cout << "t=" << t << endl;
}int main()
{Func();Func();return 0;
}
类模板中的普通成员函数可以是虚函数
即可以为类定义成员虚函数,和普通类的成员虚函数一样,类模板的成员虚函数也可以表现出多态性。
类模板中的成员函数模板不可以是虚函数
根据成员虚函数的多态机制,需要一个虚函数表(表中保存成员虚函数的入口地址),而这个表是编译器在实例化类模板时就产生,类的成员函数模板的实例化(即产生真正的函数实体)需要编译器处理完调用后才会完成,这是才出现成员虚函数的地址。
总结:
成员函数模板的延迟编译阻碍了虚函数表的静态构建。
#include
using namespace std;templateclass Base {
public:virtual void foo() {cout << "Base::foo()" << endl;}templatevoid bar() {}
};templateclass Derived : public Base {
public:virtual void foo() {cout << "Derived::foo()" << endl;}
};int main()
{Derived d;Base* pBase = &d;pBase->foo();pBase->bar();return 0;
}