函数模板知识点看这篇文章:
函数模板知识点总结
文章目录
1.类模板
1.1 类模板的声明
template<class 类型形参1, .....> class 类模板名{}
//举例
//CMath为类模板名
template<class A, class B>class CMath{
public:
A m_a;
B func(){...};
}
1.2 类模板的使用
使用类模板补习对类模板进行实例化,即,产生真正的类。类模板不能直接代表一个确定的类型,只有通过类型实参实例化成真正的类以后才具备真正类的功能。
- 类模板被实例化的时候,类模板中的成员函数并没有实例化,成员函数只有在被调用的时候才会实例化。
- 某些类型,虽然没有提供类模板所需要的全部功能,但是照样可以实例化模板,只要不调用那些没有提供功能的成员函数即可。
#ifndef CLASTMPL_H_INCLUDED
#define CLASTMPL_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
class Integer{
//自己定义了一个类,这个类只有类模板中的初始化功能,没有add()功能
public:
Integer(int const& i):m_i(i){}
int operator+(Integer const& other)const{
return m_i + other.m_i;
}
//使用友元函数重载<<运算符,使得<<可以访问成员私有变量
//友元函数的意识是,这个全局函数(操作符)是我的(这个类)的朋友
//所以,可以访问我的私有成员变量
friend ostream& operator<<(ostream& out, Integer const& that){
out << that.m_i << endl;
return out;
}
private:
int m_i;
};
//我们自己创建的类模板
template<class T>
class CMath{
public:
CMath(T const& t1, T const& t2):m_t1(t1), m_t2(t2){}
T add();
private:
T m_t1;
T m_t2;
};
//类模板成员在类模板外面去定义要点
//1.类模板的帽子不能丢了
//2.指定函数的作用域
template<class T>
T CMath<T>::add(){
return m_t1 + m_t2;
}
//编译器帮我们生成的类
//class CMath<int>{...};
//在用类模板示例化类的时候,编译器生成的东西
/*
class CMat<>{
public:
private:
int m_t1;
int m_t2;
}
注意:开始生成的类中,只有成员变量,没有成员函数
什么时候调用成员函数,成员函数什么时候出现在类中。
比如调用了构造函数,构造函数就生成到类中。
比如调用了add()函数,add()函数就生成到类中。
所以说,一个用模板生成类是一个过程,不是一蹴而就的。
*/
void ClassTemplateTest_2(){
int x = 10, y = 20;
CMath<Integer> math1(10, 20);
//虽然Integer类中没有定义+操作,
//但是,由于我们没有使用add,所以,CMath类中不会生成add()
//我们调用了构造函数,并且Integer中有构造函数,
//所以,CMath类中生成了构造函数。
cout << math1.add() << endl;
}
#endif // CLASTMPL_H_INCLUDED
1.3 类模板的静态成员
- 类模板中的静态成员即不是每个对象拥有一份,也不是类模板拥有一份
- 而应该是由类模板实例化出的每一个真正的类各有一份
- 且为该实例化类定义的所有对象共享。
#ifndef STATIC_H_INCLUDED
#define STATIC_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
template<class T>class A{
public:
static void print(){
cout << "&m_i:" << &m_i << "---&m_t:" << &m_t << endl;
}
//类的静态变量声明在类里面
//但是定义(赋值)要在类外面赋值
static int m_i;
static T m_t;
};
template<class T>int A<T>::m_i;
template<class T>T A<T>::m_t;//先不赋值,取随机数
void StaticTest(){
cout << "-----使用A<int>对象和A<int>类调用A<int>类中的static变量-----"<<endl;
A<int> x,y,z;
//类的静态变量是一个类用一份
//也就是说,用这个类定义的不同对象调用同一个静态变量
x.print();
y.print();
z.print();
A<int>::print();//用类直接调用函数
cout << endl;
cout << "-----使用A<double>对象和A<double>类调用A<double>中的static变量-----"<< endl;
A<double> a,b,c;
a.print();
b.print();
c.print();
A<double>::print();
}
#endif // STATIC_H_INCLUDED
1.4 类模板的递归实例化
- **前提:**我们可以使用任何类型来实例化类模板。
- **定义:**由类模板实例化产生的类,也可以用来实例化类模板自身,这种作为即为
类模板递归实例化
- **作用:**通过这种方法可以构建空间上具有递归特性的数据结构(多维数组)。
#ifndef RECURSION_H_INCLUDED
#define RECURSION_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
template<class T>
class Array{
public:
T& operator[](size_t i){
return m_arr[i];
}
private:
T m_arr[10];
};
void RecursionTest(){
//类模板的递归实例化
//注意,尖括号的嵌套中间要加上空格
Array<Array<int> > 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++)
std::cout << m[i][j] << ' ';
cout << endl;
}
#endif // 03_RECURSION_H_INCLUDED
1.5 类模板的全局特化(类模板的扩展)
1.5.1 全类特化和成员特化
为什么要特化?
我们定义了一个类模板,类模板里面的函数对于大部分的数据类型都适用,
但是,有极个别的类型不使用,这时候我们就要对这一两个特别的类型做特化。
特化是什么?
本质上,就是重写了针对某个特定数据类型的具体类。实际上就是写了一个类。
只不过,我们把他叫做类模板的特化。编译器在调用的时候,如果遇到了这个特化类,
即传入的参数符合特化类形式,也会优先调用特化类,而不会去依照着类模板再重新实例化一个类。
#ifndef SPECIAL_H_INCLUDED
#define SPECIAL_H_INCLUDED
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
//我们自己创建的类模板
template<class T>
class CMath{
public:
CMath(T const& t1, T const& t2):m_t1(t1), m_t2(t2){}
T add();
private:
T m_t1;
T m_t2;
};
template<class T>
T CMath<T>::add(){
return m_t1 + m_t2;
}
//成员特化:只对模板中的一个函数进行特化,这样比较省事
template<>char*const CMath<char*const>::add(){
return strcat(m_t1, m_t2);
}
//全类特化--其实就是写了一个具体的类型
//template<>这个尖括号中没有内容,代表特化
template<>
class CMath<char* const>{
public:
CMath<char* const>(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;
};
void SpecialTest(){
char cx[256] = "hello", cy[256] = "world";
CMath<char* const> m1(cx, cy);
//两个字符串拼接实现
cout << m1.add() << endl;
}
#endif // 04_SPECIAL_H_INCLUDED
1.6 局部特化
什么是局部特化呢?
将模板的参数给定一部分,啥意思呢?分为下面几种情况:
template<class T, class D>
class A{
public:
static void foo(){
cout<< "这是没有被特化的类" << endl;
}
};
局部特化情况1:
给定部分类型形参。具体看下面例子。
template<class T> class A<T, short>{
public:
static void foo(){
cout << "这是指定定了一个类型形参为short的特化" << endl;
}
};
局部特化情况2:
指定两个或多个类型形参一样,但是,不给定具体的类型形参。
template<class T>class A<T, T>{
public:
static void foo(){
cout << "指定两个类型形参为同一个类型,但是,没有给定具体值" <<endl;
}
}
局部特化情况3:
指定两个或多个类型形参为指针形式。
template<class T, class D>class A<T*, D*>{
public:
static void foo(){
cout << "指定两个类型形参都为指针类型,没有给定具体的类型" <<endl;
}
}
其实,我们通过上面的几种情况发现一个规律,只要是特化的时候,没有把所有类型形参给出具体的类型值,比如指定这个类型形参为int,另一个类型形参为double,只要不是这样给明了的,都可以叫做局部特化。
同时,编译器在执行的时候,会按照传入的具体参数情况,选择一个匹配对最高的特化类进行实例化。
但是,特化多了,我们还有使用模板的必要了吗???
所以,尽量减少特化,无论是全局特化还是局部特化。
注意
类模板的局部特化,除非必要否则尽量不要特化,因此特化版本过多容易引发编译器匹配歧义。
#ifndef PARTIAL_H_INCLUDED
#define PARTIAL_H_INCLUDED
#include <iostream>
using namespace std;
template<class T, class D>
class CMath{
public:
static void foo(){
cout << "原始版本1:CMath<T,D>::foo"<<endl;
}
};
//局部特化
//只特化了一个类型参数,没有把所有的类型参数都特化
//这就是局部特化
template<class T> class CMath<T, short>{
public:
static void foo(){
cout << "局部特化2:CMath<T, short>::foo" << endl;
}
};
template<class T>class CMath<T,T>{
public:
static void foo(){
cout << "3:CMath<T,T>:foo" << endl;
}
};
template<class T, class D>class CMath<T*, D*>{
public:
static void foo(){
cout << "4:CMath<T*, D*>" << endl;
}
};
void PartialTest(){
CMath<int, double>::foo();//1
CMath<int, short>::foo();//2
// CMath<short, short>::foo();//匹配歧义
CMath<int*,double*>::foo();//1
//CMath<int*,int*>::foo();//匹配歧义
}
#endif // PARTIAL_H_INCLUDED
1.7 类型形参的缺省值
什么是缺省值?
其实就是默认值,我们可以给尖括号里的模板类型形参定义默认值,这样,如果我们如果我们的用模板生成实例化对象的时候,如果没有给定具体类型,就可以按照默认缺省值去生成。
#ifndef QS_H_INCLUDED
#define QS_H_INCLUDED
#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;
//缺省值可以理解为默认值
//如果实例化类的时候,没有给定具体的类型,就按照缺省值实例化
//注意:如果第一个类型形参给了具体的类型,后面的类型形参也必须指定类型
template<class T=int, class D=int>
class A{
public:
A(){}
A(T t1, D d1):m_t(t1),m_d(d1){}
void foo(){
cout << "m_t type:" << typeid(m_t).name() <<'\n'<< "m_d type:" << typeid(m_d).name() << endl;
}
private:
T m_t;
D m_d;
};
void DefaultTest(){
A<double,double> a(10.1,20.2);//这里没有给定
A<> a1;
a.foo();
a1.foo();
}
#endif // QS_H_INCLUDED
1.8 数值形的模板参数
什么是数值型模板参数?
就是定义类模板的时候,尖括号里可以写类型形参,也可以将普通数值类型作为参数
**注意:**只能将普通数值类型参数作为模板,别的都不行。
#ifndef VALPARAM_H_INCLUDED
#define VALPARAM_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
template<class T = double, size_t S = 15>
class Array{
public:
T& operator[](size_t i){
return m_arr[i];
}
size_t Size(){
return S;
}
private:
T m_arr[S];
};
void ValparamTest(){
Array<int> a;
for(int i = 0; i < a.Size(); i++){
a[i] = i + 1;
}
for(int i = 0; i < a.Size(); i++)
std::cout << a[i] << ' ';
std::cout <<std::endl;
}
#endif // VALPARAM_H_INCLUDED