C++之动态内存分配 new/delete


提到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可自己计算类型的大小,返回对应类型的指针。


我们都知道C和C++定义的 内存空间有:
1、栈区(stack)一一局部变量区                                                        
2、堆区(heap)一一动态存储区         
3、全局区(static静态区)—存放全局变量、静态数据、常量。程序结束后由系统释放。
4、文字常量区 一一常量字符串就是放在这里的。 程序结束后由系统释放。
5、代码区一一存放函数体(类成员函数和全局函数)的二进制代码。

new的两种形式:
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[]一定匹配使用!!!否则可能出现内存泄露甚至崩溃的问题。
深层次了解:
上面提到new和delete只是运算符,并不是函数。那么它是通过什么来动态地去申请内存的呢?

operator new 和 operator delete    这两个 标准库函数
其原型 
void *operator new(size_t);        //   注意不是重载!,new和delete不能被重载
void *operator delete(void *);  
void *operator new[](size_t);   
void *operator delete[](void *);

new做了两件事
 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的一层封装。
代码块:
#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次析构函数(晚创建的早释放)



定位New表达式
placement new/delete 函数
概念  在已分配的原始内存中初始化一个对象,它与 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++时候我对动态内存的一点理解,如有不对还望批评指正。


猜你喜欢

转载自blog.csdn.net/komacc/article/details/80109718