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

时间:2008-12-18 08:16:27

标签: c++ new-operator

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

3 个答案:

答案 0 :(得分:16)

C ++标准可以说明<new>标题中新运算符的单个对象形式(通常使用的形式):

  

必需的行为:

     

将一个非空指针返回到适当对齐的存储(3.7.3),否则抛出一个   bad_alloc异常。此要求绑定在此函数的替换版本上。

     

默认行为:

     

- 执行循环:在循环内,函数首先尝试分配所请求的存储。是否   该尝试涉及调用标准C库函数malloc未指定。

     

- 如果尝试成功,则返回指向已分配存储的指针。否则,如果是最后一个参数   set_new_handler()是一个空指针,抛出bad_alloc。

     

- 否则,该函数调用当前的new_handler(18.4.2.2)。如果被调用的函数返回,则循环   重复。

     

- 当尝试分配所请求的存储成功或被调用时,循环终止   new_handler函数不会返回。

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

答案 1 :(得分:9)

我已经在this回答中解释了它的作用。它解释了如何

  • 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

下面是一个示例,您可以看到存储大小如何影响新表达式数组形式的分配大小(我的其他答案未涵盖):

#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_;
}

它会打印出这样的内容:

sizeof f: 1
new    p: 0x93fe008
new size: 5
&f_     : 0x93fe00c
delete p: 0x93fe008
size    : 5

对象本身的一个字节和计数的4个字节,它存储在对象的分配区域之前。现在,如果我们使用没有size参数的deallocation函数(只是从运算符delete中删除它),我们得到这个输出:

sizeof f: 1
new    p: 0x9451008
new size: 1
&f_     : 0x9451008
delete p: 0x9451008

这里的C ++运行时并不关心大小,所以它不再存储它。请注意,这是高度特定于实现的,这就是gcc在这里能够告诉您成员运算符delete的大小。其他实现可能仍然存储大小,并且很可能如果存在要为类调用的析构函数。例如,只需在上面添加~f() { }就可以使gcc存储大小,无论我们编写什么解除分配函数。

答案 2 :(得分:0)

取决于它是否过载,如果你构建应用程序进行调试,如果你正在使用内存泄漏检测器,如果你有某种内存池方案,如果你有类似Boehm垃圾收集器的标记/取消标记位等等。它可以在里面做很多自定义的东西,或者根本没什么特别的。