new、delete与malloc、free的区别,内在实现。

本文对比了C++中的new/malloc和delete/free在内存分配、构造析构函数执行、返回类型和错误处理上的差异,以及它们在自定义类型和数组对象上的适用场景。特别强调了配套使用的重要性及内存泄漏问题的预防。

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

首先明确,new、delete是C++里的运算符,和加减乘除一个类别;而malloc、free是C中的函数。

1、new 与 malloc的区别

        执行内容

        malloc,是按字节来分配内存的(意思是分配的大小,由自己指定),返回类型是void* ,因此在使用的时候,需要强制转换类型

        new,是先开辟内存(由编译器根据你的对象类型,来决定开辟内存的大小),然后对内存进行初始化(执行构造函数),返回正确指向数据的指针。

        返回类型

        malloc,返回的是void* 的指针,如果申请内存失败,返回nullptr指针;

        new,返回的是申请对象的指针,如果申请内存失败,抛出 bad_alloc类型的异常;因此两者判断是否开辟成功的所做也不一样。

int main()
{
    int *p = (int*)malloc(sizeof(int));
    if(p == nullptr) {
        return 0;
    }
       .....
    free(p);//为了避免野指针的存在,使用完之后给p赋值为nullptr;


    int* p = new(int(10))
    //if (p == nullptr) 这样判断是错误的
    
    try {
        int* p = new(int(10));
        delete p;//为了避免野指针的存在,使用完之后给p赋值为nullptr;
    }catch (bad_alloc){
        }

    return 0;
}

根据上面两个的分析,然后就可以自己来判断 new,malloc 的其他区别,下面可以自行分析 

2、delete 与 free的区别

        执行过程

        delete:先调用析构函数,然后释放内存; free 只释放内存

        根据delete的工作步骤,如果是针对于内置数据类型,例如 int,float等,new并不需要执行构造函数;delete并不需要执行析构函数,因此和malloc,free此时并没有什么区别。

3、new 内在实现

        使用关键字 new 在堆上动态创建一个对象A时,A* p = new A() ,它实际上做了三件事:

        1.向堆上申请一块内存空间(编译器生成)( operator new )

        2.调用构造函数 (调用A的构造函数(如果A有的话))( placement new)

        3.返回正确的指针,即申请数据类型的指针,A*。

        operator new 和operator new[]功能都是仅仅分配内存,底层调用了 malloc 函数。 operator new是给new用的,operator new[]是给new[]用的。而且可以被重载。

void* operator new(size_t size) {
	void* p = malloc(size);
	if (p == nullptr) {
		throw bad_alloc();
	}
	return p;
}

void* operator new[](size_t size) {
	void* p = malloc(size);
	if (p == nullptr) {
		throw bad_alloc();
	}
	return p;
}

4、delete 内在实现

        使用 delete 的时候,也是一样的,其行为如下:

        1、定位到指针p所指向的内存空间,然后根据其类型,调用其自带的析构函数(内置类型不用)

        2、然后释放其内存空间(调用 operator delete ,将这块内存空间标志为可用,然后还给操作系统)

        3、在vs上测试,编译器并没有将申请的指针指向nullptr,因此此时指针为野指针,需要自己手动赋值为nullptr。

void operator delete[](void* ptr) {
	free(ptr);
}

void operator delete(void *ptr) {
	free(ptr);
}

5、何时需要配套使用

1、可以不配套

        原则上都需要配套使用,但是当 new 的对象是基本数据类型,没有构造函数、析构函数时候,new 和 delete的实现和 malloc、free并没有什么区别,不配套使用,也不会报错。

int main()
{
    int* p = new int(10);
    delete[] p;
       
    int* p = new int[10];
    delete p;

    return 0;
}

2、必须配套

        当存在构造函数和析构函数时候,必须配套使用。

class Test{
public:
    Test(int data = 10) : mdata(data) {}
    ~Test(){}
private:
    int mdata;
}


Test* ptr = new Test[5];
delete[] ptr;

        当编译器开辟内存的时候,看见是要开辟的对象的数组,开辟的内存不只只是5*4个字节,还会在多加4个字节,表示一共有多少个对象。因此,在delete[] 的时候,编译器才知道要对几个对象进行析构。

       

Test* ptr = new Test();
delete[] ptr;

        如果像上面这样操作的话,在delete[] ptr的时候,编译器看见 [], 就会像上找四个字节,此时的地址就不再是对象的地址了,因此产生错误。

Test* ptr = new Test[5];
delete ptr;//只会执行一次析构函数

        同样道理,new[] delete也是,delete只会执行一次,只delete掉了ptr指向的那一个,后面的没法执行。

6、析构函数里要不要写delete

class A{
public:
    A(int data) : mdata(data){}
    ~A(){};
private:
    int data;
};

//~A(){  cout << "析构" << endl;    }


class A1{
public:
    A(int data) : mptr(new int[10]) {}
    ~A() {
        delete[] mptr;
    }
private:
    int* mptr;
};
    

 对象A析构函数,不需要写 delete,因为此时并没有在其他地方再次开辟内存;

对象A1析构函数,需要继续写 delete(同时注意配套),因此此时,在对象A1中,还存在在其他地方继续开辟内存,所以delete的时候,要把都清理干净。(嵌套delete)

7、从内存中看 new new[]

在使用规范上,一定要遵守new delete 与 new[] delete[] 配套使用的规范,虽然有时候,我们写的代码可能不会报错,但终归看起来奇奇怪怪,甚至造成内存泄漏。

 上面的例子中,当我们自定义数据类型,没有写析构函数的时候,地址上方的四个字节,也没有存储数组中元素的个数,

在自定义数据类型中,如果涉及到内存申请,一定得写析构函数,防止内存泄漏。当我们用delete new[]时候,只会执行第一个元素的析构函数,后面的便造成了内存泄漏。在多次实验后,感觉delete[] 主要是要知道要执行多少次析构函数。

也就是当delete 一个 new[] 的时候,也可以释放自定义数据类型占的内存,但自定义数据类型中存在新开辟的堆空间,需要在析构函数执行,然后这部分没法执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值