
目录一. C/C内存分布二. C语言中动态内存管理方式三. C内存管理方式3.1 new/delete 操作内置类型3.2 new/delete 操作自定义类型四. operator new与operator delete函数五. new和delete的实现原理5.1 内置类型5.2 自定义类型六. 定位new表达式(placement-new)七. malloc/free和new/delete的区别八. 泛型编程九. 函数模板9.1 函数模板的概念9.2 函数模板格式9.3 函数模板的实例化9.4 模板参数的匹配规则十. 类模板10.1 类模板的定义格式10.2 类模板的实例化一. C/C内存分布栈又叫堆栈——非静态局部变量/函数参数/返回值等等栈是向下增长的。内存映射段是高效的 I/O 映射方式用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存做进程间通信。Linux 课程如果没学到这块现在只需要了解一下堆用于程序运行时动态内存分配堆是可以上增长的。数据段——存储全局数据和静态数据。代码段——可执行的代码/只读常量。二. C语言中动态内存管理方式三. C内存管理方式解答 C语言内存管理方式在C中可以继续使用但有些地方就无能为力而且使用起来比较麻烦因此C又提出了自己的内存管理方式通过new和delete操作符进行动态内存管理。3.1 new/delete 操作内置类型#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; int main() { //动态申请一个int类型的空间 int* ptr4 new int; //动态申请一个int类型的空间并初始化为10 int* ptr5 new int(10); //动态申请10个int类型的空间 int* ptr6 new int[10]; delete ptr4; delete ptr5; delete[] ptr6; return 0; }3.2 new/delete 操作自定义类型#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class A { public: A(int a1 0,int a2 0) :_a1(a1) ,_a2(a2) { cout A(int a1 0,int a2 0) endl; } A(const A aa) :_a1(aa._a1) ,_a2(aa._a2) { cout A(const A aa) endl; } ~A() { this-_a1 0; cout ~A() endl; } A operator(const A aa) { this-_a1 aa._a1; this-_a2 aa._a2; cout A operator(const A aa) endl; } private: int _a1; int _a2; }; int main() { A* p1 new A; A* p2 new A(1); A* p3 new A(2, 3); A aa1(1, 2); A aa2(2, 3); A aa3(3, 4); //1.隐式类型转换 A* p4 new A[3]{ {1,2},{2,3},{4,5} }; //2.匿名对象 A* p4 new A[3]{ A(1,2),A(2,3),A(4,5) }; //3.先构造对象再作参数 A* p4 new A[3]{ aa1,aa2,aa3 }; delete p1; delete p2; delete p3; delete[] p4; return 0; }注意在申请自定义类型的空间时new 会调用构造函数delete 会调用析构函数而 malloc 与 free 不会。四. operator new与operator delete函数五. new和delete的实现原理5.1 内置类型如果申请的是内置类型的空间new 和 mallocdelete 和 free 基本类似不同的地方是 new/delete 申请和释放的是单个元素的空间new[] 和 delete[] 申请的是连续空间而且 new 在申请空间失败时会抛异常malloc 会返回 NULL。5.2 自定义类型new 的原理调用 operator new 函数申请空间在申请的空间上执行构造函数完成对象的构造delete 的原理在空间上执行析构函数完成对象中资源的清理工作调用 operator delete 函数释放对象的空间new T[N] 的原理调用 operator new[] 函数在 operator new[] 中实际调用 operator new 函数完成 N 个对象空间的申请在申请的空间上执行 N 次构造函数delete[] 的原理在释放的对象空间上执行 N 次析构函数完成 N 个对象中资源的清理调用 operator delete[] 释放空间实际在 operator delete[] 中调用 operator delete 来释放空间class B { public: B(int a110) :_a1(a1) { cout B(int a110) endl; } ~B() { cout ~B() endl; } private: int _a1; }; int main() { int* AA new int[10]; delete AA; B* _AA new B[10]; delete _AA; return 0; }对于内置类型数组比如 int* p new int[10]; 如果误用 delete p在 VS 下可能不报错也可能看不出内存泄漏因为 int 不涉及析构。但从 C 标准上说new[] 必须配 delete[]否则是未定义行为。对于类类型数组如果类型需要非平凡析构编译器可能会在数组前面额外存储元素个数。delete[] 会利用这些信息调用多次析构并释放正确的整块空间。如果误用 delete可能只析构一个对象并且把错误的位置交给 operator delete导致报错或堆损坏。六. 定位new表达式(placement-new)定位 new 表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。使用格式new (place_address) type 或者 new (place_address) type(initializer-list)place_address 必须是一个指针initializer-list 是类型的初始化列表使用场景定位 new 表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化所以如果是自定义类型的对象需要使用 new 的定位表达式进行显示调用构造函数进行初始化。#define _CRT_SECURE_NO_WARNINGS #includeiostream using namespace std; class B { public: B(int a110) :_a1(a1) { cout B(int a110) endl; } ~B() { cout ~B() endl; } private: int _a1; }; int main() { //1 B* p1 new B(1); delete p1; //2 B* p2 (B*)operator new(sizeof(B)); new(p2)B(1); p2-~B(); operator delete(p2); return 0; }七. malloc/free和new/delete的区别malloc/free 和 new/delete 的共同点是都是从堆上申请空间并且需要用户手动释放。不同的地方是malloc 和 free 是函数new 和 delete 是操作符malloc 申请的空间不会初始化new 可以初始化malloc 申请空间时需要手动计算空间大小并传递new 只需在其后跟上空间的类型即可如果是多个对象[] 中指定对象个数即可malloc 的返回值为 void*在使用时必须强转new 不需要因为 new 后跟的是空间的类型malloc 申请空间失败时返回的是 NULL因此使用时必须判空new 不需要但是 new 需要捕获异常申请自定义类型对象时malloc/free 只会开辟空间不会调用构造函数与析构函数而 new 在申请空间后会调用构造函数完成对象的初始化delete 在释放空间前会调用析构函数完成空间中资源的清理释放八. 泛型编程九. 函数模板9.1 函数模板的概念函数模板代表了一个函数家族该函数模板与类型无关在使用时被参数化根据实参类型产生函数的特定类型版本。9.2 函数模板格式templatetypename T1,typename T2,......,typename Tn返回类型 函数名参数列表{}templatetypename T1,typename T2 void func(const T1 x, const T2 y) { T1 tmp x; x y; y tmp; }注意typename是用来定义模板参数关键字也可以使用 class切记不能使用 struct 代替 class9.3 函数模板的实例化用不同类型的参数使用函数模板时称为函数模板的实例化。模板参数实例化分为隐式实例化和显式实例化。隐式实例化让编译器根据实参推演模板参数的实际类型templatetypename T1 T1 func(const T1 x, const T1 y) { return x y; } int main() { int a1 10, a2 20; double b1 10.0, b2 20.0; func(a1, a2); func(b1, b2); //类型不统一办法1-强制类型转换 cout func(a1,(int) b1) endl; return 0; }显式实例化在函数名后的中指定模板参数的实际类型templatetypename T1 T1 func(const T1 x, const T1 y) { return x y; } int main() { int a1 10, a2 20; double b1 10.0, b2 20.0; func(a1, a2); func(b1, b2); //类型不统一办法1-强制类型转换 cout func(a1,(int) b1) endl; //办法二-显示实例化 cout funcint(a1, b1) endl; return 0; }如果类型不匹配编译器会尝试进行隐式类型转换如果无法转换成功编译器将会报错。9.4 模板参数的匹配规则一个非模板函数可以和一个同名的函数模板同时存在而且该函数模板还可以被实例化为这个非模板函数。对于非模板函数和同名函数模板如果其他条件都相同在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数那么将选择模板。模板函数不允许自动类型转换普通函数允许自动类型转templatetypename T1 T1 func(const T1 x, const T1 y) { return x y; } int func(const int x, const int y) { return (x y) * 10; } int main() { int a1 10, a2 20; cout func(a1, a2) endl; return 0; }十. 类模板10.1 类模板的定义格式templateclass T1,class T2,.......class Tn class 类模板名 { //类内成员定义 };templateclass T class stack { public: stack(int n4) :_array(new T[n]) ,_size(0) ,_capacity(n) {} ~stack() { delete[] this-_array; this-_array nullptr; this-_size this-_capacity 0; } void Push(const T x); private: T* _array; int _capacity; int _size; }; templateclass T void stackT::Push(const T x) { if (_size _capacity) { int _newcapcity 2 * _capacity; T* tmp new T[_newcapcity]; memcpy(tmp, _array, sizeof(T) * _size); delete[] _array; _array tmp; tmp nullptr; _capacity _newcapcity; } _array[_size] x; _size; } int main() { //类模板都是显式实例化 stackint st1; st1.Push(1); st1.Push(2); st1.Push(3); stackdouble st1; st1.Push(1.1); st1.Push(2.2); st1.Push(3.3); return 0; }10.2 类模板的实例化类模板实例化与函数模板实例化不同类模板实例化需要在类模板名字后跟 然后将实例化的类型放在 中即可类模板名字不是真正的类而实例化的结果才是真正的类。//stack是类名stackint才是类型 stackint st1; // int stackdouble st2; // double