自己怎么做企业网站建设,太原企业网站seo,官方网页qq登陆,陕西省西安市事业单位招聘网1.泛型编程
1.1什么事泛型编程
在学习C语言时#xff0c;我们时常会有这样的烦恼#xff1a;
在针对每一种不同的类型变量进行函数传参或者是运算处理时#xff0c;我们总是编写不同的函数或者是进行不同的处理#xff0c;才能达到目的#xff0c;这时#xff0c;我们…1.泛型编程
1.1什么事泛型编程
在学习C语言时我们时常会有这样的烦恼
在针对每一种不同的类型变量进行函数传参或者是运算处理时我们总是编写不同的函数或者是进行不同的处理才能达到目的这时我们就会想到有没有什么操作能够让我们只写一种函数就能堆所有的百年来那个类型都进行处理在C中就实现了泛型编程。
我们先来看看下面的代码
这是C语言的交换函数
void swapi(int* x, int* y)
{int tmp *x;*x *y;*y tmp;
}
void swapd(double* x, double* y)
{double tmp *x;*x *y;*y tmp;
}
int main()
{int i 12;int j 20;printf(整形, 交换前i:%d, j:%d\n, i, j);swapi(i, j);printf(整形, 交换后i:%d, j:%d\n, i, j);double in 12.5;double jn 32.6;printf(双精度浮点型, 交换前i:%lf, j:%lf\n, in, jn);swapd(in, jn);printf(双精度浮点型, 交换后i:%lf, j:%lf\n, in, jn);return 0;
} 从上面的代码我们可以看出在C语言中我们进行相同的操作但由于受限于变量的类类型我们还是需要进行许多操作这不由得让我们觉得过于冗余。
但是反泛型编程就能够很好的解决这样的问题
我们来及看下面的代码 using namespace std;templateclass T
void Swap(T x, T y)
{T tmp x;x y;y tmp;
}
int main()
{int i 12;int j 20;printf(整形, 交换前i:%d, j:%d\n, i, j);Swap(i, j);printf(整形, 交换后i:%d, j:%d\n, i, j);double in 12.5;double jn 32.6;printf(双精度浮点型, 交换前i:%lf, j:%lf\n, in, jn);Swap(in, jn);printf(双精度浮点型, 交换后i:%lf, j:%lf\n, in, jn);return 0;
}通过反省编程我们就轻松的实现了各类型的数据交换避免了C语言中的一些无法避免的问题是程序更加的见解可读性也变得更高
接下来我们就来正式的了解一下什么是泛型编程。
2. 泛型模板
我们可以想象一下在一个制作工艺中我们通常可以通过相同的模板来实现将不同的类型的材料塑造吃呢公同样的形状在这里我们就需要用到一种模具来实现这样的操作。
如果C中也存在这样的模板的话通过在模板上填充不同的材料类型变量来获取不同才来哦的构建即生成相应的代码那将会节省许多的头发。巧的就是前人早已将树种好我们现在只需要再次乘凉就好。
所以综上泛型编程就是编写与类型无关代码是代码复用的手段。模板是反省编程的基础。 3.函数模板
3.1函数模板的概念
函数模板代表了一个函数家族该函数模板与类型无关再试用时被参数化根据实参类型产生函数的特定类型版本。
3.2函数模板的模式 templatetypename T1, typename T2,......,typename Tn
返回值的类型 函数名参数列表{}
templatetypename T
void Swap( T left, T right)
{
T temp left;
left right;
right temp;
}
注意typename是用来定义函数模板的关键字也可以使用class切记不能使用struct来代替class
3.3函数模板实现的原理
函数 模板是一个蓝图他本省并不是函数是编译器使用方式产生具体类型的函工具。所以其实魔板就是将我们本应该重复做的事情交给了编译器来做这样可以可以提高效率。
在编译器的编译阶段对于函数模板编译器需要根据传入的参数来推演出生成对应函数的类型将T确定为double类型然后产生一份专门处理double类型的代码 对于其他的类型也都是这样的。
3.4函数模板的实例化
用不同的类型的参数使用函数模板时称为函数模板的实例化。模板参数的实例化分为
隐式实例化和显示实例化
1.隐式实例化让编译器根据实参来推断模板参数1的实际类型 templateclass T
T Add(const T left, const T right)
{
return left right;
} i
nt main()
{
int a1 10, a2 20;
double d1 10.0, d2 20.0;
Add(a1, a2);
Add(d1, d2);
/*
该语句不能通过编译因为在编译期间当编译器看到该实例化时需要
通过实参a1将T推演为int通过实参d1将T推演为double类型但模板
一个T
编译器无法确定此处到底该将T确定为int 或者 double类型而报错
注意在模板中编译器一般不会进行类型转换操作因为一旦转化出问题编译器就需要
背黑锅
Add(a1, d1);
*/
// 此时有两种处理方式1. 用户自己来强制转化 2. 使用显式实例化
Add(a, (int)d);
return 0;
} 2.显示实例化在函数的使用时在函数名的后面中指定模板参数的实际类型
i
nt main(void)
{
int a 10;
double b 20.0;
// 显式实例化
Addint(a, b);
return 0;
}
如果类型不匹配编译器会尝试进行隐式类型转化如果无法进行正确的转化编译器就会报错。
3.5模板参数的匹配原则
1.一个非模板函数可以和一个同名的函数一起存在而且这个函数在进行调用时。会有先调用已经存在的类型的函数同时模板函数还是可以经过实例化生成这个函数。 // 专门处理int的加法函数
int Add(int left, int right)
{
return left right;
} /
/ 通用加法函数
templateclass T
T Add(T left, T right)
{
return left right;
} v
oid Test()
{
Add(1, 2); // 与非模板函数匹配编译器不需要特化
Addint(1, 2); // 调用编译器特化的Add版本
}
2.对于非模板函数和对应同名的函数模板 如果其他条件相同在调用时会有优先调用非模板函数而不会从该模板中产生一个实例。如果模板可以产生一个更好的实力函数那么编译器还是会考虑从模板重中生成。
// 专门处理int的加法函数
int Add(int left, int right)
{
return left right
}// 通用加法函数
templateclass T1, class T2
T1 Add(T1 left, T2 right)
{
return left right;
}void Test()
{
Add(1, 2); // 与非函数模板类型完全匹配不需要函数模板实例化
Add(1, 2.0); // 模板函数可以生成更加匹配的版本编译器根据实参生成更加匹配的
Add函数
}
3.模板函数不允许自动类型转换但是普通函数可以进行这样的操作
4.类模板
4.1类模板的定义方式
templateclass T1, class T2, ..., class Tn
class 类模板名
{
// 类内成员定义
};
#includeiostream
using namespace std;
// 类模版
templatetypename T
class Stack
{ p
ublic:
Stack(size_t capacity 4)
{
_array new T[capacity];
_capacity capacity;
_size 0;
}void Push(const T data);
private:
T* _array;
size_t _capacity;
size_t _size;
};
// 模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误具体原因后面会讲
templateclass T
void StackT::Push(const T data)
{
// 扩容
_array[_size] data;
_size;
}
int main()
{
Stackint st1; // int
Stackdouble st2; // double
return 0;
} 4.2类模板的实例化
类模板的实例化与函数的模板实例化有所不同类模板实例化需要在类模板名字后面跟然后就爱那个实力化的类型放在中即可类模板的名字不是真正的类而是实例化后的结果才是真正的类 / Stack是类名Stackint才是类型
Stackint st1; // int
Stackdouble st2; // double 好今天的内容就到这里咱们下期再见拜拜