new、express new、operator new、placement new 之间的千丝万缕

SAM3 几行代码支持任意图像分辨率,减少显存占用(针对transformers库) 几行代码让sam3支持任意图像分辨率 阅读详情


一、new is an expression

在C++中,new经常被用来进行动态内存的分配,例如:

Foo* obj = new Foo; //其中Foo是一个类

这种我们经常使用的new其实在C++中称为“expression”。一个expression是没办法重载的。所以,我们没办法从 expression new入手来定义一个我们想要的版本。但是,expression new 经过编译器之后执行的动作却让我们有机可趁。在一个new expression 之后,编译器其实做了三件事:

1. void* men = operator new(sizeof(Foo));
2. Foo* ptr = static_cast<Foo*>(men);
3. ptr->Foo("waterMelon", 30)
  1. 第一,编译器帮我们调用了operator new 来 申请一块干净的内存(未经过初始化)。
  2. 第二,编译器帮我们把得到那块内存用static_cast函数进行类型转换成对应的类型。(C++的四种cast动作详解)
  3. 第三,编译器帮我们调用了构造函数,在分配的内存中进行对象的构造。

在这个过程中,让我们有机可趁的地方是步骤一中的 operator new 函数,因为它是一个可以被进行重载的函数。 在步骤一中 operator new 接收了一个参数,指明了“我想要多少字节的内存”。而对于后续的步骤二、三来说,从步骤一中得到的内存究竟是从哪儿来的其实它们并不关心,毕竟它们的职责只是构造对象而已。因此,我们就能对某些版本的operator new进行重载,来进行内存管理或者做其他的事情。


二、operator new

为了验证上面的事情(使用expression之后,编译器做了三件事),我们可以使用下面的程序进行测试:

#include <iostream>
#include <cstdlib>
#include <string>

using namespace std;

class Foo {
public:
	Foo() = default;
	Foo(string name, int value) : _name(name), _value(value) {}
	void showContent() 
	{ cout << "name:" << _name << " " << "value:" << _value << endl; }
	
	void* operator new(size_t size); //重载operator new 操作

private:
	string _name;
	int _value;
};

void* Foo::operator new(size_t size) {
	cout << "call my operator new" << endl;
	return malloc(size);
}

int main() {
	Foo* obj = new Foo("waterMelon", 30);
	obj->showContent();

	delete obj;

	return 0;
}

在类Foo 中,我们定义了自己的operator new 版本。因此,在主程序中,一旦使用了expression new的操作,编译器就自动帮我们做三件事,而第一件事就是调用类作用域内重载的operator new,因此,在控制台会对结果进行输出:

在这里插入图片描述
可以看见,确实new expression 确实调用了我们自己重载的new operator版本。然而,对于后续的转型、构造来说,它们并不关心这块内存究竟是哪儿来的。在上面的例子中,这块内存其实只是简单的调用C中的malloc,而在我们使用的STL容器中,这块内存其实是从内存池(由allocator类维护)挖出来的,每次需要的时候,就从自己维护的内存池中分配一块出来返回,进行后续的转型和初始化。这是STL分配器中进行内存管理的方法,为的是减少不必要的空间浪费(cookie)和减少malloc调用。当然,这逐渐偏离了现在写的主题。

总的来说,operator new 其实就是给了我们一个机会来进行函数的重载,让我们有办法去“什么地方”拿一块内存而已。

三、placement new(一)

上面讲到了使用expression new之后发生的三件事以及我们如何使用operator new 来抓住“内存分配”这个动作进行重载,得到我们想要的版本。对于步骤二、步骤三是编译器帮我们做的事情。现在的问题是,编译器做的事情,你自己能做吗?
假设我从哪儿拿到了一块干净的内存,我能不能自己转型、然后调用构造函数来构造对象?以下为测试程序:

#include <iostream>
#include <cstdlib>
#include <string>

using namespace std;


class Foo {
public:
	Foo() = default;
	Foo(string name, int value) : _name(name), _value(value) {}
	void showContent() 
	{ cout << "name:" << _name << " " << "value:" << _value << endl; }
	
	void* operator new(size_t size); //重载operator new 操作

private:
	string _name;
	int _value;
};

void* Foo::operator new(size_t size) {
	cout << "call my operator new" << endl;
	return malloc(size);
}

int main() {
	Foo* obj = new Foo("waterMelon", 30);
	obj->showContent();
	delete obj;
	
	// 尝试在干净的内存中调用构造函数
	void* men = malloc(sizeof(Foo));
	Foo* ptr = static_cast<Foo*>(men);
	ptr->Foo("waterMelon", 30);
	
	return 0;
}

结果如下:
在这里插入图片描述
看来我们有点异想天开了,编译器根本不允许我们自己来“转型+构造”。那有没有可能C++也提供了什么函数来完成这两个步骤?答案已经呼之欲出了,那就是使用placement new。placement new 允许我们在一块已经分配的内存中来进行构造函数的调用。注意,下面是placement new 的用法:

// 注意,下面 place_address 是某块用来构造对象的内存(指针),type是某个类的名称
new (place_address) type  // 默认构造
new (place_address) type (initializers) // 传入参数,见下面的例子

实用:

#include <iostream>
#include <cstdlib>
#include <string>
#include <new>

using namespace std;


class Foo {
public:
	Foo() = default;
	Foo(string name, int value) : _name(name), _value(value) {}
	void showContent() 
	{ cout << "name:" << _name << " " << "value:" << _value << endl; }
	
	void* operator new(size_t size); //重载operator new 操作

private:
	string _name;
	int _value;
};

void* Foo::operator new(size_t size) {
	cout << "call my operator new" << endl;
	return malloc(size);
}

int main() {

	void* men = malloc(sizeof(Foo));
	Foo* obj2 = ::new(men) Foo("banana", 20); // 使用placement new进行对象的构造
	obj2->showContent();
	delete obj2;

	return 0;
}

执行结果:
在这里插入图片描述
从上面可以总结得到,placement new就是标准库开放给我们在一块已经分配的内存中来进行构造函数的调用的接口而已。再次注意:由于placement new动作是“构造”而不是“分配”,因此,没有对应的placement delete。而上面的new、operator new 都涉及了内存的分配,因此有对应的delete、operator delete(可重载)。


四、placement new(二)

在placement new(一)中我们从new、express new、operator new、placement new出发讲述了关于placement new在整个故事中解决的问题和用法。在实际应用中,placement new还有一种用法:placement new 是可以用来分配内存的。相比于直接使用 expression new, placement new可以接受额外的参数。比如,在分配内存的过程中,计算机没有足够的内存可以分配了,那么相比于expression new, placement new可以接受额外的的参数 nothrow来禁止在没有足够内存的情况下抛出异常:

int* p1 = new int; //分配失败则抛出异常
int* p2 = new(nothrow) int; //分配失败只会返回一个空指针

在实现上,placement new 其实调用的是operator new。实际上,placement new可以很多形式,括号内的参数也可以有很多种。不同的形式其实只是对应了不同的operator new的重载版本而已。

比如上面的例子中,

new(nothrow) 
调用
operator new(size_t size, nothrow_t&) noexcept;

而在placement new(一)中,

::new(men) Foo("banana", 20)
调用
operator new(size_t size, void*);

注意参数 size_t size 是编译器来传值的,所以在调用的时候只需要传一个参数就好。那这里为什么要使用全局的placement new来调用呢?原因就是这个版本的operator new是不允许用户进行重载的:

operator new(size_t size, void* address) {
	return address;
}

总结:

至此,所有的故事已经讲完,关于它们之间的关系,以下图一图概之:

![在这里插入图片描述](https://img-blog.csdnimg.cn/45fd3cae611d49cf82b5afdebc7c4912.png

在SAP系统中创建物料 860000000000000082(物料描述为“8680月球广寒宫坦克车”)时,系统报出了一个错误 摘要:SAP系统创建物料"8680月球广寒宫坦克车"时出现"C+020"错误,提示评估区域C916未在物料分类账中设置为生产性。解决方案需进入"会计-控制-产品成本控制-实际成本核算"路径,将评估区域C916手动设置为生产性状态。该操作需由具备权限的系统管理员或FICO顾问执行,建议操作前进行数据备份或在测试环境验证。完成设置后即可重新创建物料。 阅读详情

相关推荐

QGIS基于快鸟(QuickBird)影像统计小麦种植面积

​01数据情况 本次选用某地4月份快鸟(QuickBird)影像为例,使用QGIS演示提取小麦播种情况,并按照200米*200米分区计算种植面积的过程。 注:本文对应的操作视频作为今天第二篇推送同时发布,习惯看视频的朋友可以点击观看。 附数据压缩包: 链接:https://pan.baidu.com/s/1JVUS8uAOR61RxC1n9Or9_w 提取码:qgis 含快鸟影像QB_subset.tif和200*200米分区矢量图层200Mgrid.shp。 ...

QGISClass的博客 1979

Expression 表达式目录树

Expression 表达式目录树

againknow的博客 585

STM32 移植FreeModbus详细过程

modbus是一个非常好的串口协议(当然也能用在网口上),它简洁、规范、强大。可以满足大部分的工业、嵌入式需求。 这里详细说下如何将freemodbus移植到stm32平台。我之前下载的版本是1.6,我会在我的下载链接里面加上1.6的freemodbus压缩包以及我已经移植好的stm32 for freemodbus版本。我上传的移植好的绝对没问题,经过多个板子测试。这里编译器使用的是keil5.28,单片机用的是STM32F103RCT6。 移植到VET6以及ZET6的话只需要改一下选择的芯片就好,改法:

qq_40305944的博客 8万+

Mybatis Plus 数据权限插件坑点

原创 rookie发表于广东。

竹林幽深 848

别再为GEE注册发愁了!用谷歌Cloud Project,5分钟搞定谷歌地球引擎账户(保姆级图文)

本文提供2024最新版谷歌地球引擎(GEE)账户注册的保姆级教程,通过谷歌Cloud Project快速完成注册,5分钟极速通关。详细解析注册前的准备、分步图解流程、常见误区及解决方案,帮助用户避开90%的注册陷阱,直达GEE核心功能区。

weixin_42557830的博客 199

为什么BGA焊点总在四个角先坏?一次热-振耦合仿真给你讲明白

本文通过Ansys Workbench进行热-振耦合仿真,深入分析了BGA焊点在四个角点先失效的机理。研究发现,角点焊点由于CTE失配、振动载荷放大及几何应力集中等因素,承受的等效塑性应变和动态应力显著高于中心区域,导致其疲劳寿命大幅降低。仿真数据揭示了角点焊点的应力分布和蠕变特性,为电子封装可靠性设计提供了重要依据。

weixin_42535461的博客 439

MybatisPlus 实现数据拦截

基于配置文件实现(关键key存储在配置文件,通过读取配置文件来实现动态拼接sql)3、创建拦截器重写InnerInterceptor接口,重写查询方法。2、配置枚举类配置文件 EDataPermissionType。4、创建处理类,动态拼接sql片段,设置where。5、将拦截器加到mybatis-plus插件中。注:id用以区分是小程序还是应用程序。6、使用的位置加注解即可生效。app:应用程序类型权限控制。mp:小程序类型权限控制。

just love code 7285

26届数字IC秋招笔试面试全攻略-UVM验证方法学精要

本文介绍了数字IC验证中UVM方法学的核心内容,包括组件架构、Sequence机制和寄存器模型。UVM提供env、agent、driver等组件构建验证环境;Sequence机制通过item、sequence和sequencer实现激励生成与控制;寄存器模型则通过reg_block、reg等组件管理DUT寄存器。文章详细说明了各模块的功能、交互关系和代码实现,为IC验证工程师提供了完整的UVM应用指南,重点突出了验证环境的搭建方法、激励生成流程和寄存器抽象技术。这些内容是数字IC秋招笔试面试中的高频考点。

2301_79143213的博客 725

mybatis-plus sql改写插件(二)

去年基于租户过滤,权限拦截都是where tenant_id = XXX, 今年遇到一个物联网行业的需求,需要实现的是where sn in {sns} , 可能存在子查询,例如: where xinfo_id in (select x_id from x_info where sn in {sns}) ,这里的变量存在5个:xinfo_id,x_id,x_info,sn,sns 会有如下参数对象定义。

大蚊子 518

PMP--工件

合同是指对双方都有约束力的协议,强制卖方提供规定的产品、服务或结果,以及强制买方支付相应的费用。它还可能包括处于中间状态的特性,例如已设计但尚未构建的特性、质量保证中的特性或测试中的特性。RACI 矩阵是一种常见的方法,用于显示执行、担责、咨询或知情,且与项目活动、决策和可交付物有关的干系人。计划是提议的实现某种目标的方式。在项目开始前或开始时创建的文件,涉及与项目有关的战略、商业或高层级的信息。或 © 它们是用特定方法创建的结果,例如,虽然成本估算结果是一个重要工件,它们是用不同估算方法得出的结果。

stqer的博客 1665

Unitree G1仿人机器人协同搬箱:从仿真搭建到多机协同部署完整指南

Unitree G1仿人机器人协同搬箱技术方案摘要 本文提供了一套完整的Unitree G1人形机器人协同搬箱解决方案,涵盖从仿真环境搭建到多机协同部署的全流程。核心内容包括: 双仿真环境配置:同时搭建Gazebo+ROS Noetic基础仿真环境与Isaac Gym+Mujoco高性能RL训练环境,支持Sim2Sim迁移验证 训练框架搭建:详细配置unitree_rl_gym框架,提供PPO算法定制化训练流程和参数优化方案 数据集应用:整合宇树开源真机数据集(UnifoLM-WBT-Dataset)~~~

huanghm88的专栏 471

第十五节:Expression表达式目录树(与委托的区别、自行拼接、总结几类实例间的拷贝)...

一. 基本介绍 回忆: 最早接触到表达式目录树(Expression)可能要追溯到几年前使用EF早期的时候,发现where方法里的参数是Expression<Func<T,bool>>这么一个类型,当初不是很理解,只是知道传入lambda表达式使用即可,对于Expression和里面的Func<T,bool>到底是怎么一种关系,都不清楚。   今天,带着...

weixin_34256074的博客 290

OpenClaw 保姆级 window部署

OpenClaw 保姆级 window部署 养龙虾

cs821984831的博客 297

从零学习表达式树(七)【值类型、引用类型、泛型、集合、调用函数】

C# 表达式树中,定义一个变量,使用。创建变量结点的方法有两种,Expression . Parameter() Expression . Variable() // 另外,定义一个常量可以使用 Expression.Constant()。两种方式都是生成类型和Variable()都具有两个重载。他们创建一个节点,该节点可用于标识表达式树中的参数或变量。用于在块内声明局部变量。用于声明输入值的参数。先看第一种}从代码来看,没有区别。再看看具有两个参数的重载}}

极客神殿 728

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程

有看过我之前发表过的C#相关文章分享和阅读过我代码的朋友们可能会在我的代码里面经常看到各种各样的λ表达式动态拼接,C#的λ表达式树是一个好东西,也是别的语言学不来的,熟悉掌握λ表达式就能够实现各种linq场景的个性化操作,如动态拼接查询条件、排序方式等,也能够实现替代反射的高性能操作,比如我们常用到的IQueryable和IEnumerable,每个扩展方法就全是λ表达式树。 之前也分享过几篇关于λ表达式的文章,但鉴于还是有一部分初学者对于λ表达式树不太懂的,而网络上关于λ表达式树的科普不是很全面,今天就

wori的专栏 2077

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

MyBatis Plus 拦截器实现数据权限控制

上篇文章介绍的MyBatis Plus 插件实际上就是用拦截器实现的,MyBatis Plus拦截器对MyBatis的拦截器进行了包装处理,操作起来更加方便MyBatis Plus提供的InnerInterceptor接口提供了如下方法,主要包括:在查询之前执行,在更新之前执行,在SQL准备之前执行/**return;} } /*** 查询。

progammer10086的博客 5732

在EF中使用Expression自动生成p=>new Entity(){X="",Y="",..}格式的Lambda表达式灵活实现按需更新...

一、基本介绍 ...

weixin_30762087的博客 360

C# .Net学习笔记—— Expression 表达式目录树

C#学习,表达式目录树

weixin_46711336的博客 2323

.NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)

https://blog.youkuaiyun.com/WPwalter/article/details/83744781 大家都说反射耗性能,但是到底有多耗性能,哪些反射方法更耗性能;这些问题却没有统一的描述。 本文将用数据说明反射各个方法和替代方法的性能差异,并提供一些反射代码的编写建议。为了解决反射的性能问题,你可以遵循本文采用的各种方案。 本文内容 反射各方法的性能数据 反射的高性能开发建议 创...

坚定的走 6168

Orm框架开发之NewExpression合并问题

之前都是看别人写博客,自己没有写博客的习惯.在工作的过程中,总是会碰到许多的技术问题.有很多时候想记录下来,后面一直有许多的问题等着解决.总想着等系统完成了,再回头总结下.往往结果就把这事抛到脑后了. 总觉得不能一直这样哈.今天简单记一下吧.有表达不清楚的地方,多多包涵. 最近在研究.net orm框架.想开发一套更好用的Orm框架.别嫌轮子多.碰到一个Expression合并的问题. 一....

weixin_30929195的博客 296

C#使用Expression.Call动态调用方法

怎么动态调用方法呢?可以用委托啊,如果方法名称是文本或者写在配置文件里呢?可以用反射来调用,这需要写一堆代码,而且反射效率不是很高。这时就可以使用C#的Expression.Call方法来动态调用,这不是表达式树吗?是的,它也可以动态调用,它的本质上还是委托。Expression.Call是.NET Framework4.0之后出现的特性,可以用来动态调用方法。下面举例说明它的使用。首先试...

zls365365的博客 1494

Expression 构建表达式树做类型的转换

表达式树常用在需要动态生成表达式的地方,比如将QueryPrameter对象动态的生成查询表达式,通过EF获取查询结果。 对表达式树的编写就是一个拼装过程,这点需要注意。 以下方法Map,动态的生成new一个对象的表达式。 Demo中使用该方法生成“Person对象转为DTO对象”的表达式。 namespace ConsoleDemo { class Program { static void Main(string[] args) {

m0_62367247的博客 587

java new expression_yii2 new Expression

yii2 new Expression,new Expression,new \yii\db\Expression,yii2FIND_IN_SET,yii sql,yii\db\Expression演示一段yii2 关于 new Expression 或者 new \yii\db\Expression 的sql语句首先use yii\db\Expression;$params = ['1','2...

weixin_42502419的博客 216

yii2 new Expression

yii2 new Expression,new Expression,new \yii\db\Expression,yii2FIND_IN_SET,yii sql,yii\db\Expression 演示一段yii2 关于 new Expression 或者 new \yii\db\Exp...

592

C++——禁止对象产生在heap

有些类的对象我们并不希望在堆区开辟,就需要禁止对象在heap开辟的这样的手法。

JAN6055的博客 728

placement new 时出现错误:error C2660: “operator new”: 函数不接受 2 个参数

包含头文件new.h即可 #include <new.h>

gergul的专栏 2428

C++ Placement语法解析[译]

Placement syntax From Wikipedia, the free encyclopedia   (Redirected from Placement new) In the C++ programming language, placement syntax allows programmers to explicitly specify the mem

origin_lee的专栏 1583
上一篇: Linux下Makefile操作
下一篇: C++ 手写智能指针 shared_ptr
zsiming
博客等级 码龄7年 43粉丝 74原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值