对于:

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相关推荐

  1. Java编程常用英语汇总,让你写程序不再困难

    A abstract   抽象的          abstract base class (ABC)抽象基类 abstract class 抽象类 abstraction 抽象.抽象物.抽象性 ac ...

  2. 代写SOFTENG 370 Operating Systems课设、代做C/C++ 留学生作业、代写c/c++代码、代写C/C++编程作业...

    代写SOFTENG 370 Operating Systems课设.代做C/C++ 留学生作业.代写c/c++代码.代写C/C++编程作业 SOFTENG 370 Operating Systems ...

  3. r语言软件GDINA_finTech MSc代做、代写Python程序语言、代写MSc program、代做Python设计帮做C/C++编程|代写R语言...

    finTech MSc代做.代写Python程序语言.代写MSc program.代做Python设计帮做C/C++编程|代写R语言Strathclyde Business School, finTe ...

  4. 操作系统大作业 基于Linux的模拟进程调度算法 运用c++语言编程 在VMware虚拟机里 centos 亲自写亲自测试 代码 说明书

    发布文章 博文管理我的博客退出 Trash Temp 操作系统大作业 基于Linux的模拟进程调度算法 运用c++语言编程 在VMware虚拟机里 centos 亲自写亲自测试 代码 说明书 @[TO ...

  5. 【编程之美】如何将代码写得像诗一样优雅?

    01.文章目录 文章目录 01.文章目录 02.文档级注释 03.函数的外在阐述 04.函数的内在阐述 05.附录(小结) 02.文档级注释 何谓文档级注释? 我们先看下面一张图片. 看了图片,相信大 ...

  6. flash编程可以用c语言,C语言写的小游戏和FLASH小游戏有何区别呢-c语言编程手机软件...

    C语言写的小游戏和FLASH小游戏有何区别呢-c语言编程手机软件 (2010-12-05 17:12:08) 标签: 杂谈 分类: 随笔 同题有大大的区分c语言写游戏可以直接挪用体系api举行编著,合 ...

  7. python写机器人程序_用Python写的一个多线程机器人聊天程序

    本人是从事php开发的, 近来想通过php实现即时通讯(兼容windows).后来发现实现起来特别麻烦, 就想到python.听说这家伙在什么地方都能发挥作用.所以想用python来做通讯模块...所 ...

  8. SQL 语句代编代写、DATA MANAGEMENT SYSTEMS代写代做

    SQL 语句代编代写.DATA MANAGEMENT SYSTEMS代写代做 DATA MANAGEMENT SYSTEMS DESIGN PROJECT Purpose of this projec ...

  9. 先写API文档还是先写代码?你需要这款神器Apifox!

    代码未动,文档先行 其实大家都知道 API 文档先行的重要性,但是在实践过程中往往会遇到很多困难. 程序员最讨厌的两件事:1. 写文档,2. 别人不写文档.大多数开发人员不愿意写 API 文档的原因是 ...

  10. 拼音怎么写_老师:不会写的字用圈代替,看到孩子试卷,网友:人才

    遇到不会写的字怎么办? 估计每个人都会遇到这样的情况,特别是小学生,认字不全,遇到不会写的字是常有的事情. 就是不知道大家遇到这样的问题的时候是怎么解决的,记得读书的时候,老师一般会告诉我们不会写的字 ...

最新文章

  1. 查询v$lock缓慢和direct path write temp等待
  2. Android中Intent传递对象的两种方法(Serializable,Parcelable)
  3. 通过这12张手绘图,搞懂什么是微服务架构
  4. SpringBoot笔记:SpringBoot集成SpringbootAdmin监控
  5. android 异步回调中操作UI线程,UI同步、卡死阻塞等性能问题
  6. 没想到啊!3980元的Web前端视频今日免费送。
  7. java 数据结构_Java数据结构学习方法
  8. 马化腾、马云并列成为中国首富;百度回应“将上线电商直播”;.NET 5 Preview 2 发布 | 极客头条...
  9. 数据库系统概念第七版(Database System concepts 7th)课后答案英文版答案
  10. 大数据第一季--java基础(day5)-徐培成-专题视频课程
  11. SQL2005安装出错
  12. 国内外免费sns源码程序大集合
  13. MAC装虚拟机显示打不开 /dev/vmmon: 断裂管道 请确保已载入内核模块 ’vmmon’
  14. League of Legends 通过 游戏ID查询玩家QQ号码。
  15. 牛客刷题记录之语法入门选择结构篇
  16. 【Zeekr_Tech】汽车软件敏捷开发和分支管理
  17. python中len的用法_Python len函数用法
  18. windows 认证机制
  19. 广播计算机应用基础,2019年秋季考试《计算机应用基础》在线考核试题 广播幻灯片操作应选择的功能区是...
  20. android5.0模拟器pc版,逍遥安卓独家发布支持5.0安卓系统电脑模拟器

热门文章

  1. [Jule CTF 2022] 部分WP
  2. Creo5.0 永久修改尺寸单位为毫米(mmns)
  3. 淘宝足迹新玩法,如何通过足迹增加商品曝光量,打标后足迹不出解决方法,淘宝详情页的下拉出现足迹怎么实现的
  4. 【docker lnmp】 2、容器mysql 连接问题 getaddrinfo failed
  5. python补充超级鹰代码
  6. python四级是什么水平_大学英语四级相当于高考什么水平
  7. 用Javascript实现Blogger/Blogspot的传统模板翻页功能
  8. 将小图标转换成字体图标代码
  9. mysql -hlocalhost -uroot -p_MySQL数据库的操作(01)--- MySQL的安装以及与idea的连接
  10. 国外问卷调查所需准备