【C++】内存管理与模板(C++内存管理方式、new和delete的实现原理、malloc/free和new/delete的区别、函数模板、类模板)

【C++】内存管理与模板(C++内存管理方式、new和delete的实现原理、malloc/free和new/delete的区别、函数模板、类模板) 小编主页详情-请点击小编gitee代码仓库-请点击本文主要介绍了内存管理与模板C内存管理方式、new和delete的实现原理、malloc/free和new/delete的区别、函数模板、类模板内容全由作者原创无AI并带有配图帮助博友们更好的理解点个关注不迷路下面进入正文~~目录1.C内存管理方式1.1new/delete操作内置类型1.2 new和delete操作自定义类型1.3operator new与operator delete函数2. new和delete的实现原理2.1内置类型2.2自定义类型3. malloc/free和new/delete的区别4.函数模板5.类模板结语1.C内存管理方式在C语言中我们会使用malloc开辟空间用free销毁空间在C中我们依旧可以使用。但是在某些情况下譬如类的初始化和销毁会显得很乏力。因此C又引入了新的管理内存的方式通过new和delete进行动态内存管理。1.1new/delete操作内置类型int main() { int* p1 new int; // 动态申请一个int类型的空间 int* p2 new int[10]; // 动态申请10个int类型的空间 delete p1; delete[] p2; // int* p3 new int(0); // 动态申请一个int类型的空间并初始化为0 int* p4 new int[10]{ 0 }; // 动态申请10个int类型的空间并初始化为0 int* p5 new int[10]{1,2,3,4,5}; // 动态申请10个int类型的空间并初始化为1,2,3,4,5 delete p3; delete[] p4; delete[] p5; return 0; }注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],注意:匹配起来使用。1.2new和delete操作自定义类型在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。因此在操作自定义类型时我们会更推荐用new/delete实现毕竟能自动调用构造函数和析构函数还是相当方便的。class A { public: A(int a 0) : _a(a) { cout A(): this endl; } ~A() { cout ~A(): this endl; } private: int _a; }; int main() { // new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间 // 还会调用构造函数和析构函数 A* p1 (A*)malloc(sizeof(A)); A* p2 new A(1); free(p1); delete p2; // 内置类型是几乎是一样的 int* p3 (int*)malloc(sizeof(int)); // C int* p4 new int; free(p3); delete p4; A* p5 (A*)malloc(sizeof(A) * 10); A* p6 new A[10]; free(p5); delete[] p6; return 0; }1.3operator new与operator delete函数new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果该应对措施用户设置了,则继续申请,否则抛异常。operator delete 最终是通过free来释放空间的。总的来说operator new和operator delete本质上都是通过调用malloc和free来进行开辟和释放空间的。2.new和delete的实现原理2.1内置类型如果申请的是内置类型的空间new/malloc和delete/free其实差距不大。需要注意的是new/delete申请和释放的是单个元素的空间而new[]和delete[]申请和释放的是连续空间并且new在申请空间失败时会抛异常而malloc申请空间失败会返回NULL。2.2自定义类型new的原理1.调用operate new函数申请空间2.在申请的空间上调用构造函数完整对象的构造delete的原理1.在空间上执行析构函数完成对象的资源清理工作2.调用operate delete函数释放对象的空间new T[N]的原理1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请2. 在申请的空间上执行N次构造函数delete[]的原理1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间3.malloc/free和new/delete的区别malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:1. malloc和free是函数,new和delete是操作符2. malloc申请的空间不会初始化,new可以初始化3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放4.函数模板在我们进行两个数据swap交换时会因为需要交换的数据类型有很多种导致我们需要写很多种数据类型的swap交换函数。函数模板可以通过传入数据的类型生成对应类型的函数。#define _CRT_SECURE_NO_WARNINGS #include iostream using namespace std; templateclass T void Swap(T left, T right) { T tmp left; left right; right tmp; } int main() { int a 1; int b 2; Swap(a, b); cout a b endl; double c 1.1; double d 2.2; Swap(c, d); cout c d endl; return 0; }如这段代码当我们传入int类型数据时编译器会判断T的类型就是int当我们传入double类型数据时编译器会判断T的类型就是double。此时编译器会自动生成参数分别位int和double的函数本质上就是把我们要写多种相似代码的工作交给了编译器。当然函数模板参数可以有很多个比如T1、T2、T3下面是函数模板的格式templatetypename T1, typename T2,......,typename Tntypename是用来定义模拟参数的关键字也可以使用class当模板参数只有一个我们传入两个不一样的参数编译器无法判断T的类型是什么就会报错。那如果我们就是想在模板参数只有一个时让编译器不报错要怎么做呢1.用户自己强制类型转化2.使用显示实例化// 用函数模版生成对应的函数 - 模版的实例化 templateclass T T Add(const T left, const T right) { return left right; } templateclass T1, class T2 T1 Add(const T1 left, const T2 right) { return left right; } templateclass T T* func1(int n) { return new T[n]; } int main() { int a1 10, a2 20; double d1 10.1, d2 20.2; Add(a1, a2); Add(d1, d2); // 推导实例化 cout Add(a1, (int)d1) endl; cout Add((double)a1, d1) endl; // 显示实例化 cout Addint(a1, d1) endl; cout Adddouble(a1, d1) endl; //用两个模板参数实现 cout Add(a1, d1) endl; double* p1 func1double(10); return 0; }当一个非模板函数和模板函数同时存在时如果类型匹配会优先调用非模板函数。5.类模板类模板和函数模板的使用其实没有什么区别主要的区别是类模板只能显示实例化不然无法判断T的类型是什么。类模板的定义格式如下templateclass T1, class T2, ..., class Tn class // 类模板名 { // 类内成员定义 };下面是用类模板定义的一个栈templatetypename T class Stack { public: Stack(int n 4) :_array(new T[n]) ,_size(0) ,_capacity(n) {} ~Stack() { delete[] _array; _array nullptr; _size _capacity 0; } void Push(const T x); private: T* _array; size_t _capacity; size_t _size; }; templateclass T void StackT::Push(const T x) { if (_size _capacity) { T* tmp new T[_capacity * 2]; memcpy(tmp, _array, sizeof(T) * _size); delete[] _array; _array tmp; _capacity * 2; } _array[_size] x; } int main() { // 类模板都是显示实例化 Stackint st1; // int st1.Push(1); st1.Push(2); st1.Push(3); Stackdouble st2; // double st2.Push(1.1); st2.Push(1.1); st2.Push(1.1); Stackdouble* pst new Stackdouble; //... delete pst; return 0; }模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误具体原因后面会讲Stack是类名Stackint才是类型结语这篇文章全文由作者手写图片由画图软件所制无AI制作希望各位博友能有所收获欢迎各位博友的讨论觉得不错的小伙伴别忘了点赞关注哦~