c++ 中new 操作符是怎么实现的
new是C++的语言特征, 不是函数.
它可能会调用malloc, 但是具体如何分配内存要取决于实现. 相比于malloc, new默认在申请失败的时候会抛出异常而不是直接返回0.
和new对应的是delete, 它们必须成对出现. new[] 则要和 delete[] 同时出现.
new是C++关键字,你可以尝试自己去实现一个new ,必要的时候利用自己的实现的函数去替换默认的new做一些检查之类的事情。
比如如下链接内的写法
cppreference链接
void* operator new(std::size_t sz) {
return std::malloc(sz);
}
复杂点的实现——深入探究C++的new/delete操作符 - 作者@Kelvin
void * operator new(std::size_t size) throw(std::bad_alloc) {
if (size == 0)
size = 1;
void* p;
while ((p = ::malloc(size)) == 0) {
std::new_handler nh = std::get_new_handler();
if (nh)
nh();
else
throw std::bad_alloc();
}
return p;
}
void operator delete(void* ptr) {
if (ptr)
::free(ptr);
}
多了用以处理内存不足的函数new_handler
2 回答1.7k 阅读✓ 已解决
2 回答1k 阅读✓ 已解决
1 回答1.6k 阅读✓ 已解决
1 回答1.1k 阅读✓ 已解决
1 回答949 阅读
1 回答1.1k 阅读
2 回答1.1k 阅读
首先要区分
new operator
和operator new
这个两个概念。前者就是题中的
new 操作符
。一般,在 new 一个对象的时候,底层做了一下两步:
为对象分配内存;
调用构造函数初始化这块内存;
在第一步中,分配内存就使用了
operator new
。 @肥夏 的第二个例子就是一个operator new
。在第二步中,调用构造函数初始化内存。如果不涉及继承,这一步非常简单,就是给内存赋值。即使有继承关系的话,也是复杂在对继承关系、虚函数表的梳理上,与本题关系不大。
下面重点说说第一步中的
operator new
,它职责就是分配内存。operator new
的一般处理流程正如 @肥夏 的第二个例子所给出的:分配内存,如果成功则直接返回,否则,
查看
new_handler
是否可用,如果可用则调用之并跳转到 1 ,否则,抛出
bad_alloc
异常。上面的
new_handler
会尝试释放一部分内存。了解这个流程之后,我们再延伸讨论一下另外两个问题。
如下代码片段中的空检查是否有意义?
从上面我们了解到,
operator new
在分配内存失败的情况下会调用new_handler
尝试让系统释放点内存,然后再次尝试申请内存。如果这时系统中内存确实紧张,即使调用new_handler
,系统也拿不出更多的内存,这就会导致operator new
陷入一个死循环。总而言之,
operator new
要么成功申请到内存,要么在那死循环不出来。然后,再考虑
new operator
的第二步,调用构造函数初始化内存。众所周知,C++ 的构造函数是不返回任何返回值的,如果想让构造函数失败只能是抛出异常。由以上两点我们可以得出结论:
new Question
是永远不会返回空指针的。进一步地,
new Question
失败只有两种情况:operator new
抛出bad_alloc
异常;构造函数抛出自己的异常。
所以,上面代码片段中的判空逻辑是否有意义呢?是否使用
try ... catch
来捕获new
的异常才是更好的判断new
失败的方法呢?自定义内存管理
一般打 new 的主意的都是想实现自己的内存管理(比如使用内存池),并且想在不改变现有代码的基础上让旧代码也能享受到新的内存管理所带来的福利。
具体做法 @肥夏 所引用的第二篇文章里已经讲的很清楚。但因为重载
new operator
对整个系统的影响可能远比最初的想象大得多,因此可以考虑以下两个折中的方案:使用
placement new
;实现一个新的 New (而不是重载原来的
operator new
),然后在确实需要的地方使用。