C/C++编程:写了placement new也要写placement delete
对于:
Widget *pw = new Widget ;
共有两个函数被调用:一个是用以分配的operator new,一个是Widget的默认构造函数
假设第一个函数调用成功,第二个函数抛出异常-----第一步的内存分配所得的内存必须归还,否则内存泄漏。此时,客户没有能力归还内存,因为如果Widget构造函数抛出异常,pw尚未被赋值,客户手上也没有指针指向应该被归还的内存。取消第一步并恢复到原来的责任因此落在C++运行期系统上。
运行期系统会调用步骤一所调用的operator new的响应的operator delete版本,如果目前面对的是拥有正常签名式(signature)的new和delete,这并不是问题,因为正常的operator new:
void * operator new(std::size_t) throw(std::bad_alloc);
对应正常的operator delete:
void operator delete(void * ramMemory) throw(); //global作用域中的正常签名式
void operator delete(void * ramMemory, std::size_t size) throw(); //class作用域中的正常签名式
然而,当你声明非正常形式的operator new,也就是带有附加参数的operator new,那么找到对应的delete可能有点困难。
举个例子,假设你写了一个class专属的operator new,要求接受一个ostream,用来log相关分配信息,同时右写了一个正常形式的class专属operator delete:
// 这个设计有问题
class Widget{public:static void * operator new(std::size_t size, std::ostream& logStream) throw(std::bad_alloc); // 非正常适应的deletestatic void operator delete(void *pMemory, std::size_t) throw(); //正常的class专属delete
};
如果operator new接受的参数除了一定会有的那个size_t之外还有其他,这就是所谓的placement new。因此,上面的operator new是个placement版本。众多placement new版本中特别有用的一个是“接受一个指针指向对象该被构造之处”,长相如下:
void * operator new(std::size_t, void *pMemory) throw();
这个版本的new
已经被纳入C++标准库,你只要#include<new>
即可。这个new的用途之一是负责在vector的未使用空间上创建对象。它同时也是最早版本的placement new。实际上它是这个函数的命名由来:一个特定位置的new。当人们谈到placement new,就是谈这个版本,也就是“唯一额外参数是个void*”,少数情况下才是接受任意实参的operator new。
现在我们回到原先那个设计有问题的Widget class。这里的问题是:这个class将会引起微妙的内存泄漏。考虑如下代码,它在动态创建一个widget时将相关的log记在cerr:
Widget *pw = new(std::cerr) Widget; //调用operator new并传递cerr为ostream实参:这个动作会在widget构造函数抛出异常时泄漏内存。
但是,如果内存分配成功,widget构造函数抛出异常,运行期系统将寻找“参数类型与个数都与operator new相同的”operator delete。也就是说:
static void * operator delete(std::size_t size, std::ostream& logStream) throw()
但是找不到,所以它什么也不做,从而造成内存泄漏
如果内存分配成功,widget构造函数没有抛出异常,当客户调用:
delete pw;
调用的是正常使用的operator delete,而不是placement delete。placement delete只在“伴随placement new调用而触发的构造函数”出现异常时才会被调用。
也就是说,要解决placement new的内存泄漏,必须同时提供一个正常的operator delete(用于构造期间无任何异常时抛出)和一个placement delete(用于构造期间有异常抛出)。
另外,由于成员函数的名称会掩盖作用域中的相同名称,如果你在基类中声明唯一一个placement operator new,客户讲无法使用正常形式的new:
class Base{public:static void * operator new(std::size_t size, std::ostream& logStream) throw(std::bad_alloc);};Base *pb = new Base; // errror,因为正常形式的operator new被掩盖
Base *pb = new(std::cerr)Base; //ok,调用Base的placement new
同样地,当你继承时:
class Derived: public Base{public:static void* operator new(std::size_t size) throw(std::bad_alloc);
};
Derived *p = new (std::clog) Derived; // Error!
Derived *p = new Derived; // OK
这是因为子类中的”normal new”隐藏了父类中的”placement new”,虽然它们的函数签名不同。
最佳实践
缺省情况下C++在全局作用域
void* operator new(std::size_t) throw(std::bad_alloc); // normal new
void* operator new(std::size_t, void*) throw(); // placement new
void* operator new(std::size_t, const std::nothrow_t&) throw(); // 见 Item 49
如果你在类内声明了任何operator new,它会遮掩上面这些标准形式。除非你的意思就是要阻止class的客户使用这些形式,否则请确保它们在你所生成的任何定制new之外还可用,对于每一个operator new也请确定提供对应的operator delete
class StandardNewDeleteForms {public:// normal new/deletestatic void* operator new(std::size_t size) throw(std::bad_alloc) { return ::operator new(size); }static void operator delete(void *pMemory) throw() { ::operator delete(pMemory); }// placement new/deletestatic void* operator new(std::size_t size, void *ptr) throw() { return ::operator new(size, ptr); }static void operator delete(void *pMemory, void *ptr) throw() { return ::operator delete(pMemory, ptr); }// nothrow new/deletestatic void* operator new(std::size_t size, const std::nothrow_t& nt) throw() { return ::operator new(size, nt); }static void operator delete(void *pMemory, const std::nothrow_t&) throw() { ::operator delete(pMemory); }
};
凡是想以自定形式扩充标准的客户,可以利用继承机制以及using声明式取得标准形式:
class Widget: public StandardNewDeleteForms { // inherit std forms
public:using StandardNewDeleteForms::operator new; using StandardNewDeleteForms::operator delete; // 让这些形式可见 static void* operator new(std::size_t size, std::ostream& log) throw(std::bad_alloc); // 自定义 placement newstatic void operator delete(void *pMemory, std::ostream& logStream) throw(); // 对应的 placement delete
};
C/C++编程:写了placement new也要写placement delete相关推荐
- Java编程常用英语汇总,让你写程序不再困难
A abstract 抽象的 abstract base class (ABC)抽象基类 abstract class 抽象类 abstraction 抽象.抽象物.抽象性 ac ...
- 代写SOFTENG 370 Operating Systems课设、代做C/C++ 留学生作业、代写c/c++代码、代写C/C++编程作业...
代写SOFTENG 370 Operating Systems课设.代做C/C++ 留学生作业.代写c/c++代码.代写C/C++编程作业 SOFTENG 370 Operating Systems ...
- r语言软件GDINA_finTech MSc代做、代写Python程序语言、代写MSc program、代做Python设计帮做C/C++编程|代写R语言...
finTech MSc代做.代写Python程序语言.代写MSc program.代做Python设计帮做C/C++编程|代写R语言Strathclyde Business School, finTe ...
- 操作系统大作业 基于Linux的模拟进程调度算法 运用c++语言编程 在VMware虚拟机里 centos 亲自写亲自测试 代码 说明书
发布文章 博文管理我的博客退出 Trash Temp 操作系统大作业 基于Linux的模拟进程调度算法 运用c++语言编程 在VMware虚拟机里 centos 亲自写亲自测试 代码 说明书 @[TO ...
- 【编程之美】如何将代码写得像诗一样优雅?
01.文章目录 文章目录 01.文章目录 02.文档级注释 03.函数的外在阐述 04.函数的内在阐述 05.附录(小结) 02.文档级注释 何谓文档级注释? 我们先看下面一张图片. 看了图片,相信大 ...
- flash编程可以用c语言,C语言写的小游戏和FLASH小游戏有何区别呢-c语言编程手机软件...
C语言写的小游戏和FLASH小游戏有何区别呢-c语言编程手机软件 (2010-12-05 17:12:08) 标签: 杂谈 分类: 随笔 同题有大大的区分c语言写游戏可以直接挪用体系api举行编著,合 ...
- python写机器人程序_用Python写的一个多线程机器人聊天程序
本人是从事php开发的, 近来想通过php实现即时通讯(兼容windows).后来发现实现起来特别麻烦, 就想到python.听说这家伙在什么地方都能发挥作用.所以想用python来做通讯模块...所 ...
- SQL 语句代编代写、DATA MANAGEMENT SYSTEMS代写代做
SQL 语句代编代写.DATA MANAGEMENT SYSTEMS代写代做 DATA MANAGEMENT SYSTEMS DESIGN PROJECT Purpose of this projec ...
- 先写API文档还是先写代码?你需要这款神器Apifox!
代码未动,文档先行 其实大家都知道 API 文档先行的重要性,但是在实践过程中往往会遇到很多困难. 程序员最讨厌的两件事:1. 写文档,2. 别人不写文档.大多数开发人员不愿意写 API 文档的原因是 ...
- 拼音怎么写_老师:不会写的字用圈代替,看到孩子试卷,网友:人才
遇到不会写的字怎么办? 估计每个人都会遇到这样的情况,特别是小学生,认字不全,遇到不会写的字是常有的事情. 就是不知道大家遇到这样的问题的时候是怎么解决的,记得读书的时候,老师一般会告诉我们不会写的字 ...
最新文章
- 查询v$lock缓慢和direct path write temp等待
- Android中Intent传递对象的两种方法(Serializable,Parcelable)
- 通过这12张手绘图,搞懂什么是微服务架构
- SpringBoot笔记:SpringBoot集成SpringbootAdmin监控
- android 异步回调中操作UI线程,UI同步、卡死阻塞等性能问题
- 没想到啊!3980元的Web前端视频今日免费送。
- java 数据结构_Java数据结构学习方法
- 马化腾、马云并列成为中国首富;百度回应“将上线电商直播”;.NET 5 Preview 2 发布 | 极客头条...
- 数据库系统概念第七版(Database System concepts 7th)课后答案英文版答案
- 大数据第一季--java基础(day5)-徐培成-专题视频课程
- SQL2005安装出错
- 国内外免费sns源码程序大集合
- MAC装虚拟机显示打不开 /dev/vmmon: 断裂管道 请确保已载入内核模块 ’vmmon’
- League of Legends 通过 游戏ID查询玩家QQ号码。
- 牛客刷题记录之语法入门选择结构篇
- 【Zeekr_Tech】汽车软件敏捷开发和分支管理
- python中len的用法_Python len函数用法
- windows 认证机制
- 广播计算机应用基础,2019年秋季考试《计算机应用基础》在线考核试题 广播幻灯片操作应选择的功能区是...
- android5.0模拟器pc版,逍遥安卓独家发布支持5.0安卓系统电脑模拟器
热门文章
- [Jule CTF 2022] 部分WP
- Creo5.0 永久修改尺寸单位为毫米(mmns)
- 淘宝足迹新玩法,如何通过足迹增加商品曝光量,打标后足迹不出解决方法,淘宝详情页的下拉出现足迹怎么实现的
- 【docker lnmp】 2、容器mysql 连接问题 getaddrinfo failed
- python补充超级鹰代码
- python四级是什么水平_大学英语四级相当于高考什么水平
- 用Javascript实现Blogger/Blogspot的传统模板翻页功能
- 将小图标转换成字体图标代码
- mysql -hlocalhost -uroot -p_MySQL数据库的操作(01)--- MySQL的安装以及与idea的连接
- 国外问卷调查所需准备