C++中placement new操作符(经典)

2023-11-11

 

C++中placement new操作符(经典)

placement new是重载operator new的一个标准、全局的版本,它不能被自定义的版本代替(不像普通的operator new和operator delete能够被替换成用户自定义的版本)。

它的原型如下:

void *operator new( size_t, void *p ) throw()  { return p; }

 

首先我们区分下几个容易混淆的关键词:new、operator new、placement new

new和delete操作符我们应该都用过,它们是对堆中的内存进行申请和释放,而这两个都是不能被重载的。要实现不同的内存分配行为,需要重载operator new,而不是new和delete。

 

看如下代码:

class MyClass {…};

MyClass * p=new MyClass;

这里的new实际上是执行如下3个过程:

1调用operator new分配内存;

2调用构造函数生成类对象;

3返回相应指针。

operator new就像operator+一样,是可以重载的,但是不能在全局对原型为void operator new(size_t size)这个原型进行重载,一般只能在类中进行重载。如果类中没有重载operator new,那么调用的就是全局的::operator new来完成堆的分配。同理,operator new[]、operator delete、operator delete[]也是可以重载的,一般你重载了其中一个,那么最好把其余三个都重载一遍。

placement new是operator new的一个重载版本,只是我们很少用到它。如果你想在已经分配的内存中创建一个对象,使用new是不行的。也就是说placement new允许你在一个已经分配好的内存中(栈或堆中)构造一个新的对象。原型中void*p实际上就是指向一个已经分配好的内存缓冲区的的首地址。

我们知道使用new操作符分配内存需要在堆中查找足够大的剩余空间,这个操作速度是很慢的,而且有可能出现无法分配内存的异常(空间不够)。placement new就可以解决这个问题。我们构造对象都是在一个预先准备好了的内存缓冲区中进行,不需要查找内存,内存分配的时间是常数;而且不会出现在程序运行中途出现内存不足的异常。所以,placement new非常适合那些对时间要求比较高,长时间运行不希望被打断的应用程序。

 

使用方法如下:

1. 缓冲区提前分配

可以使用堆的空间,也可以使用栈的空间,所以分配方式有如下两种:

class MyClass {…}; 
char *buf=new char[N*sizeof(MyClass)+ sizeof(int) ] ; 或者char buf[N*sizeof(MyClass)+ sizeof(int) ];

2. 对象的构造

MyClass * pClass=new(buf) MyClass;

3. 对象的销毁

一旦这个对象使用完毕,你必须显式的调用类的析构函数进行销毁对象。但此时内存空间不会被释放,以便其他的对象的构造。

pClass->~MyClass();

4. 内存的释放

如果缓冲区在堆中,那么调用delete[] buf;进行内存的释放;如果在栈中,那么在其作用域内有效,跳出作用域,内存自动释放。

 

注意:

1)        在C++标准中,对于placement operator new []有如下的说明: placement operator new[] needs implementation-defined amount of additional storage to save a size of array. 所以我们必须申请比原始对象大小多出sizeof(int)个字节来存放对象的个数,或者说数组的大小。

2)        使用方法第二步中的new才是placement new,其实是没有申请内存的,只是调用了构造函数,返回一个指向已经分配好的内存的一个指针,所以对象销毁的时候不需要调用delete释放空间,但必须调用析构函数销毁对象。

-----------------------------------------------------------------------------------------------------------------

今天跟同事聊天,他说到STL源码有用到显示调用析构函数。试一了一下。果然能行。

 

 

结果:

Constructors

Destructors        //这个是显示调用的析构函数

Destructors        // 这个是delete调用的析构函数

 

这有什么用? 

有时候,在对象的生命周期结束前,想先结束这个对象的时候就会派上用场了。

 

由此想到的: 

因为我知道。

new的时候,其实做了两件事,一是:调用malloc分配所需内存,二是:调用构造函数。

delete的时候,也是做了两件事,一是:调用析造函数,二是:调用free释放内存。

 

所以推测构造函数也是可以显式调用的。做了个实现。

 

int _tmain(int argc, _TCHAR* argv[])
{
    MyClass* pMyClass = (MyClass*)malloc(sizeof(MyClass));
     pMyClass->MyClass();
    //
}

 

 

编译pMyClass->MyClass()出错:

error C2273: 'function-style cast' : illegal as right side of '->'operator

天啊,它以为MyClass是这个类型。

 

解决办法有两个:

 

第一:pMyClass->MyClass::MyClass();
第二:new(pMyClass)MyClass();

 

 

第二种用法涉及C++ placement new 的用法 。

placement new的作用就是:创建对象(调用该类的构造函数)但是不分配内存,而是在已有的内存块上面创建对象。用于需要反复创建并删除的对象上,可以降低分配释放内存的性能消耗。请查阅placement new相关资料。

显示调用构造函数有什么用? 

有时候,你可能由于效率考虑要用到malloc去给类对象分配内存,因为malloc是不调用构造函数的,所以这个时候会派上用场了。

 

另外下面也是可以的,虽然内置类型没有构造函数。

 

int* i = (int*)malloc(sizeof(int));
new (i) int();

 

感觉这些奇奇怪怪的用法最好在写代码库时,为了达到某个目时去使用,不推荐应用开发时使用。

#include <iostream>
usingnamespace std;

class MyClass
{
public:
    MyClass()
    {
        cout <<"Constructors"<< endl;
    }
    ~MyClass()
    {
        cout <<"Destructors"<< endl;
    }

};

int _tmain(int argc, _TCHAR* argv[])
{
   MyClass* pMyClass =new MyClass;
   pMyClass->~MyClass();
   delete pMyClass;

}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++中placement new操作符(经典) 的相关文章

  • gdb:“未加载符号表”

    尝试在 gdb 中添加断点时 我不断收到此错误消息 我使用这些命令来编译 gcc g main c utmpib2 c o main o and cc g main c utmpib2 c o main o and also g g mai
  • 如何在GDB中运行记录指令历史记录和函数调用历史记录?

    编辑 根据下面的第一个答案 当前的 技巧 似乎正在使用 Atom 处理器 但我希望一些 gdb 专家可以回答这是否是一个基本限制 或者路线图上是否添加了对其他处理器的支持 反向执行似乎在我的环境中起作用 我可以反向继续 查看合理的记录日志
  • GDB 函数参数上的条件中断

    我想在函数参数大于某个值时设置断点 下面的虚拟代码 int main void uint64 t num 123456 uint64 t x 847534 uint64 t other num x x num other stuff her
  • 专门逐行调试

    我有一个用 Pascal 编写的脚本 我会以这种方式调试它 在每一行停止 转储内存中所有变量的值 然后转到下一行 是否可以使用 gdb 或其他 Linux 开源工具来完成此操作 使用选项编译文件 g fpc gpc g file pas R
  • gdb 通过指向错误的代码行显示不正确的回溯

    我们可以通过在源代码中包含多个中止调用 用非常简单的示例重现此问题 在下面的示例代码中 我们在不同条件下总共有四个中止调用 但是当我们使用优化标志 O3 进行编译时 我们只能看到一个中止调用的调试信息 因此 在这四个中止调用中发生崩溃时 g
  • 使用 gdb 调试反汇编库

    在Linux和Mac OS X中可以使用strapi和next来调试应用程序而无需调试信息 在 Mac OS X 上 gdb 显示在库内部调用的函数 尽管有时会在每个 stepi 指令中推进多个汇编程序指令 在 Linux 上 当我进入动态
  • 哪个信号被传递到信号处理程序中死锁的进程

    我有一个来自调用信号处理程序后死锁的进程的核心转储 如何确定传送了哪个信号以及是谁发送的 GDB 为接收信号的线程生成的回溯如下 信号处理程序在第 15 帧中被调用 gdb bt 0 0x00007fa9c204654b in sys fu
  • GDB错误:“进程记录:当前架构不支持记录功能”

    我正在尝试在 GDB 中进行反向执行 特别是target record按照说明在 gdb 中运行我的程序后here https stackoverflow com questions 1206872 go to previous line
  • 观察点固定地址

    对于我当前的嵌入式应用程序 我尝试将 GDB 观察点放在固定的内存地址处 例如 我的应用程序更新以下地址 0x10793ad0 为了确定代码的哪一部分破坏了值 我尝试了 watch 0x10793ad0 即使 GDB 在此之后不会打印任何错
  • 缺少单独的调试信息,请使用: debuginfo-install glibc-2.12-1.47.el6_2.9.i686 libgcc-4.4.6-3.el6.i686 libstdc++-4.4.6-3.el6.i686

    CentOS 6 2 GNU gdb GDB 红帽企业 Linux 7 2 50 el6 当我使用 GDB 调试简单的 C 代码时 我看到以下警告 Missing separate debuginfos use debuginfo inst
  • gdb 错误 - 文件不是可执行格式:无法识别文件格式

    我正在尝试使用 gdb 调试某个名为 xdf 的程序 但是当我运行 gdb xdf 时 出现以下错误 home nealtitusthomas X ray astronomy heasoft 6 24 x86 64 pc linux gnu
  • gdb 不会从外部架构读取核心文件

    我正在尝试在 Linux 桌面上读取 ARM 核心文件 但似乎无法找出我的核心文件 有什么方法可以指示 gdb 我的核心文件是什么类型吗 file daemon daemon ELF 32 bit LSB executable ARM ve
  • 在 C 程序中追踪数组越界访问/写入的推荐方法

    考虑用 C 语言编写一些不太明显的算法的实现 例如 让它成为递归快速排序 我在 K N King 的 C 编程 现代方法 第二版 书中找到了它 可以从here http knking com books c2 programs qsort
  • “gdb”调试器奇怪地跳过断点

    这是我的代码的一小段 l2tp inspector cc 14 else if f info gt key proto UDP PROTO 15 if size gt 4 16 uint32 t l2tp part 17 l2tp part
  • gdb 脚本:在选定的断点处执行命令

    我想在 gdb 脚本中预定义一些断点 并在这些断点处调用一些特殊命令 然后自动继续程序执行 因此 理想情况下 我想要一个如下所示的 gdb 脚本 b someFunction if breakpoint from above reached
  • 内存转储格式类似于 gdb 中的 xxd

    我正在尝试检查一个缓冲区 其中包含二进制格式的消息 但也包含字符串数据 作为示例 我正在使用以下 C 代码 int main void char buf 100 x01 x02 x03 x04String Data xAA xBB xCC
  • 如何在 VSCode 中配置调度程序锁定 gdb 选项

    我可以添加额外的参数launch json到 gdb 通过miDebuggerArgs https code visualstudio com docs cpp launch json reference midebuggerargs 像这
  • 是否可以根据函数要返回的内容在函数末尾设置条件断点?

    我有以下更复杂的版本 unsigned int foo unsigned int bar unsigned int myFunc return foo bar 就我而言 myFunc从很多地方调用 在其中一种情况下 出现了问题 通过进一步调
  • 从 gdb 设置 std::string 变量值?

    是否有可能 当调试器在断点处停止时 修改 std string 变量的值 而不需要采取诸如调整当前缓冲区的内存映像之类的黑客手段 例如类似于 set var mystring hello world 试试这个 经过测试并且对我有用 call
  • 在 GDB 中,是否可以给出一个相对于函数开头的地址(以行为单位)?

    主题行基本上说明了一切 如果我根据文件和行号给出位置 则在编辑文件时该值可能会更改 事实上 如果我在重构过程中编辑多个函数 它往往会经常发生变化 而且会带来不便 但是 如果它相对于函数的开头 行 则更改的可能性较小 如果无法给出距函数开头的

随机推荐

  • 创建节点和用法

    创建节点 appendchild 的用法 效果如下 insertBefore 的用法 效果 removeChild 的用法 效果 replaceChild 的用法 效果 search 方法用于查询指定的字符串的初始位置 并获取它的下标 se
  • 研究生必备:从0到1使用Zotero

    一 初识zotero 文献导入与引用 1 安装地址 https www zotero org 点击下一步一直安装 编辑 首选项 高级 选择语言为中文 2 文献导入 1 在我的文库下面可以新建文件夹 在中间标题部分可以拖进去本地下载的PDF
  • STL——(8)set/ multiset 容器和pair对组

    set multiset 容器和pair对组 1 set基本概念 2 set构造和赋值 3 set大小和交换 4 set插入和删除 5 set查找和统计 6 set和multiset区别 7 pair对组创建 8 set容器排序 1 set
  • 学编程太枯燥太难怎么办?

    大家好 我是老三 和大家分享一些我学编程的经历 那年二十 头发浓密如野狗 夏日炎炎 枯坐机房如木头 一根指头 颤颤巍巍如老叟 敲下了第一行 Hello World 开启了编程学习生涯 刚开始 参加的是学校的一个夏季编程训练营 起初是有学长学
  • 2023年以太坊测试网水龙头整理(包含Goerli和Sepolia)

    2023年以太坊测试网水龙头整理 包含Goerli和Sepolia 区块漫步 2023年以太坊测试网水龙头整理 包含Goerli和Sepolia 空投交互By blockwander 去中心化应用在以太坊主网上线之前 都会在以太坊测试网上先
  • 互联网摸鱼日报(2022-09-16)

    互联网摸鱼日报 2022 09 16 InfoQ 热门话题 1 从某保险机构数据库全面国产化 看如何跨越金融数据价值鸿沟 2 Flink 从实时计算到流式数仓 下一步去往哪里 3 CEO们突然介入到 IT建设 企业纷纷迁出VM虚拟机基础设施
  • 【git学习】本地关联远程仓库

    目录 一 本地仓库关联远程仓库 新建仓库 二 拉取远程分支到本地 已有远程仓库 一 本地仓库关联远程仓库 新建仓库 本地新建工程 然后关联远程git仓库并向远程仓库提交代码 1 本地新建工程 这里我使用idea创建 2 在远程仓库新建仓库
  • SIP相关的RFC文档索引

    http www packetizer com ipmc sip standards html
  • Mysql服务器安装步骤

    安装包 windows10 MySQL Server 5 7 mysql installer community 5 7 26 0 msi 安装步骤 双击运行下载好的mysql installer community 5 7 26 0 ms
  • 01--背包问题以及构造最优解

    目录 1 01 背包问题 2 构造最优解 3 动态规划法求解01 背包问题的局限性 1 01 背包问题 01 背包问题 就是有n个物品 它们有各自的体积和价值 现有给定容量的背包 如何让背包里装入的物品具有最大的价值总和 一个物品只有装与不
  • 调试flex程序

    如果程序编译时报错 需要修改程序 有时还要进行调试 在Flash中 最常用的调试方式是使用trace函数 将想要检测的对象或函数运行结果在输出面板中打印出来 在Flex中 同样可以使用trace函数来进行调试 修改上面的代码
  • 自动代码生成 - 使用Seq2Seq模型在代码生成数据集上进行自动代码生成任务。

    1 引言 自动代码生成是一个具有挑战性和实用性的任务 它可以帮助开发人员自动化生成代码 提高开发效率 在这篇博客中 我们将介绍如何使用Seq2Seq模型进行自动代码生成任务 并在代码生成数据集上进行实验 我们将使用Python作为代码生成语
  • SeleniumLibrary4.5.0 关键字详解(一)

    SeleniumLibrary4 5 0 关键字详解 一 库版本 4 5 0 库范围 全局 命名参数 受支持 简介 SeleniumLibrary是Robot Framework的Web测试库 本文档说明了如何使用SeleniumLibra
  • leetcode 编写一个函数来查找字符串数组中的最长公共前缀。

    编写一个函数来查找字符串数组中的最长公共前缀 如果不存在公共前缀 返回空字符串 string longestCommonPrefix vector
  • Flutter控件之CircularProgressIndicator

    CircularProgressIndicator的作用 Flutter中的CircularProgressIndicator是一个圆形进度指示器 用于表示正在进行的任务的进度 它通常用于长时间运行的任务 例如文件下载 网络请求等 Circ
  • vue-amap生成地图遮罩层、点标记和弹窗

  • vmware 安装window server 2012 完只有命令窗口

    原因是 解决办法 输入 Dism online enable feature all featurename Server Gui Mgmt featurename Server Gui Shell featurename ServerCo
  • signature=27dcc93dc9d59db77c2d43c8888c8f5d,ftv-20201027

    0001659166 20 000184 txt 20201027 0001659166 20 000184 hdr sgml 20201027 20201027162536 ACCESSION NUMBER 0001659166 20 0
  • TypeError: Argument ‘bb’ has incorrect type (expected numpy.ndarray, got list)

    问题说明 这个问题是在mmdetect中使用使用自己做的coco数据集用maskrcnn做目标检测时遇到的 主要原因是你的json文件里面的segmentation中的数据不符合要求 正常来说这里面是类似于 x y x y x y x y
  • C++中placement new操作符(经典)

    C 中placement new操作符 经典 placement new是重载operator new的一个标准 全局的版本 它不能被自定义的版本代替 不像普通的operator new和operator delete能够被替换成用户自定义