C++动态内存管理——new/delete

1.c语言的动态内存管理

  • malloc:动态开辟指定大小的空间,返回值是void* ,所以要自己指定返回的数据类型
    • int *ptr = (int*) malloc (10*sizeof(int));
  • calloc:动态开辟指定大小的空间,与malloc不同的是它会进行初始化
    • int* ptr = (int*)calloc(10,sizeof(int));
  • realloc: 为动态开辟的空间调整大小,如果原空间后有足够大的空间,之间在后面开辟。如果原空间后的空间不够大,它会开辟一块空间,将原来的数据拷贝过来,并释放原来的空间

int *ptr = (int*) malloc (10*sizeof(int));

realloc(ptr,100*sizeof(int));

  • free:动态开辟的空间一定要记得释放,否则可能导致内存泄漏(指针丢了,内存还在)

动态开辟的内存都在堆上,堆上的空间由程序员手动开辟,手动释放。

2.c++的动态内存管理:

c++通过new/delete动态管理内存

  • new/delete动态管理对象
  • new[]/delete[]动态管理对象数组
  • new做了俩件事:
  1. 调用operator new分配空间
  2. 调用了构造函数初始化对象
  • delete做了俩件事
  1. 调用析构函数权力对象
  2. 调用operator delete释放空间
  • new[N]
  1. 调用operator new分配空间
  2. 调用N次构造函数分别初始化每个对象
  • delete[N]
  1. 调用N次析构函数清理对象
  2. 调用operator delete释放空间
void Test()
{
	int *p1 = new int;//动态分配4字节(1个int)的空间单个数据
	int *p2 = new int(3);//动态分配4字节(1个int)的空间并初始化
	int *p3 = new int[3];//动态开辟12个字节(3个int)的空间

	delete p1;
	delete p2;
	delete[] p3;
}

注意:malloc和free,new/delete,new[]/delete[]要配对使用,否则可能出现内存泄漏甚至崩溃的问题

问:为什么c++不能使用mallco/free来动态管理内存呢?

答:c语言是面向过程的语言,如果函数出错返回错误码(malloc/free出错返回错误码)

c++面对对象编程,出错会抛异常

3.c++的其他内存管理接口

  • void* operator new(size_t size);
  • void operator delete(size_t size);
  • void *operator new[](size_t size);
  • void operator delete[](size_t size);

标准库对函数operator new和operator delete的命名容易让人误解,与其他operator函数(如operator=)不同,他这些函数并不是对new和delete的重载,只是malloc和free的 一层封装,实际上我们不能对new和delete重载

  1. operator new/operator delete;operator new[]/operator delete[]和malloc/free用法一样
  2. 他们只负责分配空间,不会调用对象构造函数/析构函数来初始化/清理对象
  3. 实际上operator new和operator delete只是malloc和free的 一层封装

4.maollc/free与new/delete的区别,联系

  • 他们都是动态管理内存的入口
  • malloc/free是函数,new/delete是操作符
  • malloc要手动计算开辟空间的带下,new开辟空间的大小由系统计算
  • malloc/free不调用构造/析构函数,new/delete调用构造函数/析构函数进行初始化和函数清理
  • malloc失败返回错误码,new失败抛异常

5.malloc/free,new/delete,new[]/delete[]不匹配使用会出现哪些问题?

1)内置类型

  • 不匹配使用也不会出现程序出错、崩溃等问题
  • 他们不需要调用构造/析构函数,不用保存count,所以不会出现访问内存出错的问题

2)自定义类型:

  • 不调用构造函数,不会崩溃
  • 调用构造函数,会崩溃,因为要保存count ,容易出现内存访问出错
  • 编译器会自动优化,如果自定义类型中没有定义构造/析构,或者构造/析构函数内部什么都没有做,就不会多开辟用来存储count的4个字节

以下为内置类型的验证(不匹配使用不会出错,但是我们仍建议配对使用):


void Test()
{
	int *p1 = new int;
	int *p2 = new int(3);
	int *p3 = new int[3];
	int *p4 = (int*)malloc(sizeof(int));

	delete[] p1;
	//free(p1);
	delete p2;
	//free(p2);
	//delete p3;
	free(p3);
	delete p4;
}

自定义类型:

class AA
{
public:
	AA(size_t size = 10)
		:_size(size)
		, _a(0)
	{
		cout << "AA()" << endl;
	}
	~AA()
	{
		cout << "~AA()" << endl;
		if (_a)
		{
			delete[] _a;
			_a = 0;
			_size = 0;
		}
	}
private:
	int* _a;
	size_t _size;
};

 情况一:new

void Test()
{
	AA* p1 = new AA;

	delete p1;
        //正确
	//delete[] p1;
        //错误,因为delete[]对应new[],new[]在开辟空间的时候多加了4,delete[]就会-4向前面四个字节去取,所以会出错
	free(p1);
        //正确,但是没有调用析构函数
}

情景二: new(3),和情景一情况一样,只是多了初始化

void Test()
{
	AA* p2 = new AA(3);

	delete p2;
        //正确
	//delete[] p2;
        //程序出错,因为delete[]对应new[],new[]在开辟空间的时候多加了4,delete[]就会-4向前面四个字节去取,所以会出错
	//free(p2);
        //正确,但是没有调用析构函数
}

情景三:

void Test()
{
	AA* p1 = new AA;
	AA* p2 = new AA(3);
	AA* p3 = new AA[3];

	delete p1;
	delete p2;
	delete[] p3;
        //正确
	//delete p3;
//错误,delete直接跳过开始4字节从后面开始释放,所以前面的四个字节并没有释放,delete[]从new[]多创建的4个字节处开始释放
	//free(p3);
        //程序崩溃
}

 场景四:

void Test()
{

	AA* p4 = (AA*)malloc(sizeof(AA));

	free(p4);//正确
	delete p4;//析构函数什么都不做时正确
	delete[] p4;//错误
}

猜你喜欢

转载自blog.csdn.net/audience_fzn/article/details/81452345