提到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++时候我对动态内存的一点理解,如有不对还望批评指正。