Mic*_*urr 16
C++标准对<new>
标题中新运算符的单个对象形式(通常使用的形式)有这样的说法:
所需行为:
将非空指针返回到适当对齐的存储(3.7.3),否则抛出bad_alloc异常.此要求绑定在此函数的替换版本上.
默认行为:
- 执行循环:在循环内,函数首先尝试分配所请求的存储.是否未指定尝试是否涉及调用标准C库函数malloc.
- 如果尝试成功,则返回指向已分配存储的指针.否则,如果set_new_handler()的最后一个参数是空指针,则抛出bad_alloc.
- 否则,该函数调用当前的new_handler(18.4.2.2).如果被调用函数返回,则循环重复.
- 当尝试分配所请求的存储成功或者调用的new_handler函数未返回时,循环终止.
该标准还有很多关于新运算符和动态内存分配的其他内容(非常多),但我认为"默认行为"列表很好地总结了新运算符的基础知识.
我已经写了一个关于它在这个答案中的作用的解释.它解释了如何
new
获得记忆new
处理内存故障new
处理构造函数异常new
处理特殊放置和非放置版本Michael解释了默认分配器函数(:: operator new)如何很好地获取内存以及它如何处理失败.我已经在他的评论中看到了关于对象大小存储位置的问题.答案是,如果不是必要的话,没有存储大小.请记住,C不需要大小free
(和:: operator new可以使用malloc
):
void * memory = malloc(x);
free (memory); // no need to tell it the size
Run Code Online (Sandbox Code Playgroud)
下面是一个示例,您可以看到存储大小如何影响新表达式的数组形式的分配大小(我的其他答案未涵盖):
#include <cstddef>
#include <iostream>
struct f {
// requests allocation of t bytes
void * operator new[](std::size_t t) throw() {
void *p = ::operator new[](t);
std::cout << "new p: " << p << std::endl;
std::cout << "new size: " << t << std::endl;
return p;
}
// requests deleting of t bytes starting at p
void operator delete[](void *p, std::size_t t) throw() {
std::cout << "delete p: " << p << std::endl;
std::cout << "size : " << t << std::endl;
return ::operator delete[](p);
}
};
int main() {
std::cout << "sizeof f: " << sizeof (f) << std::endl;
f * f_ = new f[1];
std::cout << "&f_ : " << f_ << std::endl;
delete[] f_;
}
Run Code Online (Sandbox Code Playgroud)
它会打印出这样的东西:
sizeof f: 1
new p: 0x93fe008
new size: 5
&f_ : 0x93fe00c
delete p: 0x93fe008
size : 5
Run Code Online (Sandbox Code Playgroud)
对象本身的一个字节和计数的4个字节,它存储在对象的分配区域之前.现在,如果我们使用没有size参数的deallocation函数(只是从运算符delete中删除它),我们得到这个输出:
sizeof f: 1
new p: 0x9451008
new size: 1
&f_ : 0x9451008
delete p: 0x9451008
Run Code Online (Sandbox Code Playgroud)
这里的C++运行时并不关心大小,因此它不再存储它.请注意,这是高度特定于实现的,这就是gcc在这里能够告诉您成员运算符delete的大小.其他实现可能仍然存储大小,并且很可能如果存在要为类调用的析构函数.例如,只需添加~f() { }
上面的内容就可以使gcc存储大小,无论我们编写什么解除分配函数.