博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c++ new operator和operator new,delete operator和operator delete
阅读量:4121 次
发布时间:2019-05-25

本文共 3253 字,大约阅读时间需要 10 分钟。

1 new operator 和 operator new,delete operator 和 operator delete

new operator: c++中的关键字new,如A *a = new A;

operator new:c++中的一个操作符,并且可以被重载(类似加减乘除操作符)

operator new can be called explicitly as a regular function, but in C++, new is an operator with a very specific behavior: An expression with the new operator, first calls function operator new (i.e., this function) with the size of its type specifier as first argument, and if this is successful, it then automatically initializes or constructs the object (if needed). Finally, the expression evaluates as a pointer to the appropriate type.

operator new和operator delete的声明

void *operator new(size_t);     //allocate an objectvoid *operator delete(void *);    //free an object void *operator new[](size_t);     //allocate an arrayvoid *operator delete[](void *);    //free an array

例如:

A* a = new A;

分三步:

1 分配内存;2 调用A()构造对象;3 返回分配指针

事实上,分配内存这一操作就是由operator new(size_t)来完成的,如果类A重载了operator new,那么将调用A::operator new(size_t ),否则调用全局::operator new(size_t ),后者由C++默认提供。因此前面的步骤也就是:

1 调用operator new (sizeof(A)); 2 调用A::A(); 3 返回指针

下面的图是一个具体的示例

简单总结下:

  • 首先需要调用上面提到的 operator new 标准库函数,传入的参数为 class A 的大小,这里为 8 个字节,至于为什么是 8 个字节,你可以看看《深入 C++ 对象模型》一书,这里不做多解释。这样函数返回的是分配内存的起始地址,这里假设是 0x007da290。
  • 上面分配的内存是未初始化的,也是未类型化的,第二步就在这一块原始的内存上对类对象进行初始化,调用的是相应的构造函数,这里是调用 A:A(10); 这个函数,从图中也可以看到对这块申请的内存进行了初始化,var=10, file 指向打开的文件。
  • 最后一步就是返回新分配并构造好的对象的指针,这里 pA 就指向 0x007da290 这块内存,pA 的类型为类 A 对象的指针。

delete pA 的过程:

delete 就做了两件事情:

  • 调用 pA 指向对象的析构函数,对打开的文件进行关闭。
  • 通过上面提到的标准库函数 operator delete 来释放该对象的内存,传入函数的参数为 pA 的值,也就是 0x007d290。

 

2 operator new的三种形式

throwing (1)    void* operator new (std::size_t size) throw (std::bad_alloc);nothrow (2) void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();placement (3)   void* operator new (std::size_t size, void* ptr) throw();

(1)(2)的区别仅是是否抛出异常,当分配失败时,前者会抛出bad_alloc异常,后者返回null,不会抛出异常。它们都分配一个固定大小的连续内存。

A* a = new A; //调用throwing(1)A* a = new(std::nothrow) A; //调用nothrow(2)

3 new[] 和 delete[]

假设 class A *pAa = new A[3];

从这个图中我们可以看到申请时在数组对象的上面还多分配了 4 个字节用来保存数组的大小,但是最终返回的是对象数组的指针,而不是所有分配空间的起始地址。

这样的话,释放就很简单了:

delete []pAa;

这里要注意的两点是:

  • 调用析构函数的次数是从数组对象指针前面的 4 个字节中取出;
  • 传入 operator delete[] 函数的参数不是数组对象的指针 pAa,而是 pAa 的值减 4。

4 为什么 new/delete 、new []/delete[] 要配对使用?

4.1 使用new[]分配内存,delete释放

内置类型:

int *pia = new int[10];delete []pia;

内置类型因为不需要调用析构函数,所以使用new[]分配的内存的时候没有多分配4个字节保存数组大小;所以此时使用delete 释放内存不会有问题

带有自定义析构函数的类类型:

class A *pAa = new class A[3];delete pAa;

问题有两点,第一点是只对第一个对象调用了析构函数,剩下两个对象没有调用析构函数,如果类对象中申请了大量的内存需要在析构函数中释放,而你却在销毁数组对象时少调用了析构函数,这会造成内存泄漏;

第二点是致命的,直接释放 pAa 指向的内存空间,这个总是会造成严重的段错误,程序必然会崩溃!因为分配的空间的起始地址是 pAa 指向的地方减去 4 个字节的地方。你应该将传入参数设为那个地址!

4.2 使用new分配内存,delete[]释放

同4.1进行分析

总的来说,记住一点即可:new/delete、new[]/delete[] 要配套使用总是没错的!

5 动态创建对象时的初始化问题

int *a = new int(5)  //allocates an integer, set to 5. (same syntax as constructors)

int *a = new int     //allocates an integer,明确不初始化,内置类型的对象无初始化

int *a = new int()  //allocates an integer,采用值初始化,将*a初始化为0

 string *ps = new string(10,'9')    //使用给定的值初始化该内存空间(十个9的字符串)

string *ps = new string                //明确不初始化,但是string会使用默认构造函数初始化(initialized to empty string)

string *ps = new string()             //对于提供了默认构造函数的类类型,无论程序是明确的不初始化还是要求进行值初始化,都会自动调用默认构造函数初始化该对象

int *a = new int[5];

int *a = new int[5]();

原理同上(没有 int *a = new int[5](9) 这种写法)

 

 

参考文章:

你可能感兴趣的文章
Flex 布局教程:语法篇
查看>>
年薪50万+的90后程序员都经历了什么?
查看>>
2019年哪些外快收入可达到2万以上?
查看>>
【JavaScript 教程】标准库—Date 对象
查看>>
前阿里手淘前端负责人@winter:前端人如何保持竞争力?
查看>>
【JavaScript 教程】面向对象编程——实例对象与 new 命令
查看>>
我在网易做了6年前端,想给求职者4条建议
查看>>
SQL1015N The database is in an inconsistent state. SQLSTATE=55025
查看>>
RQP-DEF-0177
查看>>
Linux查看mac地址
查看>>
Linux修改ip
查看>>
MySQL字段类型的选择与MySQL的查询效率
查看>>
Java的Properties配置文件用法【续】
查看>>
JAVA操作properties文件的代码实例
查看>>
IPS开发手记【一】
查看>>
Java通用字符处理类
查看>>
文件上传时生成“日期+随机数”式文件名前缀的Java代码
查看>>
Java代码检查工具Checkstyle常见输出结果
查看>>
北京十大情人分手圣地
查看>>
Android自动关机代码
查看>>