十一. C++动态对象创建:new与delete详解

本文详细介绍了C++中动态内存管理的相关知识点,包括malloc分配内存的问题,new运算符如何结合构造函数创建对象,delete运算符如何调用析构函数释放内存。特别强调了使用void*进行delete操作的潜在风险,以及数组new和delete操作的注意事项,如必须使用delete [] 来释放数组内存,以确保正确调用析构函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

malloc分配内存的问题

  1. 程序员必须确定对象的长度。
  2. malloc返回一个void*指针,C++不允许将void*赋值给其他任何指针,必须强转。
  3. malloc可能申请内存失败,所以必须判断返回值来确保内存分配成功。
  4. 用户在使用对象之前必须记住对他的初始化,构造函数不能显示调用初始化(构造函数由编译器调用),用户可能忘记调用初始化函数。

malloc实例

class Person{
public:
	Person(){
		mAge = 20;
		pName = (char*)malloc(strlen("john")+1);
		strcpy(pName, "john");
	}
	void Init(){
		mAge = 20;
		pName = (char*)malloc(strlen("john")+1);
		strcpy(pName, "john");
	}
	void Clean(){
		if (pName != NULL){
			free(pName);
		}
	}
public:
	int mAge;
	char* pName;
};
int main(){

	//分配内存
	Person* person = (Person*)malloc(sizeof(Person));
	if(person == NULL){
		return 0;
	}
	//调用初始化函数
	person->Init();
	//清理对象
	person->Clean();
	//释放person对象
	free(person);
	return EXIT_SUCCESS;
}

new分配内存

new operator

C++解决动态内存分配的方案是将创建一个对象所需要的操作均结合在一个称为new的运算符里。当用new创建一个对象时,它就在堆里为对象分配内存并调用构造函数完成初始化。

Person* person = new Person;
相当于:
Person* person = (Person*)malloc(sizeof(Person));
	if(person == NULL){
		return 0;
	}
person->Init();

delete operator

new表达式的反面是delete表达式。delete表达式先调用析构函数,然后释放内存。delete只适用于new创建的对象(malloc与free配套使用)

#define _CRI_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
public:
	Person() {
		cout << "默认构造调用" << endl;
	}
	Person(int a) {
		cout << "有参构造" << endl;
	}
	~Person()
	{
		cout << "析构函数调用" << endl;
	}
};

void test01()
{
	//Person p1;   栈区开辟
	Person* p2 = new Person;   //堆区开辟
	//所有new出来的对象 都会返回该类型的指针
	//malloc返回void* 还要强转
	//malloc 会调用构造吗?不会   new会调用构造
	//new 运算符, malloc为函数
    //释放 堆区空间 delete
	delete p2;    //delete也是运算符 配合new用,malloc配合free用
}

int main()
{
	test01();
	return 0;
}

delete void*可能会出错

如果对一个void*执行delete操作,可能会是一个程序错误,除非指针指向的内容是非常简单的,因为其不执行析构函数,以下代码未调用析构函数,导致可用内存减少。因此也void *接受new出来的指针,会出现释放问题。

#define _CRI_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
public:
	Person() {
		cout << "默认构造调用" << endl;
	}
	Person(int a) {
		cout << "有参构造" << endl;
	}
	~Person()
	{
		cout << "析构函数调用" << endl;
	}
};

void test02()
{
	Person* p = new Person;
	//当用void *接受new出来的指针,会出现释放的问题  delete p;无法释放
	delete p;
}

int main()
{
	test02();
	return 0;
}

用于数组的new和delete

通过new开辟数组 一定会调用默认构造函数,所以一定要提供默认构造函数
Person* pArray = new Person[10];             //在堆区必须使用默认构造,不可以指定有参构造 

Person pArray2[2] = { Person(1), Person(2) };//在栈上开辟数组,可以指定有参构造

//释放数组

 delete [] pArray;  //数组释放  一定 要加 [],否则会出错

#define _CRI_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
public:
	Person() {
		cout << "默认构造调用" << endl;
	}
	Person(int a) {
		cout << "有参构造" << endl;
	}
	~Person()
	{
		cout << "析构函数调用" << endl;
	}
};

void test03()
{
	//通过new开辟数组 一定会调用默认构造函数,所以一定要提供默认构造函数
	Person* pArray = new Person[10];             //在堆区必须使用默认构造,不可以指定有参构造 

	Person pArray2[2] = { Person(1), Person(2) };//在栈上开辟数组,可以指定有参构造

	//释放数组
	delete [] pArray;  //数组释放  一定 要加 [],否则会出错

}
int main()
{
	test03();
	return 0;
}

delete数组为加[]的问题

Person* person = new Person[10];
delete person;

以上代码是用了new,也搭配了delete。但问题在于Person有是个对象,那么此时其余9个可能没有调用析构函数,也就是其余9个对象可能删除不完全,因此其析构函数没有被调用。

我们知道调用new和delete发生了如下事件:

  • new: 一. 分配内存;二. 调用构造函数
  • delete: 一. 析构函数;二. 释放内存

而上段代码最大的问题在于:person指针指向的内存中到底有多少个对象,因为这决定多少个析构函数应该被调用。换句话说,person指针指向的是一个单一的对象还是一个数组对象,由于单一对象和数组对象的内存布局不一样。明确确的说,数组所用的内存通常还包括“数组大小记录”,使得delete的时候知道应该调用几次析构函数。单一对象无此记录。可以如下图理解:

因此,当我们使用delete时,我们必须要让delete知道指针指向的内存空间中是否存在一个“数组大小记录”的方法就是我们告诉delete。而当我们使用delete [] 时,delete就知道是一个数组对象,从而清楚应该调用几次析构函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值