C++ new运算符除了分配和ctor调用之外还做了什么?

yes*_*aaj 8 c++ new-operator

new除了分配内存和调用构造函数之外,运算符还有什么其他的东西?

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函数未返回时,循环终止.

该标准还有很多关于新运算符和动态内存分配的其他内容(非常多),但我认为"默认行为"列表很好地总结了新运算符的基础知识.


Joh*_*itb 9

我已经写了一个关于它在这个答案中的作用的解释.它解释了如何

  • 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存储大小,无论我们编写什么解除分配函数.