【C++】C++ 核心进阶(一):泛型编程基石 —— 模板初阶与 STL 体系开篇

发布时间:2026/9/6 23:47:33
【C++】C++ 核心进阶(一):泛型编程基石 —— 模板初阶与 STL 体系开篇 目录一泛型编程二函数模板1函数模板的格式2模板函数的原理3函数模板的实例化4模板参数的匹配原则三类模板1类模板的定义格式2类模板的使用3,类模版的内存开辟和释放四STL1了解STL2STL的六大组件五补充C当中的隐式类型转换1自动类型转换2强制类型转换显式转换3运算符重载中的隐式转换4用户定义转换运算符一泛型编程之前在C语言当中进行交换变量值对于不同类型的变量的交换需求就需要重新创建一个新的函数来接收不同的类型变量例如对于这种函数只有类型不同的情况可以创建一个模版传入不同类型的数据就形成不同的函数。这样的编程就可以称为泛型编程。泛型编程编写与类型无关的通用代码模版是泛型编程的基础。模版分为1函数模版 2类模版二函数模板1函数模板的格式template typename T1,typename T2, ………,typename Tn返回值类型 函数名参数列表{函数体}typename也可以换成class返回值类型可以是T也可以是自己设置的类型注意参数列表当中的参数类型为T1,T2T3……实例展示内容交换//模板函数 //只能同种类型直接进行交换 templatetypename T void Swap(T a, T b) { T tmp a; a b; b tmp; } //支持同种类型进行交换也支持不同中类型的交换 //两个函数的函数名相同根据传入的类型进行匹配函数 templateclass T1,class T2 void Swap(T1 a, T2 b) { T1 tmp a; a b; b tmp; } int main() { int i 1, j 2; double m 3.0, n 2.3; //同类型调用第一个swap() swap(i, j); swap(m, n); //不同类型调用第二个swap() Swap(i, m); return 0; }2模板函数的原理函数模板其实相当于一个蓝图他不是真正的函数是编译器在编译期间根据具体的调用实例出的具体函数。3函数模板的实例化使用不同类型的参数使用模版09函数就是函数的实例化模板参数的实例化分为隐式实例化和显式实例化1隐式实例化让编译器根据传入的实参来推演参数类型//用函数模板生成对应的函数--模板实例化 template typename T T Add(const T left, const T right) { return left right; } int main() { int i 2, j 3; double m 2.4, n 5.5; //推导实例化--先强制类型转换 cout Add(i, j) endl; cout Add(m, n) endl; //先强制类型转换 cout Add(i, (int)m) endl; cout Add((double)i, m) endl; return 0; }2显示实例化在函数模板后的中指定参数类型补充解释显示实例化已经确定了函数参数的类型将不同类型的实参传入函数的参数时会生成临时对象并传入其中当函数执行完后临时对象也会进行销毁传入的参数生成临时对象的过程相当于进行隐式类型转化生成一个新的对象。4模板参数的匹配原则1一个非模板函数和一个模板函数同时存在且实参的类型和非模板函数的参数匹配时会直接使用非模板函数。2当两者同时存在但是非模板函数的参数类型和实参不匹配时优先使用模板函数。函数的返回值类型T2是double补充在模板参数推导时编译器不会进行隐式类型转换但在模板显式实例化后类型已确定就相当于一个普通函数函数调用时会发生隐式类型转换。举个例子“模板推导是‘看菜下饭’每次都根据实参重新匹配上一顿吃的 int不影响这一顿吃 double。只有当你强行指定‘今天只吃 int’显式指定int时厨房才会把 double 硬塞进 int 的盘子里。”三类模板1类模板的定义格式templatetypename T1,typename T2,……typename Tnclass 类模板名{//类内成员定义}2类模板的使用//类模板 template typename T class Stack { public: Stack(int n 4) :array(new T[n]) , size(0) , capacity(n) { } ~Satck() { delete[] array; array nullptr; size capacity 0; } void Push(const T x) { if (size capacity) { //C中不能使用realloc扩展空间 //手动扩容--直接创建一个新的对象 T* tmp new T[capacity * 2]; memcpy(tmp, array, capacity*sizeof(T)); delete[] array;//清理原空间 array tmp; capacity * 2; } array[size] x; } private: T* array; size_t size; size_t capacity; }; int main() { //模板类只有显示实例化 Stackint str1; str1.Push(1); str1.Push(1); str1.Push(1); Stackdouble str2; str2.Push(1.1); str2.Push(1.1); str2.Push(1.1); return 0; }灵魂拷问1template定义类中数组的类型类似于typedef那为什么不使用typedef呢typedef只能定义一种确定的类型不能进行随意的切换。2为什么C中不能使用realloc?当原来的空间不够时realloc会在一片新的地址开辟新空间然后将旧字节原封不动的拷贝过来旧内存空间归还给处理器旧指针会失效找不到原来的空间。如果在旧内存当中开辟了资源找不到原空间就无法调用析构函数进行资源的释放造成内存泄漏。就算提前定义一个变量记录旧地址依旧不能调用析构函数进行释放这是因为realloc在拷贝字节后新对象的内存处于“假活”状态。即由于realloc是一个一个字节的拷贝新对象内部的指针依旧指向久空间已经被释放的地址之后访问新对象时对象的指针就是“悬挂指针”即指向无效内存的指针 。所以形成了死结使不使用析构都不行而且偏偏类会自己调用析构。3析构中的delete会不会和Push中的delete造成冲突答不会具体原因如下补充注意在类模板当中进行声明在类模板外面定义时需要重新加上template并且模板不支持声明和定义放在不同的文件当中3,类模版的内存开辟和释放按照1-2-3-4的步骤执行比较两种情况四STL1了解STL1,STL(标准模版库)是C标准库的重要组成部分不仅是一个可以复用的组件库还是一个数据结构和算法的软件框架STL的重要版本SGI版本了解由Silicon Graphics Computer SystemsInc公司开发继承自HP版 本。被GCC(Linux)采用可移植性好可公开、修改甚至贩卖从命名风格和编程 风格上看阅读性非常高。我们后面学习STL要阅读部分源代码主要参考的就是这个版本。2STL的六大组件五补充C当中的隐式类型转换隐式类型转换分为两种1自动类型转换 2强制类型转换1自动类型转换特点1发生在兼容类型之间可以进行数值转化2小范围自动转化为大范围类型char/short -int -long - float -double3安全进行转换不丢失数据4遵循算术提升原则转化展示2强制类型转换显式转换主动标明要进行成为的类型1强制类型转换可以对不兼容的数据进行转换但是可能会出错2可以跳过范围的限制但是大范围转换为小范围可能会丢失精度这里的65转化成16进制就是0x41在内存当中由小到大就是 41 00 00 00,char刚好只取第一个字节取到41转化为对应的字符就是AASCII码值为2的字符为控制字符无法进行打印所以就不会显示值。3运算符重载中的隐式转换演示//运算符重载中的隐式转换 class complex { private: double real, image; public: complex(double i 0 , double j 0) :real(i) ,image(j) {} //const函数不会修改对象中的变量real,image complex operator(const complex tr)const { return complex(real tr.real, image tr.image); } void print()const { cout real image i endl; } }; int main() { complex r1 complex(2, 3); complex r2 r1 4; //4转换为complex类型complex(40 r2.print();// 63i //相当于下面 complex r3 r1 complex(4, 0); r3.print();//63i return 0; }4用户定义转换运算符演示class Time { private: int hour, minutes, seconds; public: Time(int a 0, int b 0, int c0) :hour(a) ,minutes(b) ,seconds(c) { } //Time转换为int - 转换为秒 //类型转换运算符不需要返回值 //因为int即是函数名的一部分也是返回值类型 operator int()const { return hour * 3600 minutes * 60 seconds; } //Time转换为double - 转换为小时 operator double()const { //60和60.0不同 带小数和不带小数不同带小数计算后的值是double类型带小数 //不带小数就是取整数 return hour minutes / 60.0 seconds / 3600.0; } void Push()const { cout hour minutes seconds endl; } }; int main() { Time t1 Time(3, 100, 300); //隐式转换 Time-int int r1 t1; cout r1 endl; //隐式转换 Time-double double r2 t1; cout r2 endl; //显式转换 int r3 (int)t1; cout r3 endl; return 0; }运行结果171004.7517100