Placement new 的返回值与其操作数的强制转换值之间是不是存在(语义)差异?

     2023-02-21     179

关键词:

【中文标题】Placement new 的返回值与其操作数的强制转换值之间是不是存在(语义)差异?【英文标题】:Is there a (semantic) difference between the return value of placement new and the casted value of its operand?Placement new 的返回值与其操作数的强制转换值之间是否存在(语义)差异? 【发布时间】:2018-05-19 02:17:43 【问题描述】:

placement new 的返回值与其操作数的强制转换值之间是否存在(语义)差异?

struct Foo  ... ;
char buffer[...];

Foo *a = new(buffer) Foo;
Foo *b = reinterpret_cast<Foo *>(buffer);

ab 有什么不同吗?


编辑:根据 DaBler 的评论,如果使用 const/reference 成员,这个问题说明存在差异:Placement new and assignment of class with const member

所以,我的一点更新问题:如果Foo 没有 const 或引用成员,ab 是否有任何不同?

【问题讨论】:

新(缓冲区)Foo;将调用对象的构造函数;另一个没有。 @UKMonkey:对不起,也许我不够清楚。两条线都运行,首先是新的展示位置,然后是演员表。 是的,如果类包含 const 成员,则会有所不同。详情请见this question。 【参考方案1】:

只有a 可以安全地用于直接访问由放置new-expression 创建的Foo 对象(为了便于参考,我们将其称为x)。使用b 需要std::launder

a的值在[expr.new]/1中指定:

如果实体是非数组对象,new-expression的结果 是一个指向所创建对象的指针。

a 的值因此是“指向x 的指针”。当然,这个指针可以安全地用于访问x

reinterpret_cast&lt;Foo*&gt;(buffer) 将数组到指针的转换应用于buffer(请参阅[expr.reinterpret.cast]/1)。应用转换后的结果值为“指向buffer 的第一个元素的指针”。 这是一个对象指针的reinterpret_cast,指向不同类型的对象指针,被[expr.reinterpret.cast]/7定义为等同于static_cast&lt;Foo*&gt;(static_cast&lt;void*&gt;(buffer))

void* 的内部转换实际上是一种隐式转换。每[conv.ptr]/2,

指针的值不会因为这个转换而改变。

因此,内部转换产生一个void*,其值为“指向buffer的第一个元素的指针”。

外部演员表由[expr.static.cast]/13 管理,我已将其重新格式化为要点:

“指向 cv1 void 的指针”类型的纯右值可以转换为“指向 cv2 T 的指针”类型的纯右值,其中T是一个对象类型,并且 cv2 的 cv 限定与 cv1 相同或更高。

如果原始指针值表示内存中某个字节的地址A,而A不满足T的对齐要求, 那么得到的指针值是未指定的。

否则,如果原始指针值指向对象a,并且存在T类型的对象b(忽略cv-qualification) 即指针与a 可相互转换,结果是指向 b

否则指针值不会因转换而改变。

假设buffer 是适当对齐的(如果不是这样,您将在此之前遇到麻烦),第一个项目符号不适用。第二个项目符号同样不适用,因为这里没有pointer-interconvertiblity。紧接着我们击中了第三个子弹——“指针值没有被转换改变”并且仍然是“指向buffer的第一个元素的指针”。

因此,b 不指向Foo 对象x;它指向buffer 的第一个char 元素,即使它的类型是Foo*。因此不能用于访问x;尝试这样做会产生未定义的行为(对于非静态数据成员情况,从[expr.ref] 中省略;对于非静态成员函数情况,[class.mfct.non-static]/2)。

要从b 恢复指向x 的指针,可以使用std::launder

b = std::launder(b); // value of b is now "pointer to x"
                     // and can be used to access x

【讨论】:

【参考方案2】:

通过a 访问是合法的,而b 则不合法。来自[basic.compound]

两个对象 ab 在以下情况下是指针可互转换的:

它们是同一个对象,或者

一个是标准布局联合对象,另一个是该对象的非静态数据成员,或者

一个是标准布局类对象,另一个是该对象的第一个非静态数据成员,或者,如果该对象没有非静态数据成员,则该对象的第一个基类子对象([class .mem]),或

存在一个对象c,使得ac 是指针可互转换的,cb 是指针互转换的。

如果两个对象是指针可互转换的,那么它们具有相同的地址,并且可以通过reinterpret_­cast 从指向另一个对象的指针中获得指向其中一个对象的指针。 [ 注意:数组对象和它的第一个元素不是指针互转换的,即使它们具有相同的地址。 — 尾注 ]

它们不是同一个对象,不是联合,也不是彼此的子对象,因此不能指针互转换。

注意[expr.reinterpret.cast] 只保证reinterpret_cast&lt;char*&gt;(b) == buffer

对象指针可以显式转换为不同类型的对象指针。当对象指针类型的prvaluev 转换为对象指针类型“指向cv T的指针”时,结果为static_­cast&lt;cv T*&gt;(static_­cast&lt;cv void*&gt;(v))。 [ 注意:将“指向T1”的纯右值转换为“指向T2”的类型(其中T1T2 是对象类型,T2 的对齐要求不比T1) 并返回其原始类型会产生原始指针值。 — 尾注 ]

【讨论】:

javascript中使用new操作符实例化对象时构造函数有返回值的情况分析(代码片段)

...类的构造函数实例化对象作为类的构造函数使用时在不同返回值情况下的情况分析构造函数无返回值构造函数有返回值,返回值是非null的对象构造函数有返回值,返回值是函数构造函数有返回值,返回值不是对象也... 查看详情

C++ 构造“placement new”的用途是啥?

】C++构造“placementnew”的用途是啥?【英文标题】:WhatareusesoftheC++construct"placementnew"?C++构造“placementnew”的用途是什么?【发布时间】:2010-09-2615:37:48【问题描述】:我刚刚了解了名为“placementnew”的C++结构。它允许您... 查看详情

Haskell:返回与其前任的绝对差等于或小于某个值的列表项

】Haskell:返回与其前任的绝对差等于或小于某个值的列表项【英文标题】:Haskell:Returntheitemofalistwhichabsolutedifferencetoitspredecessorisequalorsmallerthanacertainvalue【发布时间】:2022-01-1609:58:30【问题描述】:假设我有一个列表:[1,2,1.2,1.8,... 查看详情

将placement new 与存储类一起使用时的额外构造

】将placementnew与存储类一起使用时的额外构造【英文标题】:Extraconstructionwhenusingplacementnewwithastorageclass【发布时间】:2020-01-2110:18:35【问题描述】:在我想避免动态内存分配的情况下,我将new运算符替换为本质上使用某些静态... 查看详情

我可以通过placement-new 覆盖一个const 对象吗?

】我可以通过placement-new覆盖一个const对象吗?【英文标题】:CanIoverwriteaconstobjectviaplacement-new?【发布时间】:2021-11-1511:01:54【问题描述】:Basic.life/8告诉我们,我们可以使用对象占用的存储空间在其生命周期结束后创建一个新对... 查看详情

可以使用placement-new 和vector::data() 替换向量中的元素吗?

】可以使用placement-new和vector::data()替换向量中的元素吗?【英文标题】:Canplacement-newandvector::data()beusedtoreplaceelementsinavector?【发布时间】:2012-10-0604:20:38【问题描述】:关于替换不可赋值的向量元素存在两个问题:C++UseUnassignable... 查看详情

c++c&c++内存管理(代码片段)

...newT[N]、delete、delete[]的实现原理)六、定位new表达式(placement-new)(初始化内存池分配出来的内存空间)七、常见面试题1.malloc/free和new/delete的区别(从 查看详情

构造函数以及this(代码片段)

...驼峰命名规则来区别普通的函数,构造函数实际上是通过返回一个this值来完成构造函数的创建的.这个ruternthis的操作由new这个操作符来完成,当然个人也可以手动来设置return的返回值,手动设置的返回值会覆盖由new所自动添加的... 查看详情

SIGBUS 何时使用 Placement new?

】SIGBUS何时使用Placementnew?【英文标题】:SIGBUSWhenUsingPlacementnew?【发布时间】:2018-02-0807:49:56【问题描述】:我有以下代码(已更新):#include<iostream>#include<cassert>#include<errno.h>#include<string.h>#include<sys/mman.h& 查看详情

需要帮助了解此操作

...?【问题讨论】:查看安置新操作员:parashift.com/c++-faq/placement-new.html【参考方案1】:这行代码 查看详情

不推荐通过引用分配 new 的返回值

】不推荐通过引用分配new的返回值【英文标题】:Assigningthereturnvalueofnewbyreferenceisdeprecated【发布时间】:2010-11-0808:42:37【问题描述】:我刚刚遇到了一个错误。当我尝试分配这样的对象时:$obj_md=newMDB2();我得到的错误是“不推荐... 查看详情

New-ADUser -POBox 返回“属性的值不在可接受的值范围内”

】New-ADUser-POBox返回“属性的值不在可接受的值范围内”【英文标题】:New-ADUser-POBoxreturns"Avaluefortheattributewasnotintheacceptablerangeofvalues"【发布时间】:2022-01-1818:43:21【问题描述】:我们有一个创建用户但有时会失败的脚本Ne... 查看详情

c++:new操作符(代码片段)

...放,释放利用操作符delete利用new创建的数据,会返回该数据对应的类型的指针2语法语法:new数据类型2.1基本语法new数据类型(值)测试代码:#include<iostream>#include<string>usingnamespacestd;//使用new在堆区分配一个i... 查看详情

js使用new操作符创建对象的方法分析

...new操作符调用函数,就只会简单的执行函数,并把函数的返回值赋给person1,所以上面的例子中person1的值是undefined,.如果我们使用new操作符调用构造函数,做了哪些事情呢?1.构造函数没有返回值使用new操作符调用函数,会隐式的... 查看详情

05-03字符串与其常用操作(代码片段)

...的操作连接分割split通过指定分隔符对字符串进行切片,返回一个listrsplitsplitlines:分割行partition返回三元组rpartition转换upper:全部转换为大写lower:全部转换为小写title:标题,首字母大写capitalize:首字母大写,常用于正文center:... 查看详情

javascript中构造函数的返回值问题和new对象的过程

首先明白一点:javascript中构造函数是不须要有返回值的,这一点跟java非常类似。能够觉得构造函数和普通函数的最大区别就是:构造函数中没有return语句,普通函数能够有return语句。构造函数中会使用thiskeyword定义成员... 查看详情

多态基础

...写是多态的基础父类作为形参(实参是子类对象)父类作为返回值(返回的是子类对象)在new一个对象时,用谁new谁,多态主要是作为实参或者返回值使用的向上转型父类引用指向子类对象自动向上转型调用的是子类重写父类的方法... 查看详情

javascript对象初探---返回对象的函数

...数来创建对象,这样就能执行某些预备工作,并已对象为返回值的函数。、functionher(){return{name:‘Jon‘};}然后我们调用her()来生成对象:vara=her();a.name;//Jonconsole.log(a.constructor);//functionObject(){} 实际 查看详情