C++ 类模板
一、定义类模板
类模板和函数模板类似,只是提供如何生成类和成员函数的模板,并不是定义类和成员函数
1、类模板声明格式
template <typename 类型参数> //typename可用class替换
class 类名
{
类成员声明
};
2、类模板定义对象的格式
类模板名<真实类型参数表> 对象名(构造函数实际参数表);
如果类模板有无参构造函数,那么也可以使用如下写法:
类模板名<真实类型参数表> 对象名;
示例:
#include <iostream>
using namespace std;
//类模板
template <typename T>
class Add
{
private:
T m_a;
T m_b;
public:
Add(T a, T b)
{
m_a = a;
m_b = b;
}
T Sum()
{
return (m_a + m_b);
}
};
int main()
{
Add<int> a(1, 2); //模板类
int sum = a.Sum();
cout << "sum = " << sum << endl;
return 0;
}
运行结果:
sum = 3
3、类模板定义模板成员函数的格式
1)、在类模板声明中定义
与普通类的成员函数的定义方法一致。这种情况下属于内联定义
template <typename T>
class Add
{
private:
T m_a;
T m_b;
public:
Add(T a, T b)
{
m_a = a;
m_b = b;
}
T Sum() //类中定义
{
return (m_a + m_b);
}
};
2)、在类模板外定义
需要加模板前缀和类限定符,不需要类名
template <typename T>
class Add
{
private:
T m_a;
T m_b;
public:
Add(T a, T b)
{
m_a = a;
m_b = b;
}
T Sum();
};
template <typename T> //类外定义需要加模板前缀和类限定符
T Add<T>::Sum()
{
return (m_a + m_b);
}
4、类模板、模板类与对象的关系
二、类模板的派生
可以从类模板派生出新的类,既可以派生类模板,也可以派生非模板类
1、从类模板派生出新的类模板
格式:
template <class T>
class Base
{
//TODO
};
template <class T>
class Derive : public Base<T>
{
//TODO
};
与一般的类派生定义相似,只是在指出它的基类时要缀上模板参数,即Base< T >
2、从类模板派生非模板类
可以从类模板派生出非模板类,在派生中,作为非模板类的基类,必须是类模板实例化后的模板类,并且在定义派生类前不需要模板声明语句:template< class T>。
template <class T>
class Base
{
//TODO
};
class Derive : public Base<int>
{
//TODO
};
在定义Derive类时,Base已实例化成了int型的模板类。
三、类模板的多功能性
1、递归使用模板
template <typename T, int n> //声明Array类模板
class Array
{
private:
//TODO
public:
//TODO
};
Array< Array<int, 5>, 10> arr;
这里arr是包含10个元素的数组,其中每个元素都是一个包含5个int元素的数组。类似于int [10][5];
2、模板可以包含多个类型参数
template <typename T1, typename T2>
class Array
{
private:
//TODO
public:
//TODO
};
3、模板可以有默认类型参数
template <typename T1, typename T2 = int>
class Test
{
private:
//TODO
public:
//TODO
};
Test<double> t2; //T1是double类型,T2是int类型
Test<double, double> t1; //T1和T2都是double类型的,覆盖默认参数
4、成员模板
模板可用作结构、类或模板类的成员
5、模板可用作参数
模板可以包含类型参数(typename T)和非类型参数(如int),还可以包含本身就是模板的参数
四、模板具体化
模板的隐式实例化、显式实例化和显式具体化统称为具体化
1、隐式实例化
template <typename T, int n> //声明Array类模板
class Array
{
private:
//TODO
public:
//TODO
};
Array<double, 10> a; //隐式实例化
编译器在需要对象之前,不会生成类的隐式实例化
Array<double, 10> *P; //不需要对象,不会实例化
p = new Array<double, 10>; //需要对象,此时实例化
示例:
#include <iostream>
using namespace std;
template <typename T>
class A
{
private:
T m_a;
public:
A(T a)
{
m_a = a;
}
void print()
{
cout << "m_a = " << m_a << endl;
}
};
int main()
{
A<int> a(10); //隐式实例化
a.print();
}
运行结果:
m_a = 10
2、显式实例化
显式实例化和隐式实例化一样,也是根据通用模板来生成具体化。
显式实例化的声明必须位于模板定义所在的名称空间中。
显式实例化虽然没有创建对象,但是编译器会生成类声明,包括方法定义
格式:
template class 类模板名<实际类型列表>;
#include <iostream>
using namespace std;
template <typename T>
class A
{
private:
T m_a;
public:
A(T a)
{
m_a = a;
}
void print()
{
cout << "m_a = " << m_a << endl;
}
};
//显式实例化
template class A<int>;
int main()
{
A<int> i(4);
i.print();
}
结果:
m_a = 4
3、显式具体化
显式具体化是用于替换模板中泛型的特定类型,是对模板进行修改的特殊实例化
格式:
template class <> className(specialized-type-name) {...};
示例:
#include <iostream>
using namespace std;
template <typename T>
class A
{
private:
T m_a;
public:
A(T a)
{
m_a = a;
}
void print()
{
cout << "m_a = " << m_a << endl;
}
};
//显式具体化
template <>
class A<int>
{
private:
int m_a;
public:
A(int a)
{
m_a = a;
}
void print()
{
cout << "m_a*m_a = " << m_a * m_a << endl;
}
};
int main()
{
A<float> f(10.1); //隐式实例化
f.print();
A<int> i(4); //使用具体化模板
i.print();
}
运行结果:
m_a = 10.1
m_a*m_a = 16
4、部分具体化
1)、格式
template <class T1, class T2> //类模板
class className
{
//TODO
}
template <class T1> //部分具体化
class className<T1, int>
{
//TODO
}
2)、注意事项
1、当类模板参数只有一个参数时,部分具体化其实就是显示具体化
2、可以将所有的参数都具体化,此时template后面的<>为空。template <> class A<int, int> {…}
3、当给定多个模板时,编译器选择使用具体化程度最高的那个。例如:
template <class T1, class T2> class A {...}; //模板1
template <class T1> class A<T1, int> {...}; //模板2
template <> class A<int, int> {...}; //模板3
A<double, double> p1; //使用模板1
A<double, int> p1; //使用模板2
A<int, int> p1; //使用模板3
示例
#include <iostream>
using namespace std;
template <typename T1, typename T2>
class A
{
private:
T1 m_a;
T2 m_b;
public:
A(T1 a, T2 b)
{
m_a = a;
m_b = b;
}
void print()
{
cout << "m_a*m_b = " << m_a * m_b << endl;
}
};
template <class T1>
class A<T1, int>
{
private:
T1 m_a;
int m_b;
public:
A(T1 a, int b)
{
m_a = a;
m_b = b;
}
void print()
{
cout << "m_a*m_b = " << m_a * m_b << endl;
}
};
int main()
{
A<float, int> obj(1.2, 4);
obj.print();
}
结果
m_a*m_b = 4.8
5、显式实例化和显式具体化的区别
显式实例化只是显式声明模板需要示例化为哪种类型,同隐式实例化一样依然是根据类模板进行实例化;显式具体化不是根据类模板进行实例化的,是类模板的修改版本
五、类模板的注意事项
类模板的声明和定义必须在同一个文件中,即不能将声明放在头文件中,而实现放在源文件中
参考:
1、《C++ Primer Plus》
2、类模板