C++new运算符
new运算符虽然用的很久,但是我实际上对它的了解仅仅局限于表面知道和malloc一样可以在堆内存中申请内存,可能会调用对象的构造函数。但是具体的实现和什么时候调用构造函数完全不了解,因此今天在这里进行梳理。
1. new的常见用法
int *a =new int[5];
int *b =new A();
在实际操作中,我们发现A的申请的空间的储存的值为随机值,new仅仅申请了一块随机空间,但是B的申请的空间的值已经初始化了,new调用了A的构造函数。
new A()的功能如下
- 在堆上申请空间
- 在分配的空间上调用对象的构造函数(这也是new和malloc的主要区别,是否调用构造函数)
在调用delete b时的发生的事情是 - 调用该对象的析构函数
- 释放该对象的内存空间
new的具体实现
new的功能可分为两部分
-
分配空间
-
调用构造函数
C++规定这两个功能是由两个函数分别实现的分别是 -
分配空间:operator new
-
调用构造函数:调用palcement new
所以说实际上C++的new的实现过程是关键词new调用表达式new(operator new)来分配空间,这个operator new是一个全局的函数,写在一个文件中,当使用new关键词时,编译器会找到这个函数,并且调用,new操作符的声明如下
void * operator new(std::size_t size) throw(std::bad_alloc){
if(size == 0)
size = 1;
void *p;
while((p = std::malloc)==0){
std::new_handler nh = std::get_new_headler();
if(nh)
nh();
else
throw std::bad_alloc();
}
return p;
}
void operator delete(void* ptr){
if(ptr)
{
std::free(ptr);
}
}
上面的operator new函数就是全局函数operator new。这里成为全局是因为每个对象都可以重载自己的operator new()函数。
还有一件事,throw(std::bad_alloc) 意味着这个函数只能抛出std::bad_alloc 类型的异常,throw(类型 1,类型2 …类型 n)意味着只能抛出从1到n的特定的异常,如果没写反而可以抛出所有的异常。
全局operator new分配空间
从上个例子中可以看到,全局operator new分配空间,简单调用了malloc函数分配空间,没有做任何初始化工作。
分配后的空间如何调用构造函数来创建一个真正的对象?这时候就需要调用:palcement new
palcement new 调用构造函数
placement new的功能是在一个已经分配好的空间上调用构造函数创建一个对象,它不是一个卸载文件里的函数而是编译器在编译时候做的事情
用法如下
int *buf = (int*)malloc(sizeof(int));//简单的分配空间
Class *pc = new(buf) class() ;在分配空间中调用构造函数
有一个疑问这里的空间是任意的吗?栈上的堆上的全局区的都可以吗?答案是“YES”
class A {int a;}
int buf[sizeof(A)]; //在栈上,分配一个数组
A *obj = new(buf) A(); //在这个数组上构造一个 对象 A。
实际上我们可以重载operator new(),重载的理由如下
- 检测代码中的内存错误
- 优化性能
- 获得内存使用的统计数据
重载的两种方法
- List item不改变签名,直接替换系统原有的版本
#include <new>
void* operator new(size_t size);
void operator delete(void* p);
2.增加新的参数,调用时后也提供这些额外的参数
void* operator new(size_t size, const char* file, int line); // 其返回的指针必须能被普通的 ::operator delete(void*) 释放
void operator delete(void* p, const char* file, int line); // 这个函数只在析构函数抛异常的情况下才会被调用
针对有必要重载operator new,陈硕大佬的态度是反对的。
- 绝对不能在 library 里重载 ::operator new()
如果你是某个 library 的作者,你的 library 要提供给别人使用,那么你无权重载全局 ::operator new(size_t) ,因为这非常具有侵略性:任何用到你的 library 的程序都*使用了你重载的 ::operator new(),。另外,如果有两个 library 都试图重载 ::operator new(size_t),那么它们会打架,可能会发生 duplicated symbol link error。
另一方面在主程序中重载operator new()的意义不大,因为使用重载后的需要手动判断内存是否泄露
综上,对于现实生活中的 C++ 项目,重载 ::operator new() 几乎没有用武之地,因为很难处理好与程序所用的 C++ library 的关系,毕竟大多数 library 在设计的时候没有考虑到你会重载 ::operator new() 并强塞给它。
具体可以看陈硕大佬的博客
陈硕的Blog
C++ 工程实践(2):不要重载全局 ::operator new()
上一篇: 中国标准时间转为时间轴
下一篇: js中国标准时间转化为年月日,时间戳