new和delete表达式

new和delete表达式

一、operator new 和 new operator的区别

new operator指的是new表达式,是一种操作,new操作,如stirng *pStr = new string("hello");

而operator new,这个new是一个运算符函数

前者是语言的特性,不能够改变,而后者是一个运算符函数,可以对其进行重载

二、operator delete 和 delete operator的区别

同样operator delete也是一个delete运算符函数,delete operator是一个delete表达式

delete运算符只是去释放内存,而delete表达式还干了其他事情,具体看四

三、new表达式工作步骤

当我们使用new表达式时,new做的事情

  1. 调用名为operator new的标准库函数,其作用是申请空间,即分配指定大的原始的未类型化的内存,用于保存指定类型的一个对象
  2. 再进行构造对象,运行该类型的一个构造函数初始化对象
  3. 再返回对象的指针,返回指向新分配并构造的构造函数对象的指针

四、delete表达式工作步骤

当使用delete表达式时,delete做的事情

  1. 调用析构函数,回收对象种数据成员所申请的资源
  2. 调用operator delete的标准库函数释放该对象所用的内存

例子:

#include <iostream>
#include <string.h>
using std::endl;
using std::cout;

class Computer {
public:
    Computer(const char *brand, double price) 
    :_brand(new char[strlen(brand) + 1]()),
    _price(price) { 
        strcpy(_brand, brand);
        cout << "Computer(const char *, double)" << endl; 
    }

    ~Computer() {
        delete [] _brand;
        cout << "~Computer()" << endl;
    }

private:
    char *_brand;
    double _price;
};

int main() {
    Computer *pc = new Computer("lenovo", 8000);
    printf("pc对象存放的地址:%p\n", &pc);
    printf("pc对象所指内存的地址:%p\n", pc);
    return 0;
}

当我们执行上面代码时,会发现创建了Computer对象,并分配了内存空间

Computer(const char *, double)
pc对象存放的地址:0x7ffd799f3840
pc对象所指内存的地址:0x56346e309e70

我们发现pc对象的地址在内存布局是偏高的,pc对象所指内存存放的地址是比其要低一些的,原因我使用的平台是Linux平台,内存分布是栈内存在高出向下生长,堆内存在低处向上生长,如此可见new申请了空间,还进行了对象的构造

但我们还发现一个问题,Computer对象的构造函数调用了,但为什么没有进行析构函数的调用?

这里就是我们要探讨的调用delete表达式时,其做的事情(见本节开头)

此时我们加上delete pc;

int main() {
    Computer *pc = new Computer("lenovo", 8000);
    printf("pc对象存放的地址:%p\n", &pc);
    printf("pc对象所指内存的地址:%p\n", pc);
 	delete pc;
    return 0;
}
Computer(const char *, double)
pc对象存放的地址:0x7fff74cceed0
pc对象所指内存的地址:0x560b87241e70
~Computer()

发现了析构函数的调用

这里我们为了验证内存布局,特意将数据成员设置成public方便我们使用,并用图来解释下列代码

Computer *pc = new Computer("lenovo", 8000);
printf("pc对象存放的地址:%p\n", &pc);
printf("pc对象所指内存的地址:%p\n", pc);
printf("pc中_brand的地址:%p\n", &pc->_brand);
printf("pc中_brand所指内存的地址:%p\n", pc->_brand);                       
delete pc;
Computer(const char *, double)
pc对象存放的地址:0x7ffdf7495ea0
pc对象所指内存的地址:0x560407d2be70
pc中_brand的地址:0x560407d2be70
pc中_brand所指内存的地址:0x560407d2be90
~Computer()

在这里插入图片描述

通过这张图和上面代码运行的结果我们可以发现:

  1. pc指针指向的Computer对象的首地址和_brand数据成员的地址一样,这表示了类的大小保存的是数据成员的大小(这里可以找相关类和对象的文章去看关于类的内存布局)
  2. _brand数据成员进行了深拷贝,就是在构造函数时new char[strlen(brand) + 1](),申请了堆空间这个堆空间的地址就是pc中_brand所指内存的地址
  3. 从delete的工作机制中我们可以发现,第一步调用对象的析构函数,而我们的析构函数可以对其显式的定义出来,目的是为了释放对象中数据成员所申请的堆内存,即delete [] _brand;按照图中所进行的步骤为步骤1
  4. 然后再调用operator delete函数释放申请对象时所用的内存,即图中的步骤2

问题

针对于delete的工作步骤,调用析构函数和对象销毁等价吗?

A:如果是内置对象(如int,long…)的话就等价,如果是自定义类类型的话就不等价,因为两步所执行的内容不一样。

六、定位new表达式

普通的new一般都会执行其步骤中的内容,主要一点是会申请内存空间,从堆中找到一个满足要求的内存块。

而定位new运算符(placement new),通过指定要使用的位置,即可以指定在哪块空间去“创建对象”,注意这里没有说是申请内存,而是说使用指定的位置去创建对象。

所以,定位new运算符,即指定位置之后,它并不负责去创建内存空间,也不负责检查所指定的地址是否可以使用。

与常规的new表达式不同,定位new不需要相应的delete去释放内存,原因是它本身就不开辟内存空间,它只是使用指定的位置去创建对象。

七、new/delete与malloc/free的相同点和不同点

void* __CRTDECL operator new(size_t const size)
{
    for (;;)
    {
        if (void* const block = malloc(size))
        {
            return block;
        }

        if (_callnewh(size) == 0)
        {
            if (size == SIZE_MAX)
            {
                __scrt_throw_std_bad_array_new_length();
            }
            else
            {
                __scrt_throw_std_bad_alloc();
            }
        }
    }
}
void __CRTDECL operator delete(void* const block) noexcept
{
    #ifdef _DEBUG
    _free_dbg(block, _UNKNOWN_BLOCK);
    #else
    free(block);
    #endif
}

这里抽取了new和delete运算符函数的源码,发现底层使用的是malloc和free

这里我们来总结一下malloc和new的相同点

不同点

  1. malloc/free是C语言的标准库函数,而new/delete是C++的标准库函数
  2. malloc在使用该函数时就要把大小制定好(多少个字节),malloc申请的是原始的未初始化的堆空间;new能够自动分配空间大小,new申请的是已经初始化的堆空间
  3. new分配内存按照数据类型进行分配,malloc分配内存按照指定的大小进行分配
  4. new返回的是指定对象的指针,而malloc返回的是void*,因此malloc的返回值一般都需要进行类型转化。
  5. new不仅分配一段内存,而且还会调用构造函数,malloc不会。
  6. new分配的内存要用delete销毁,malloc要用free来销毁;delete销毁的时候会调用对象的析构函数,而free则不会。
  7. new是一个可重载的运算符,malloc是一个C标准库函数。
  8. new如果分配失败了会抛出bad_malloc的异常,而malloc失败了会返回NULL。
  9. 申请数组时,new[]一次分配所有的内存,多次调用构造函数,搭配使用delete[],delete多次调用析构函数,销毁数组中的每个对象,而malloc则只能sizeof(int)*n;

相同点

  1. 都是用来申请堆空间的
  2. malloc和free以及new与delete要成对出现,否则会造成内存泄漏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值