C++的new&delete

new的过程

new的过程:先分配memory,再调用ctor

我们常用的创建对象的方法有两种

Complex c(1,2);					//栈
Complex *pc = new Complex(1,2); //堆

第一种创建出来的对象将保存在栈上,第二种则在堆上,必须手动回收内存空间(通过delete)

为了解释new的过程,我们先建立一个Complex类

class Complex
{
public:
Complex(...) {...}//构造函数
...
private:
double real;
double imag;
};

当我们使用new构建Complex类对象的时候

Complex *pc = new Complex(1,2);

当我们使用new这一个动作,在上动态创建一个对象时,编译器实际上帮你做了三件事:

Complex *pc;

//1.分配内存(调用 operator new() 函数)
void* memory = operator new(sizeof(Complex));
//2.转型
pc = static_cast<Complex*>(memory);
//3.调用构造函数
pc->Complex::Complex(1,2);
  1. 分配内存:operator new也是一个函数,其内部调用malloc(n),拿到sizeof(Complex)大小的内存空间;这时候我们得到指向内存空间始址的指针memory,它是一个指向viod类型的指针
  2. 转型:用static_cast函数,把步骤①得到的指针memory(这是一个pointer to void)转换为pointer to Complex,并将其赋值到pc(步骤①和②可以写在一起)
  3. 调用构造函数:步骤②得到的指针pc指向的内存空间,即为新对象的起始内存地址;于是编译器将通过指针pc调用对象的构造函数

所以从结果上看,这两段代码是等效的

//代码1.
Complex *pc = new Complex(1,2);
//代码2.
Complex *pc;
void* memory = operator new(sizeof(Complex));
pc = static_cast<Complex*>(memory);
pc->Complex::Complex(1,2);

malloc和new的区别在于,当malloc失败时,它不会调用分配内存失败处理程序new_handler,因此我们还是要尽可能的使用new,除非有一些特殊的需求

delete的过程

delete的过程:先调用dtor,再释放memory

我们再建立一个包含指针的类String:

class String {
public:
...
~String()
{delete[] m_data;}
...
private:
char* m_data;
};

当我们试用new&delete时:

String* ps = new String("HELLO");
...
delete ps;

编译器在delete这里实际上帮你做了两件事:

String::~String(ps);	//1.调用析构函数
operator delete(ps); //2.释放内存
  1. 调用析构函数:由于String类是包含指针的,所以设计时不能使用默认析构函数,而是重载一个符合需求的析构函数,在我们delete ps时,编译器第一步就是调用我们重载后的析构函数(没有重载则调用默认)
  2. 释放内存:operator deleteoperator new一样也是一个函数,其内部调用free(ps)

new的三种形态

有的朋友可能被上面的newoperator new搞晕了,实际上在C++中提到new,至少可能代表以下三种含义:new operatoroperator newplacement new

new operator

new operator 就是 new 操作符,不能被重载

我们上面所说的new,都是指new operator,也就是我们平时使用的new

operator new

operator new 是函数,可以被重载,new operator 调用它用来分配内存,通过重载它,可以改变 new operator 的功能

默认有三种

void* operator new (std::size_t size) throw (std::bad_alloc);
void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw();
void* operator new (std::size_t size, void* ptr) throw();
  1. 第一种分配size个字节的存储空间,并将对象类型进行内存对齐。如果成功,返回一个非空的指针指向首地址。失败抛出bad_alloc异常。 (A* a = new A; 调用第一种
  2. 第二种在分配失败时不抛出异常,它返回一个NULL指针。 (A* a = new(std::nothrow) A; //调用第二种
  3. 第三种是 placement new 版本,它本质上是对 operator new 的重载,定义于#include 中,它不分配内存,调用合适的构造函数在 ptr 所指的地方构造一个对象,之后返回实参指针ptr,下文细谈

重载 operator new

class Complex
{
public:
Complex(...) {...}//构造函数
...
//重载第一种
void* operator new(size_t size){
cout << "operator new called\n" << endl;
//通过::operator new调用了原有的全局的new
return ::operator new(size);
}
void operator delete(void* pointer)
{
cout << "operator delete" << endl;
::operator delete(pointer);
}
private:
double real;
double imag;
};

然后你可以直接调用 Complex::operator new(),或者使用 new 来调用

int main()
{
Complex* pc2 = new Complex(1,2);
}

输出

operator new called

operator delete

这里通过::operator new调用了原有的全局的new,也就相当于是在分配内存之前输出一句话

如上代码所示,delete 也有 delete operatoroperator delete 之分,后者也是可以重载的。并且,如果重载了 operator new,就应该也相应的重载 operator delete,这是良好的编程习惯

重载 operator new 需要注意以下几点:

  1. 重载时,返回类型必须声明为void*
  2. 重载时,第一个参数类型必须为表达要求分配空间的大小(字节),类型为 size_t
  3. 重载时,可以带其它参数

带其他参数的重载:

void* operator new(size_t size, string str) {
cout << "operator new called\n" << endl;
cout << "with string:" << str << endl;
//通过::operator new调用了原有的全局的new
return ::operator new(size);
}

调用时就可以这样操作

Complex* pc = new("Test") Complex(1,2);

placement new

placement new 是 c++ 实现的 operator new 版本,它的实现如下

// Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }
inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; } // Default placement versions of operator delete.
inline void operator delete (void*, void*) _GLIBCXX_USE_NOEXCEPT { }
inline void operator delete[](void*, void*) _GLIBCXX_USE_NOEXCEPT { }
//@}

可以看到实际上它就返回了传进来的地址,根据operator的第二个例子,通过重载全局的operator new之后,new函数的操作就被改变了。也就能猜出,在调用new的时候参数需要加上一个地址,placement new的功能就是在这个地址之上进行构造。

placement new 的使用步骤如下:

//1.分配内存
char* buff = new char[ sizeof(Complex) * N ];
memset( buff, 0, sizeof(Foo)*N );
//2.构建对象
Complex* pc = new (buff)Complex;
//3.使用对象
pc->XXXXXX();
//4.析构对象,显式的调用类的析构函数
pc->~Complex();
//5.销毁内存
delete[] buff;

上面5个步骤是标准的placement new的使用方法

placement new 是用来实现定位构造的,因此可以实现 new operator 三步操作中的调用构造函数这一步(在取得了足够内存空间后,在这块内存空间是上构造一个对象)

上面写的pc->Complex::Complex(1,2);这句话并不是一个标准的写法,正确的写法是使用 placement new

#include <new.h>

int main()
{
char* buff = new char[ sizeof(Complex) ];
Complex* pc = new(buff) Complex(1,2);
...
}

placement new 它实现了在指定内存地址上用指定类型的构造函数来构造一个对象的功能,这块指定的地址既可以是栈,又可以是堆,placement 对此不加区分

除非特别必要,不要直接使用placement new ,这毕竟不是用来构造对象的正式写法,只不过是new operator的一个步骤而已。使用new operator地编译器会自动生成对placement new的调用的代码,因此也会相应的生成使用delete时调用析构函数的代码

如果是像上面那样在栈上使用了placement new,则必须手工调用析构函数,这也是显式调用析构函数的唯一情况

pc->~Complex();

当我们觉得默认的new operator对内存的管理不能满足我们的需要,而希望自己手工的管理内存时,placement new就有用了。STL中的allocator就使用了这种方式,借助placement new来实现更灵活有效的内存管理。

C++的new&delete的更多相关文章

  1. 如何区别数据库删除语句drop与delete与truncate?

    1.delete:删除数据表中的行(可以删除某一行,也可以在不删除数据表的情况下删除所有行) 删除某一行:delete from 数据表名称 where 列名称=值: 删除所有行:delete*fro ...

  2. 数据库设计中的Soft Delete模式

    最近几天有点忙,所以我们今天来一篇短的,简单地介绍一下数据库设计中的一种模式——Soft Delete. 可以说,该模式毁誉参半,甚至有非常多的人认为该模式是一个Anti-Pattern.因此在本篇文 ...

  3. 关于JavaScript中的delete操作

    关于JavaScript中的delete操作 看到一道题,是这样的: (function(x){ delete x; return x; })(1); 1 null undefined Error 我 ...

  4. Git异常:Cannot delete the branch 'test1' which you are currently on

    GitHub实战系列汇总:http://www.cnblogs.com/dunitian/p/5038719.html ———————————————————————————————————————— ...

  5. HTTP Method详细解读(`GET` `HEAD` `POST` `OPTIONS` `PUT` `DELETE` `TRACE` `CONNECT`)

    前言 HTTP Method的历史: HTTP 0.9 这个版本只有GET方法 HTTP 1.0 这个版本有GET HEAD POST这三个方法 HTTP 1.1 这个版本是当前版本,包含GET HE ...

  6. IIS7.5上的REST服务的Put,Delete操作发生HTTP Error 405.0 - Method Not Allowed 解决方法

    WebDAV 是超文本传输协议 (HTTP) 的一组扩展,为 Internet 上计算机之间的编辑和文件管理提供了标准.利用这个协议用户可以通过Web进行远程的基本文件操作,如拷贝.移动.删除等.在I ...

  7. ASP.NET Core 中文文档 第二章 指南(4.10)检查自动生成的Detail方法和Delete方法

    原文 Examining the Details and Delete methods 作者 Rick Anderson 翻译 谢炀(Kiler) 校对 许登洋(Seay).姚阿勇(Mr.Yao) 打 ...

  8. new/delete重载

    在c++中,有时我们需要在运行阶段为一个变量分配未命名的内存,并使用指针来访问它,这里就可以用到new关键字.另外需要指出的是,new分配的内存块通常与常规变量分配的内存块不同,常规变量的值都储存在被 ...

  9. EC笔记:第三部分:16成对使用new和delete

    我们都知道,申请的资源,使用完毕后要释放.但是这个释放动作,一定要注意. 举个例子,很多人动态分配的资源,在使用之后,往往直接调用了delete,而不管申请资源的时候用的是new还是new[]. 如下 ...

  10. Spring boot: Request method 'DELETE' not supported, Request method 'PUT' not supported, Request method 'POST' not supported

    GET,POST,PUT,DELETE, Spring都支持,不要怀疑Spring, 一定是前端发送的rest 请求和后端的响应不匹配, 查找原因以及解决办法, 很简单 用chrome打开F12控制台 ...

随机推荐

  1. JavaScript 数组,字符串,函数

    数组 创建数组 利用 new 创建数组 var arr = new Array(); console.log(arr); // Array(0) 数组字面量创建 var arr = []; // 空数 ...

  2. rf关键字

    1.获取字典中的key ${b} Set Variable ${a}[0][dealer_buy_price] Log ${b}   2.${b}的float类型转换string 再和后面比较 Sho ...

  3. Flink架构(五)- 检查点,保存点,与状态恢复

    检查点,保存点,与状态恢复 Flink是一个分布式数据处理系统,这种场景下,它需要处理各种异常,例如进程终止.机器故障.网络中断等.因为tasks在本地维护它们的state,Flink必须确保在出现故 ...

  4. mac VirtualBox虚拟机装CentOS,并配置网络,使其发布的静态网站能访问

    1.下载VirtualBox(macOS版本) 下载地址:https://download.virtualbox.org/virtualbox/6.0.8/VirtualBox-6.0.8-13052 ...

  5. resize2fs: 报错

    报错如下 [root@localhost ~]# resize2fs /dev/mapper/centos-root resize2fs (-Dec-) resize2fs: Bad magic nu ...

  6. 每天进步一点点------Nios II 的Run as hardware 中报错:Downloading ELF Process failed

    今天继续调试,又出现了新问题.在执行NIOS程序代码时,不能下载了:Pausing target processor: not responding. Resetting and trying aga ...

  7. mongo shell远程连接使用数据库

    mongo mydb --username user1 --host --password --username 用户名 --host 连接ip --port 连接端口号 --password 密码 ...

  8. maven一直加载2.0.0.M7 的 config server 失败

    之前学习的时候使用F版的SpringBoot管理项目依赖一直好好的,今天不知idea为何抽疯,一直加载失败,各种重启,清除,没用 只能像之前学习注册consul 时将F版的SpringBoot 改为G ...

  9. OSS链接出现 connection pool shutdown错误修改

    在类中创建了OSSClient对象 ,方法共用此实例对象,在前端很短的时间内连续提交,造成异常错误. 解决方法时将OSSClient对象在方法中创建

  10. 全网最全!小白搭建hexo+Github/Gitee/Coding

    Hexo是一个快速.简洁且高效的博客框架.Hexo使用Markdown解析文章,在几秒内,即可利用靓丽的主题生成静态网页. 本站内容已全部转移到https://www.myyuns.ltd,具体请移步 ...