C++学习笔记-模板
模板把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属。模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。
什么是模板
- 类属——类型参数化,又称参数模板
使得程序(算法)可以从逻辑功能上抽象,把被处理的对象(数据)类型作为参数传递 - C++提供两种模板机制:
- 函数模板
- 类模板
函数模板
考虑求两参数之中大值函数:max(a , b)
对 a, b 的不同类型,都有相同的处理形式:
return(a > b) ? a : b;
用已有方法解决对不同数据类型处理:
- 宏替换
#define max(a , b)(a > b ? a : b)
问题:避开类型检查
2. 重载
问题:需要许多重载版本
3. 使用函数模板
重载函数通常基于不同的数据类型实现类似的操作
对不同数据类型的操作完全相同,用函数模板实现更为简洁方便
模板说明
声明模板中使用的类属参数。形式为(template和typename都是关键字)
template <类型形式参数表>
类型形式参数的形式为:
typename T1, typename T2,···, typename Tn
或 class T1, class T2,···, class Tn
e.g.
template <typename T>
template <typename ElementType>
template <typename NameType, typename DateType>
函数模板与模板函数
函数模板声明
template <类型形式参数表>
类型 函数名(形式参数表)
{
语句序列
}
- 函数模板定义由模板说明和函数定义组成
- 模板说明的类属参数必须在函数定义中至少出现一次
- 函数参数表中可以使用类属类型参数,也可以使用一般类型参数
e.g.
#include <iostream>
using namespace std;
template <typename T>
T max(T a, T b)
{
return a > b ? a : b;
}
void main()
{
cout << "max (3, 5) is " << max(3, 5) << endl;
cout << "max ('y', 'e') is " << max('y', 'e') << endl;
cout << "max (9.3, 0.5) is " << max(9.3, 0.5) << endl;
system("pause");
}
输出结果为:
max (3, 5) is 5
max (‘y’, ‘e’) is y
max (9.3, 0.5) is 9.3
请按任意键继续. . .
重载函数模板
有些特殊情况需要函数模板参与重载
e.g.
template <typename T>
T max(T a, T b)
{
return a > b ? a : b;
}
当存在以下调用时候:
void f(int i, char c)
{
max(i, i); // ok
max(c, c); // ok
max(i, c); // error,无法匹配
max(c, i); // error
}
这种时候就需要进行重载
template <typename T>
T max(T a, T b)
{
return a > b ? a : b;
}
int max(int a, int b) // 模板函数重载版本
{
return a > b ? a : b;
}
void f(int i, char c)
{
max(i, i); // ok
max(c, c); // ok
max(i, c); // ok ,由系统提供隐式转换
max(c, i); // ok
}
e.g.
#include <iostream>
using namespace std;
template <typename T>
T Max(const T a, const T b)
{
return a>b ? a : b;
}
template <typename T>
T Max(const T a, const T b, const T c)
{
T t;
t = Max(a, b);
return Max(t, c);
}
int Max(const int a, const char b)
{
return a>b ? a : b;
}
void main()
{
cout << "Max(3, 'a') is " << Max(3, 'a') << endl;
cout << "Max(9.3, 0.5) is " << Max(9.3, 0.5) << endl;
cout << "Max(9, 5, 23) is " << Max(9, 5, 23) << endl;
system("pause");
}
输出结果为:
Max(3, ‘a’) is 97
Max(9.3, 0.5) is 9.3
Max(9, 5, 23) is 23
请按任意键继续. . .
匹配约定:
- 寻找和使用最符合函数名和参数类型的函数,若找到则调用它
- 否则,寻找一个函数模板,将其实例化产生一个匹配的模板函数,若找到则调用它
- 否则,寻找可以通过类型转换进行参数匹配的重载函数,若找到则调用它
- 如果按以上步骤均未能找到匹配函数,则调用错误
- 如果调用有多于一个的匹配选择,则调用匹配出现二义性
e.g.
#include <iostream>
using namespace std;
int Max(int a, int b)
{
cout<<"int Max(int a, int b)"<<endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b)
{
cout<<"T Max(T a, T b)"<<endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b, T c)
{
cout<<"T Max(T a, T b, T c)"<<endl;
return Max(Max(a, b), c);
}
void main()
{
int a = 1;
int b = 2;
cout<<Max(a, b)<<endl;
cout<<Max<>(a, b)<<endl;
cout<<Max(3.0, 4.0)<<endl;
cout<<Max(5.0, 6.0, 7.0)<<endl;
cout<<Max('a', 100)<<endl;
system("pause");
return ;
}
类模板
- 类模板用于实现类所需数据的类型参数化
- 类模板在表示如数组、表、图等数据结构显得特别重要,这些数据结构的表示和算法不受所包含的元素类型的影响
类模板与模板类
类模板由模板说明和类说明构成
template <类型形式参数表>
类声明
e.g.
template <typename Type>
class TClass
{
// TClass的成员函数
private:
Type DateMember;
//…
};
数组类模板例子:
#include <iostream>
using namespace std;
template <typename T>
class Array
{
public:
Array(int s);
virtual ~Array();
virtual const T& Entry(int index) const;
virtual void Enter(int index, const T & value);
protected:
int size;
T * element;//数据成员是T类型指针
};
//类模板的成员函数是函数模板
template <typename T>
Array<T>::Array(int s)
{
if (s > 1)
size = s;
else size = 1;
element = new T[size];
}
template <typename T>
Array <T>::~Array()
{
delete[] element;
}
template <typename T>
const T& Array<T>::Entry(int index) const
{
return element[index];
}
template <typename T>
void Array <T>::Enter(int index, const T& value)
{
element[index] = value;
}
void main()
{
Array <int> IntAry(5);
int i;
for (i = 0; i < 5; i++)
{
IntAry.Enter(i, i);
}
cout << "Integer Array : \n";
for (i = 0; i < 5; i++)
{
cout << IntAry.Entry(i) << '\t';
}
cout << endl;
Array <double> DouAry(5);
for (i = 0; i < 5; i++)
{
DouAry.Enter(i, (i + 1)*0.35);
}
cout << "Double Array : \n";
for (i = 0; i < 5; i++)
{
cout << DouAry.Entry(i) << '\t';
}
cout << endl;
system("pause");
}
输出结果:
Integer Array :
0 1 2 3 4
Double Array :
0.35 0.7 1.05 1.4 1.75
请按任意键继续. . .
类模板作函数参数
- 函数的形式参数类型可以是类模板或类模板的引用对应的实际参数是该类模板实例化的模板类对象
- 当一个函数拥有类模板参数时,这个函数必定是函数模板
一个用 Array 作参数的函数模板:
template <typename T>
void Tfun(const Array <T> & x, int index)
{
cout << x.Entry(index) << endl;
}
调用函数模板
Array <double> DouAry(5);
···
Tfun(DouAry, 3);
① 建立对象
class Array
{
public:
Array(int s);
virtual ~Array();
virtual const double & Entry(int index) const;
virtual void Enter(int index, const double & value);
private:
int size;
double * element;
};
调用构造函数,实例化模板类,建立对象
② 调用函数
virtual const double & Entry( int index ) const;
在类层次中的类模板
一个类模板在类层次结构中既可以是基类也可以是派生类:
- 类模板可以从模板类派生
- 类模板可以从非模板类派生
- 模板类可以从类模板派生
- 非模板类可以从类模板派生
e.g.(从类模板Array派生一个安全数组类模板BoundArray
)
template <typename T>
class Array
{
public:
Array(int s);
virtual ~Array();
virtual const T& Entry(int index) const;
virtual void Enter(int index, const T & value);
protected:
int size;
T * element;
};
template <typename T>
class BoundArray : public Array <T>
{
public:
BoundArray(int low = 0, int height = 1);
virtual const T& Entry(int index) const;
virtual void Enter(int index, const T& value);
private:
int min;
};
- 类模板派生普通类,在定义派生类时要对基类的抽象类参数实例化
- 从普通类派生模板类,意味着派生类添加了抽象类数据成员
从类模板A派生普通类B:
#include <iostream>
using namespace std;
template <typename T>//定义类模板
class A
{
public:
A(T x)
{
t = x;
}
void out()
{
cout << t << endl;
}
protected:
T t;
};
class B : public A<int>//派生一般类
{
public:
B(int a, double x) : A <int>(a)
{
y = x;
}
void out()
{
A <int> ::out();
cout << y << endl;
}
protected:
double y;
};
void main()
{
A <int> a(123);
a.out();
B b(789, 5.16);
b.out();
system("pause");
}
输出结果:
123
789
5.16
请按任意键继续. . .
类模板与友员
在类模板中可以声明各种友员关系
- 一个函数或函数模板可以类是或类模板的友员
- 一个类或类模板可以是类或类模板的友员类
- 声明这种模板之间的友员关系符号比较烦琐
template <typename T>
class X
{
friend void f1();
}
函数f1成为类模板X实例化的每个模板类的友员函数
template <typename T>
class X
{
friend void f2(X<T> &);
}
对特定类型(如double),使模板函数f2(X&)成为X的友员
template <typename T>
class X
{
friend void A::f3();
}
A类的成员函数f3成为类模板X实例化的每个模板类的友员函数
template <typename T>
class X
{
friend void B<T>::f4(X<T> &);
}
对特定类型(如double),使模板类B的成员函数f4(X&)成为模板类X的友员
template <typename T>
class X
{
friend class Y;
}
Y类的每个成员函数成为类模板X实例化的每个模板类的友员函数
template <typename T>
class X
{
friend class Z<T>;
}
对特定类型(如double),使模板类Z所有成员函数成为模板类X的友员
e.g.(为复数类模板用定义重载运算符友员函数)
#include <iostream>
using namespace std;
template <typename T>
class Complex;
template <typename T>
class Complex
{
public:
Complex(T r = 0, T i = 0);
Complex(T a)
{
Real = a;
Image = 0;
}
void print() const;
friend Complex operator+ <T>(const Complex<T> &c1, const Complex<T> &c2);
friend Complex operator- <T>(const Complex<T> &c1, const Complex<T> &c2);
friend Complex operator- <T>(const Complex<T> &c);
private:
T Real, Image;
};
template <typename T>
Complex<T>::Complex(T r, T i)
{
Real = r;
Image = i;
}
template <typename T>
Complex<T> operator+ (const Complex<T> &c1, const Complex<T> &c2)
{
T r = c1.Real + c2.Real;
T i = c1.Image + c2.Image;
return Complex<T>(r, i);
}
template <typename T>
Complex<T> operator- (const Complex<T> &c1, const Complex<T> &c2)
{
T r = c1.Real - c2.Real;
T i = c1.Image - c2.Image;
return Complex<T>(r, i);
}
template <typename T>
Complex<T> operator- (const Complex<T> &c)
{
return Complex<T>(-c.Real, -c.Image);
}
template <typename T>
void Complex<T>::print()const
{
cout << '(' << Real << " , " << Image << ')' << endl;
}
void main()
{
Complex<double> c1(2.5, 3.7), c2(4.2, 6.5);
Complex<double> c;
c = c1 - c2;
c.print();
c = c2 + c2;
c.print();
c = -c1;
c.print();
system("pause");
}
输出结果为:
(-1.7 , -2.8)
(8.4 , 13)
(-2.5 , -3.7)
请按任意键继续. . .
在这里过于模板的操作符重载,在开始的时候有一个问题,参考文章如下:
关于操作符的友元模版函数重载
类模板与static成员
- 从类模板实例化的每个模板类有自己的类模板数据成员,该模板类的所有对象共享一个static数据成员
- 和非模板类的static数据成员一样,模板类的static数据成员也应该在文件范围定义和初始化
- 每个模板类有自己的类模板的static数据成员副本
e.g.(为圆类模板定义静态成员)
#include <iostream>
using namespace std;
const double pi = 3.14159;
template<typename T>
class Circle
{
T radius;
static int total;//类模板的静态数据成员
public:
Circle(T r = 0)
{
radius = r;
total++;
}
void Set_Radius(T r)
{
radius = r;
}
double Get_Radius()
{
return radius;
}
double Get_Girth()
{
return 2 * pi * radius;
}
double Get_Area()
{
return pi * radius * radius;
}
static int ShowTotal();//类模板的静态成员函数
};
template<typename T>
int Circle<T>::total = 0;
template<typename T>
int Circle<T>::ShowTotal()
{
return total;
}
void main()
{
Circle<int> A, B;//建立了2个对象
A.Set_Radius(16);
cout << "A.Radius = " << A.Get_Radius() << endl;
cout << "A.Girth = " << A.Get_Girth() << endl;
cout << "A.Area = " << A.Get_Area() << endl;
B.Set_Radius(105);
cout << "B.radius = " << B.Get_Radius() << endl;
cout << "B.Girth=" << B.Get_Girth() << endl;
cout << "B.Area = " << B.Get_Area() << endl;
cout << "Total1=" << Circle<int>::ShowTotal() << endl;//显示建立的对象数
cout << endl;
Circle<double> X(6.23), Y(10.5), Z(25.6);//建立了3个对象
cout << "X.Radius = " << X.Get_Radius() << endl;
cout << "X.Girth = " << X.Get_Girth() << endl;
cout << "X.Area = " << X.Get_Area() << endl;
cout << "Y.radius = " << Y.Get_Radius() << endl;
cout << "Y.Girth=" << Y.Get_Girth() << endl;
cout << "Y.Area = " << Y.Get_Area() << endl;
cout << "Z.Girth=" << Z.Get_Girth() << endl;
cout << "Z.Area = " << Z.Get_Area() << endl;
cout << "Total2=" << Circle<double>::ShowTotal() << endl;//显示建立的对象数
system("pause");
}
输出结果:
A.Radius = 16
A.Girth = 100.531
A.Area = 804.247
B.radius = 105
B.Girth=659.734
B.Area = 34636
Total1=2X.Radius = 6.23
X.Girth = 39.1442
X.Area = 121.934
Y.radius = 10.5
Y.Girth=65.9734
Y.Area = 346.36
Z.Girth=160.849
Z.Area = 2058.87
Total2=3
请按任意键继续. . .
关于模板补充
模板调用的两种方式
template <typename T>
void swap(T &a, T &b)
{
T t = a;
a = b;
b = t;
}
int x = 1;
int y = 2;
swap(x, y);//自动类型推导
swap<int>(x, y);//具体类型调用
函数模板的深入理解
- 编译器并不是把函数模板处理成能够处理任意类型的函数
- 编译器从函数模板通过具体类型产生不同的函数
- 编译器会对函数模板进行两次编译
- 在声明的地方对模板代码本身进行编译
- 在调用的地方对参数替换后的代码进行编译
模板总结
- 模板是C++类型参数化的多态工具。C++提供函数模板和类模板
- 模板定义以模板说明开始。类属参数必须在模板定义中至少出现一次
- 同一个类属参数可以用于多个模板
- 类属参数可用于函数的参数类型、返回类型和声明函数中的变量
- 模板由编译器根据实际数据类型实例化,生成可执行代码。实例化的函数。模板称为模板函数;实例化的类模板称为模板类
- 函数模板可以用多种方式重载
- 类模板可以在类层次中使用
C++学习笔记-模板的更多相关文章
- es6学习笔记--模板字符串
这几天简单看了一下深入浅出es6这本书,感觉特实用,学习了一个新特性---模板字符串在项目开发中,拼接字符串是不可缺少的,动态创建dom元素以及js操作数据都要拼接字符串,在es6出来之前,我们都通常 ...
- 学习笔记——模板模式Template
模板模式,主要是利用多态来实现具体算法和父类逻辑的松耦合.父类中TemplateMethod内部定义了相应的算法操作顺序,子类负责实现相应的具体实现. 举例: 项目中曾遇到过一个需求,叫做高级价格体系 ...
- django学习笔记-模板层
模板层 将Python嵌入到HTML中. 模板简介 将HTML硬解码到视图并不是那么完美原因如下: 对页面设计时也需要对python代码进行相应的修改,模板可以不就行python代码修改的情况下变更设 ...
- 扩展中国剩余定理学习笔记+模板(洛谷P4777)
题目链接: 洛谷 题目大意:求同余方程组 $x\equiv b_i(mod\ a_i)$ 的最小正整数解. $1\leq n\leq 10^5,1\leq a_i\leq 10^{12},0\leq ...
- Thinkphp学习笔记-模板赋值
如果要在模板中输出变量,必须在在控制器中把变量传递给模板,系统提供了assign方法对模板变量赋值,无论何种变量类型都统一使用assign赋值. $this->assign('name',$va ...
- Thinkphp学习笔记-模板主题
一个模块如果需要支持多套模板文件的话,就可以使用模板主题功能. 默认情况下,没有开启模板主题功能,如果需要开启,设置 DEFAULT_THEME 参数即可: // 设置默认的模板主题 'DEFAULT ...
- 《TP5.0学习笔记---模板变量输出、替换和赋值篇》
原文地址:http://blog.csdn.net/self_realian/article/details/75214922 模板变量输出.替换和赋值 我们看一下文件编译的结果,我们知道我们现在写的 ...
- vue学习笔记 模板语法(三)
<div id="kk"> <div>直接输出文本:{{msg}}</div> <div>自定义过滤器输出文本:{{msg|capi ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
随机推荐
- Badboy + JMeter性能测试(转)
1. 软件介绍 1.1 Badboy Badboy是用来录制操作过程的,它录制的结果是被jmeter做并发测试的素材使用. 下载网址:http://www.badboy.com.au/ 1.2下 ...
- ibatis和mybatis中的BatchExecutor
ibatis中的的处理方法 spring集成了ibatis的批量提交的功能,我们只要调用API就可以了 首先在你的dao中需要继承org.springframework.orm.ibatis.supp ...
- JavaScript基础之变量的自增与自减
一.自增(++) ⑴什么是自增? 通过自增运算符可以使变量在自身的基础上加一: 对于一个变量自增以后,原变量的值会立即自增一: 示例: <!DOCTYPE html> <html l ...
- Subarray Sorting (线段树)
题意:给你两个长度为 n 的序列 a 和 b , 可以对 a 进行 操作: 选择一段区间[ l, r ] ,使得序列a 在这段区间里 按升序排序. 可以对a 进行任意多次操作,问 a是否有可能变成b序 ...
- ZOJ - 3591 NIM
ZOJ - 3591NIM 题目大意:给你n,s,w和代码,能生成长度为n的序列,问异或和不为0的子序列有多少个? 这是个挂羊头卖狗肉的题,和NIM博弈的关系就是要异或和不为0,一开始以博弈甚至循环节 ...
- Codevs 1851 越狱 2008年湖南省队选拔赛
1851 越狱 2008年湖南省队选拔赛 时间限制: 10 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 监狱有连续编号为1-N的N个房间,每 ...
- template模板循环嵌套循环
template嵌套循环写法:在第一次循环里面需要循环的地方再写个循环,把要循环的数据对象改为第一层的循环对象别名 //template模板循环嵌套循环 <script id="ban ...
- soa soap http rpc
soa 是一种计算机软件的设计模式,主要应用于不通应用组件中通过某种协议来互操作 它的基本设计原理是:服务提供了一个简单的接口,抽象了底层的复杂性,然后用户可以访问独立的服务,而不需要去了解服务底层平 ...
- centos 链接错误解决方法
执行除cd sln以外任何指令都报错,解决方法:sln /usr/lib64/ld-2.17.so /usr/lib64/ld-linux-x86-64.so.2 LD_PRELOAD=/lib64/ ...
- springboot备忘
1.springboot中有ApplicationRunner类,如果项目中的启动类名称也是ApplicationRunner,单元测试时需要注意:import不要import到springboot的 ...