欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++new运算符

程序员文章站 2022-03-30 09:04:32
...

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(sizeofint));//简单的分配空间
Class *pc = new(buf) class() ;在分配空间中调用构造函数

有一个疑问这里的空间是任意的吗?栈上的堆上的全局区的都可以吗?答案是“YES”

   class A {int a;}
    int buf[sizeof(A)];   //在栈上,分配一个数组
    A *obj =  new(buf) A();  //在这个数组上构造一个 对象 A。

实际上我们可以重载operator new(),重载的理由如下

  1. 检测代码中的内存错误
  2. 优化性能
  3. 获得内存使用的统计数据

重载的两种方法

  1. 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()

C++ 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()

C++ new 的用法 (总结)

相关标签: 复习 个人理解