C++内存管理方式 (new和delete的操作)

C++内存管理方式

C语言内存管理方式(malloc, realloc, calloc, free等函数)在C++中可以继续使用,但有些地方就无能为力而且使用起来比较麻烦

我们先来看一段代码, 创建自定义对象:

class A{
public:
	// 无参构造和全缺省的构造都是默认构造
	A(int a = 1, int b = 2, int c = 3)
	//A(int a, int b, int c)
		: _a(a)
		, _b(b)
		, _c(c){
		cout << "A(int, int, int)" << endl;
	}
private:
	int _a;
	int _b;
	int _c;
};
void test2(){
	// 第一种
	A* pa = (A*)malloc(sizeof(A));// 堆上 值都为随机值
	// 第二种
	A obj(1, 2, 3);// 栈上 掉用了构造函数

	// pa->_a = 1; // 不行
	// pa(4, 5 , 6);// 也不行
	// A* pa = (A*)malloc(sizeof(A))(4, 5, 6);// 也不行

第一种:

  • 对于第一种用malloc创建对象来说, 他只是在堆上为对象开辟了空间, 而里面的内容并没有初始化, 也没有调构造函数来创建对象.
  • 当类中的成员变量为private时, 不能对成员变量进行赋值, 也不能调用构造函数, 相当于创建的这个对象是一个不能使用的对象

第二种

  • 对于第二种调构造函数创建对象来说, 却是在栈上创建的对象, 并且完成了初始化

那么有没有一种方式既可以在堆上创建对象, 也可以调构造函数呢??? 所以C++给了新的操作符来完成这一任务.

1 new操作

我们先来看看这种新的操作符堆上申请内存空间的使用方式:

单个空间: 相当于申请一个值的空间

  • new + 数据类型(初始值)

连续空间 : 相当于申请一个数组的空间

  • new + 数据类型[元素个数]
void Test(){
	 // 在堆上动态申请一个int类型的空间
	 int* p = new int;
 
	 // 在堆上动态申请一个int类型的空间并初始化为2
	 int* p2 = new int(2);
 
	 // 在堆上动态申请10个连续的int类型的整形空间
	 int* p3 = new int[3];
 
 	 // 自定义类型: new --> 申请空间 + 调用构造函数初始化
	 A* pa2 = new A(4, 5, 6); // 相比于前面的pa, 这里的值已经赋好了, 自动调用了构造函数

}
  • 由此也可以看出, 在创建自定义类型的对象时, malloc只是申请空间, 并不会调用构造函数, 而new则是既申请了空间, 同样也调了构造函数, 还可以对对象进行初始化操作,
  • new创建对象和在栈上创建对象都可以一步完成, 都可以开好空间并初始化好, 区别在于在栈上开辟空间是系统帮我们自动完成的, 而堆上需要我们手动去开空间 并且堆上也需要手动释放空间

2. delete操作

跟new对应的释放就是delete操作符, 我们来看一下delete释放

void Test(){
     // 内置类型
	 int* p = new int;
	 int* p2 = new int(2);
	 int* p3 = new int[3];
	 // 自定义类型对象
	 A* pa2 = new A(4, 5, 6);

	 // 释放内置类型空间
	 delete p; // 释放单个元素的空间
	 delete p2;
	 delete[] p3; // 释放连续空间
	 // 释放自定义类型对象空间
	 delete pa2;

注意: new 和 delete 的使用必须一一对应,

  • 申请和释放单个元素的空间,使用new和delete操作符, new —> delete
  • 申请和释放连续的空间,使用new[]和delete[] , new [ ] —> delete[ ]+指针

我们都知道对象销毁会调析构函数资源, 空间还给系统, 如果是构造了n个对象, 那就会调n次析构函数来完成资源的清理, 在用new创建自定义类型的对象时, 会调构造函数; 而用delete释放new所开辟的空间时, 同样也调了析构函数来完成自定义类型对象的销毁以及清理资源, 现在再去看释放自定义类型对象时, delete就起到了既释放空间 又调用析构清理资源

	 A* pa2 = new A(4, 5, 6); // 申请空间 + 调构造函数初始化
	 delete pa2;  // 释放空间 + 清理资源

当用new[]创建了n个自定义对象时, 用delete[]释放空间时, 就会调n次析构函数

class A{
public:
	// 如果这里不是默认构造 new是调不了构造函数的,
	A(int a = 1, int b = 2, int c = 3)
		: _a(a)
		, _b(b)
		, _c(c){}
	A(const A& A){
		cout << "A(const A&)" << endl;
	}
	~A(){
		cout << "~A()" << endl;
	}
private:
	int _a;
	int _b;
	int _c;
};
void test(){
	// 栈上创建对象数组
	A array[10]; 构造100个A 类型的对象, 
	
	// 堆上动态创建
	cout << "heap creat: " << endl;
	// 动态申请自定义类型的连续空间,需要有默认构造函数(包括无参构造和全缺省构造)
	A* array2 = new A[10];

	// delete array2; // 若用这个来释放, 就会导致程序崩掉, 调用不匹配, 没有与new[]对应 
	delete[] array2; // 销毁了10个A类型对象

}

那来个问题, 清理资源是在释放空间之前还是之后呢???

我们想一下, 空间释放了还能不能继续操作这个空间的资源呢, 答案是不行了, 因为空间释放后可能会被别人使用, 已经不属于我门的空间了, 我们也不能进行操作, 所以 我们只能在释放空间之前, 先调用析构函数 进行资源清理, 清理完之后再去释放空间, 这就是delete做的事情

从上面的new和delete操作, 我们可以明显发现, 这两个属于C++的操作符, 已经不再是函数操作了, 所以并不属于库函数, 而malloc和free是属于库函数的, 也可以发现, C++的内存管理的新方式写起来更简单一点 也不容易出错, 因为我们不需要去计算数据的大小, 也不需要做类型的转换,
new和delete只是在堆上的操作, 不管栈上的空间, 和malloc和free一样 只管堆上的空间, 所以不要用delete去释放栈上的空间, 就跟free去释放栈上的空间是一个道理,

需要注意的是动态申请自定义类型的连续空间时,必须要有默认构造函数(包括无参构造和全缺省构造); 申请一个对象的空间则没有要求 直接把参数传进去即可

	A* pa = new A(10, 9, 8);// 调带参构造
	A* pa1 = new A; // 调默认构造
	A* pcopy = new A(*pa);// 也可以调拷贝构造

总结:

1 内置类型:

  • new: 申请空间 , 也可以初始化(new 类型(初始值))
  • delete: 空间释放
  • new[] : 申请连续空间, 不可以初始化
  • delete[]: 释放连续空间

2 自定义类型:

  • new: 申请空间 + 调用构造函数 (可以是默认构造, 也可以是带参构造, 用户指定, 如果参数列表是一对象, 则调的就是拷贝构造)

  • delete: 调用析构 + 空间释放

  • new[] : 申请连续空间 + 调用默认构造

  • delete[] : 调用析构 + 释放连续空间

  • new/delete, new []/ delete [] 配套使用

  • 对于内置类型,此时delete就相当于free,不会造成内存泄漏

  • 如果是自定义类型, 不使用方括号就会运行时错误

  • 针对数组释放应使用delete[], delete如果没有使用[],只会调用一次析构函数,往往就会引发程序崩溃

  • 在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值