c++ new operator和operator new,delete operator和operator delete
Posted sole_cc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++ new operator和operator new,delete operator和operator delete相关的知识,希望对你有一定的参考价值。
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 object
void *operator delete(void *); //free an object
void *operator new[](size_t); //allocate an array
void *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) 这种写法)
参考文章:
https://blog.csdn.net/WUDAIJUN/article/details/9273339
以上是关于c++ new operator和operator new,delete operator和operator delete的主要内容,如果未能解决你的问题,请参考以下文章
C++内存管理(new operator/operator new/operator delete/placement new)
C++内存管理机制学习笔记:重载operate new/::operator new..../new()