提到C语言的内存分配,我们自然而然会想到标准库函数中的 malloc 和 free 来动态分配内存空间
而在C++中,我们则是提供了运算符 new 和 delete ,来取代 malloc 和 free 进行动态分配内存空间。
malloc/free和new/delete的区别和联系?
1. 它们都是动态管理内存的入口。
2. malloc/free是C/C++标准库的函数,new/delete是C++操作符。
3.malloc/free只是动态分配内存空间/释放空间。而new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理
4. malloc/free需要手动计算类型大小且返回值会void*,new/delete可自己计算类型的大小,返回对应类型的指针。
1. 它们都是动态管理内存的入口。
2. malloc/free是C/C++标准库的函数,new/delete是C++操作符。
3.malloc/free只是动态分配内存空间/释放空间。而new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理
4. malloc/free需要手动计算类型大小且返回值会void*,new/delete可自己计算类型的大小,返回对应类型的指针。
我们都知道C和C++定义的
内存空间有:
1、栈区(stack)一一局部变量区
2、堆区(heap)一一动态存储区
3、全局区(static静态区)—存放全局变量、静态数据、常量。程序结束后由系统释放。
4、文字常量区 一一常量字符串就是放在这里的。 程序结束后由系统释放。
5、代码区一一存放函数体(类成员函数和全局函数)的二进制代码。
2、堆区(heap)一一动态存储区
3、全局区(static静态区)—存放全局变量、静态数据、常量。程序结束后由系统释放。
4、文字常量区 一一常量字符串就是放在这里的。 程序结束后由系统释放。
5、代码区一一存放函数体(类成员函数和全局函数)的二进制代码。
new的两种形式:
1.new/delete动态管理对象。
2.new[]/delete[]动态管理对象数组。
1.new/delete动态管理对象。
2.new[]/delete[]动态管理对象数组。
代码块:
int* p4 = new int; // 动态分配4个字节(1个 int)的空间单个数据 int* p5 = new int(3); // 动态分配4个字节(1个 int)的空间并初始化为3 int* p6 = new int[3]; // 动态分配12个字节(3个 int)的空间 delete p4 ; delete p5 ; delete[] p6 ;
注意:
new运算符返回的是一个指向所分配类型变量(对象)的指针。
malloc/free、new/delete、new[]/delete[]一定匹配使用!!!否则可能出现内存泄露甚至崩溃的问题。
malloc/free、new/delete、new[]/delete[]一定匹配使用!!!否则可能出现内存泄露甚至崩溃的问题。
深层次了解:
上面提到new和delete只是运算符,并不是函数。那么它是通过什么来动态地去申请内存的呢?
operator new 和 operator delete 这两个
标准库函数
其原型
void *operator new(size_t); // 注意不是重载!,new和delete不能被重载
void *operator delete(void *);
void *operator new(size_t); // 注意不是重载!,new和delete不能被重载
void *operator delete(void *);
void *operator new[](size_t);
void *operator delete[](void *);
void *operator delete[](void *);
new做了两件事
1.调用operator new函数分配空间,把malloc放入死循环申请空间。//operator new其实是对malloc的封装
2.调用构造函数初始化对象。
1.调用operator new函数分配空间,把malloc放入死循环申请空间。//operator new其实是对malloc的封装
2.调用构造函数初始化对象。
delete也做了两件事
1.调用析构函数清理对象
2.调用operator delete函数释放空间
new[N]
1.调用operator new分配空间。
2.调用 N次构造函数分别初始化每个对象。
delete[]
1.调用N次析构函数清理对象。
2.调用operator delete释放空间
1. operator new/operator delete , operator new[]/operator delete[] 和malloc/free用法一样。
2. 它们只负责分配空间/释放空间,不会调用对象构造函数,析构函数来初始化或清理对象。
3. 实际operator new和operator delete只是malloc和free的一层封装。
2. 它们只负责分配空间/释放空间,不会调用对象构造函数,析构函数来初始化或清理对象。
3. 实际operator new和operator delete只是malloc和free的一层封装。
代码块:
#include<stdio.h> #include<iostream> using namespace std; class Array { public: Array(size_t size = 10) : _size(size) { cout << "构造函数:Array()" << endl; } ~Array() { cout << "析构函数:~Array()" << endl; } private: size_t _size; }; void Test() { Array* p2 = new Array; Array* p3 = new Array(20); Array* p4 = new Array[3];//调用3次构造函数分别初始化每个对象。 delete p2; delete p3; delete[] p4;//调用N次析构函数清理对象。 } int main() { Test(); system("pause"); return 0; }
从运行结果来看, 一共调用了五次构造函数和析构函数。
其中这个new test[N]编译器做了几件事
1.调用operator new[ ](size_t)函数 //这里的参数size=sizeof(test)*N+4,前四个字节存放需要构造的次数
2.调用operator new(size)函数
3.malloc(size)
delete[]p4 也做了类似的事情
1.从p4前四个字节取调用析构函数的次数
2.调用N次析构函数(晚创建的早释放)
1.从p4前四个字节取调用析构函数的次数
2.调用N次析构函数(晚创建的早释放)
定位New表达式
placement new/delete 函数
概念 : 在已分配的原始内存中初始化一个对象,它与 new 的其他版本的不同之处在于,它不分配内存。相反,它接受指向已分 配但未构 造内存的指针,并在该内存中初始化一个对象。实际上,定位 new 表达式使我们能够在特定的、预分配的内存 地址构造一个对象。
概念 : 在已分配的原始内存中初始化一个对象,它与 new 的其他版本的不同之处在于,它不分配内存。相反,它接受指向已分 配但未构 造内存的指针,并在该内存中初始化一个对象。实际上,定位 new 表达式使我们能够在特定的、预分配的内存 地址构造一个对象。
形式:
new (place_address) type
new (place_address) type (initializer-list) //可以看作在 place_address处调用了构造函数初始化对象
返回值: place_address
其中 place_address 必须是一个指针,而 initializer-list 提供了(可能为空的)初始化列表,以便在构造新分配的对象时使用。
如何模拟实现new/delete new[]/delete[]
代码块:
#include<stdio.h> #include<iostream> using namespace std; class A { public: A(int a = 2) //在new重定位时候使用初始化列表了,所以这里的2就无效 :_a(a) { cout << _a << "次构造-->A()" << endl; } ~A() { cout << "~A()" << endl; } private: int _a; }; void Test() { //分配1个A类型空间 A *pa = (A *)malloc(sizeof(A)); new(pa)A(666); //调用构造函数 pa->~A(); //调用析构函数 free(pa); //分配N个A类型的空间 int N; cout << "请输入你要构造A类型空间的个数:"; cin >> N ; A* pb = (A *)malloc(sizeof(A)*N+4);//多余四个字节存放N *(int*)pb = N; //解引用pb指针 for (int i = 0; i < N; i++) //调用N次 { new(pb + i) A(i); //重定位new并调用构造函数 } for (int i = 0; i < N; i++) //调用N次析构函数 { (pb + i)->~A(); } free(pb); } int main() { Test(); system("pause"); return 0; }
以上大概就是在学习C++时候我对动态内存的一点理解,如有不对还望批评指正。