目录
一.泛型编程
二.函数模板
1.函数模板概念
2.函数模板格式
swap(a, b); 和 swap(c, d); 调用的是同一个函数吗?
3.函数模板的原理
4.函数模板的规则(包含类模板规则)
1.例1:可推演的例子
1.例2:不可推演的例子
3.例3
4.例4
函数模板:
类模板:
4.(1)例5
4.(2)例6
5.例7
6.例8
三.类模板
1.类模板的定义格式
2.类模板规则 请看 函数模板规则第4条
3.类模板和模板类
三.模板类相关易错题目
1.下面有关C++中为什么用模板类的原因,描述错误的是? ( )
2.关于模板的编译说法错误的是( )
3.下列的模板声明中,其中几个是正确的( )
泛型编程: 不再是针对某种类型,能适应广泛的类型,跟具体的类型无关的代码
如何实现一个通用的交换函数呢?
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(double& left, double& right)
{
double temp = left;
left = right;
right = temp;
}
因此我们需要告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码
template //或template
void Swap(T& left, T& right)
{
T tmp = left;
left = right;
right = tmp;
}
int main()
{
int a = 0, b = 1;
double c = 2.2, d = 3.3;
swap(a, b);
swap(c, d);
return 0;
}
答:不是同一个。底层汇编可以看出不是同一个,如果调试时发现走的是同一个函数,其实是编译器的优化导致。
实际上以后swap函数都不用自己写了,库中有模板,直接用就行
1.函数模板的类型一般是编译器根据实参传递给形参,推演出来的如果不能自动推演,那么我们就需要显示实例化,指定模板参数(请看例1,2)
2.一个template
3.模板参数--很多用法和函数参数是很像的,模板也可以用缺省形式(请看例3)
模板参数 -- 传递的是类型
函数参数 -- 传递的对象值
例如 template
4.模板可以声明定义分离(在同一个文件),但模板不支持声明和定义放到两个文件中的。会出现链接错误。(如果声明和定义分别放在.h和.cpp文件中,由于.h的声明中T没有确定,所以就找不到调用函数的地址)
声明定义写法解决方案:第一种更好,更常用
(1)声明定义合并写到.hpp或.h文件中:模板不支持声明和定义分别放到xxx.h和xxx.cpp中,一般是要放到一个文件中。有些地方就会命名成xxx.hpp,寓意就是头文件和定义实现内容合并一起,但是并不是必须是.hpp, .h也是可以的,只是.hpp寓意更好(实际上声明定义写在一个文件就直接实例化了)(请看例5)
(2)声明定义分别放在.h和.cpp中: 需要在template.cpp中针对于要使用的模板类型显示实例化(请看例6)
5.函数模板参数类型不可以矛盾。(请看例7)
6.有现成的函数,就用现成的函数;若显示实例化给出
template
void Swap(T& left, T& right)
{
T tmp = left;
left = right;
right = tmp;
}
int main()
{
int a = 0, b = 1;
double c = 2.2, d = 3.3;
swap(a, b);
swap(c, d);
return 0;
}
需要在函数名后显示实例化,指定模板参数(即加尖括号)
template
T* func(int n)
{
return new T[n];
}
int main()
{
// 函数模板显示实例化
int* p1 = func(10);
double* p2 = func(10);
return 0;
}
(1)模板也可以用缺省形式:template
template
T* func(int n)
{
return new T[n];
}
int main()
{
// 函数模板显示实例化
int* p1 = func(10);
double* p2 = func(10);
char* p3 = func(10); //不显示实例化时就是char类型
return 0;
}
(2)缺省只能从右往左
template
void Func()
{
cout << sizeof(K) << endl;
cout << sizeof(V) << endl;
}
int main()
{
Func();
Func();
return 0;
}
(3)全缺省
template
void Func()
{
cout << sizeof(K) << endl;
cout << sizeof(V) << endl;
}
int main()
{
Func();
Func();
Func();
return 0;
}
模板可以声明定义分离,但模板不支持声明和定义放到两个文件中的。会出现链接错误。
// 函数模板声明
template
void Swap(T& left, T& right);
// 函数模板定义
template
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
// 类模板声明
template
class Vector
{
public:
Vector(size_t capacity = 10);
void PushBack(const T& x);
private:
T* _pData;
size_t _size;
size_t _capacity;
};
// 类模板定义
template
Vector::Vector(size_t capacity)
: _pData(new T[capacity])
, _size(0)
, _capacity(capacity)
{}
声明和定义放到同一个文件中,这个文件后缀写成.hpp,寓意就是头文件和定义实现内容合并一起,其实这个文件写成.h也是可以正常用的,只是.hpp寓意更明显。
template.hpp中:
// 类模板声明
template
class Vector
{
public:
Vector(size_t capacity = 10);
void PushBack(const T& x);
private:
T* _pData;
size_t _size;
size_t _capacity;
};
// 函数模板声明
template
void Swap(T& left, T& right);
// 类模板定义
template
Vector::Vector(size_t capacity)
: _pData(new T[capacity])
, _size(0)
, _capacity(capacity)
{}
template //声明定义分开写,每个函数都要加 template
void Vector::PushBack(const T& x)
{
// ...
}
// 函数模板定义
template
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
test.c中:
#include "template.hpp" //就可以正常使用了
int main()
{
int a = 0, b = 1;
Swap(a, b);
Vector v1;
Vector v2;
return 0;
}
细节注意:类中每个函数定义时都要前面加上template
template
void Vector::PushBack(const T& x)
{
// ...
}
声明定义分开在两个文件,只需要在.cpp文件中 显示实例化指定 要使用的类型
template.cpp中:
#include "template.h"
template
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
template
Vector::Vector(size_t capacity)
: _pData(new T[capacity])
, _size(0)
, _capacity(capacity)
{}
//显示实例化指定
template
void Swap(int& left, int& right); //显示实例化指定
template
class Vector < int > ; //显示实例化指定
template
class Vector < double > ; //显示实例化指定
如果参数类型一个传的是整形,另一个传的是浮点型就会报错,①需要把它们全部显示实例化成一个类型,②或改变其中一个参数类型,使整体类型相同
template
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a1 = 10, a2 = 20;
double d1 = 10.0, d2 = 20.0;
Add(a1, a2); //正常情况
Add(d1, d2);
//Add(a1, d2); 这样是推不出来的
//解决方案:
Add(a1, d2);
Add(a1, d2);
Add(a1, (int)d2);
Add((double)a1, d2);
}
6.有现成的函数,就用现成的函数;若显示实例化给出
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template
T Add(T left, T right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 与非模板函数匹配,直接用现成的
Add(1, 2); // 显示实例化,用模板
}
Vector v1;
Vector v2;
举例:调用 Stack
// 类模板
template
class Stack
{
public:
Stack(int capacity = 0)
{
_a = new T[capacity];
_capacity = capacity;
_top = 0;
}
~Stack()
{
cout << "~Stack()" << endl;
delete[] _a;
_capacity = 0;
_top = 0;
}
void Push(const T& x)
{}
private:
T* _a;
int _top;
int _capacity;
};
int main()
{
Stack st1; // int
st1.Push(1);
Stack st2; // double
st2.Push(2.2);
return 0;
}
2.类模板中的成员函数全部是模板函数。(假设类中不用虚拟类型(模板类型),只用内置类型,然后我给一个打印函数只打印内置类型,这个打印函数虽然没有用模板类型,但它仍是模板函数)
类模板是一个类家族,模板类是通过类模板实例化的具体类
模板类是一个家族,编译器的处理会分别进行两次编译,其处理过程跟普通类不一样
A.可用来创建动态增长和减小的数据结构
B.它是类型无关的,因此具有很高的可复用性
C.它运行时检查数据类型,保证了类型安全
D.它是平台无关的,可移植性
解:
A.模板可以具有非类型参数,用于指定大小,可以根据指定的大小创建动态结构
B.模板最重要的一点就是类型无关,提高了代码复用性
C.模板运行时不检查数据类型,也不保证类型安全,相当于类型的宏替换,故错误
D.只要支持模板语法,模板的代码就是可移植的
选C。
A.模板在.h文件中声明,在.cpp里面实现
B.模板程序一般直接在一个文件里面进行定义与实现
C.不久的将来,编译器有望支持export关键字,实现模板分离编译
D.模板不能分离编译,是因为模板程序在编译过程中需要经过两次编译
解:
A.模板不支持分离编译,所以不能在.h声明,在.cpp实现
B.由于不支持分离编译,模板程序一般只能放在一个文件里实现
C.不支持分离编译并不是语法错误,而是暂时的编译器不支持,不久将来,或许会被支持
D.模板程序被编译两次,这是不能分离编译的原因所在。选A。
1)template
2)template
3)template
4)template
5)template
6)template
7)template
8)
9)template
10)template
11)template
A.3
B.4
C.5
D.6
答案:——>
分析:正确的定义为:4 6 7 9 10 11,一共6个,故答案为D。(第11个 template