用相同类型的对象覆盖对象

cur*_*guy 4 c++ const placement-new memcpy language-lawyer

以下是否定义明确?

#include <iostream>
#include <string.h>

using namespace std;

struct Const {
    const int i; 
    Const (int i) : i(i) {}
    int get0() { return 0; } // best accessor ever!
};

int main() {
    Const *q,*p = new Const(1);
    new (p) Const(2);
    memcpy (&q, &p, sizeof p);
    cout << q->i;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

注意,在构造第二个之后Const,p在语义上(故意?)指向新对象,并且第一个已经消失,因此它可以"用作void*".但是第二个对象是在完全相同的地址构造的,因此位模式p表示新对象的地址.

评论

new (p) Const(2)擦除存储在的旧对象p,因此指针不再有效,除非作为指向storage(void*)的指针.

我想恢复p作为一个的价值Const*.

评论2

在任何一个p->~Const()memset (p, 0, sizeof *p)很明显p没有指向有效对象之后,因此p只能用作存储(void*char*)的指针,例如重建另一个对象.此时p->get0()是不允许的.

这里旧对象的拆除是由新对象的构造函数完成的,但我认为这不会产生影响.

我的直觉是:在任何情况下,旧对象都消失了,并p指向旧对象,而不是新对象.

我正在寻找基于该标准的确认或驳斥.

也可以看看

我在C和C++中提出了与指针基本相同的问题:

在回答"这太荒谬了"之前,请先阅读这些讨论.

Ton*_*roy 5

(将社区维基纳入dyp的评论3.8/7是非常重要的;虽然我之前的分析是正确的,我会说很多相同的事情,关于破坏的代码,我自己忽略了3.8/7)

Const *q,*p = new Const(1);
new (p) Const(2);
Run Code Online (Sandbox Code Playgroud)

new(p) Const(2);行将覆盖已构造的对象Const(1).

memcpy (&q, &p, sizeof p);
Run Code Online (Sandbox Code Playgroud)

这相当于q = p;.

cout << q->i;
Run Code Online (Sandbox Code Playgroud)

这将访问该q->i成员2.

有些值得注意的事情是:

  • std::memcpy是一种难以分配的p方式q......虽然在3.9/3下是合法的:

对于任何平凡复制的类型T,如果两个指针T指向不同T对象obj1obj2,其中既不obj1也不obj2是碱基-类子对象,如果构成底层字节(1.7)obj1被复制到obj2,obj2随后应保持相同的值obj1.[例如:

T* t1p;
T* t2p;
// provided that t2p points to an initialized object ...
std::memcpy(t1p, t2p, sizeof(T));
// at this point, every subobject of trivially copyable type in *t1p contains
// the same value as the corresponding subobject in *t2p
Run Code Online (Sandbox Code Playgroud)
  • 只要程序不依赖于前者析构函数的副作用,就可以覆盖旧Const(1)对象Const(2).

  • (正如dyp在下面的评论中指出的)在3.8/7的第三点下,持续访问Const(2)使用对象p是非法的:

指向原始对象的指针可用于操纵新对象,如果......

  • 原始对象的类型不是const-qualified,如果是类类型,则不包含任何类型为const-qualified或引用类型的非静态数据成员...
  • 使用q- 而不是p- 访问i可能是必要的,以避免基于推测的知识的编译器优化i.

至于你的评论:

注意,在构造第二个之后Const,p在语义上(故意?)指向新对象,并且第一个已经消失,因此它可以"用作void*".

鉴于您在包含的地址中放置了一个新对象p,p大多数肯定会指向新创建的对象,并且非常有意,但它不能用于在3.8/7下操作该对象,如上所述.

鉴于你似乎有一个"语义指向"的概念,这个概念在C++中没有定义,这个陈述的部分真实在你自己的脑海里.

"在构建第二个之后Const,p...... 可以使用"作为一个void*"毫无意义"......它不像以前那样可以用作任何东西.

但是第二个对象是在完全相同的地址构造的,因此位模式p表示新对象的地址.

当然,但是你的评论表明你认为"位模式"在某种程度上与指针的不同,适用于赋值=,这是不正确的.

new (p) Const(2)擦除存储在的旧对象p,因此指针不再有效,除非作为指向storage(void*)的指针.

"擦除"对它来说是一个奇怪的术语......覆盖会更有意义.正如上面提到并解释的dyp,3.8/7表示你不应该p在放置new之后"操纵"对象指向,但是指针的值和类型不受placmeent new的影响.就像你可以f(void*)使用指向任何类型的指针调用一样,放置 - new不需要知道或关心p表达式的类型.

在任何一个p->~Const()memset (p, 0, sizeof *p)很明显p没有指向有效对象之后,因此p只能用作存储(void*char*)的指针,例如重建另一个对象.此时p->get0()是不允许的.

大多数情况都是如此,如果" p只能使用",则表示当时的值p而不是指针本身(当然也可以指定).并且你试图对void*/ char*thing 有点过于聪明- p仍然是一个Const*,即使它只用于不关心指针类型的placement new.

"我希望恢复p作为一个的价值Const*."

p首次初始化后,该值未更改.placement- new使用值 - 它不会修改它.没有什么可以恢复,因为什么都没有丢失.也就是说,dyp强调不需要使用p来操纵对象,所以虽然值没有丢失,但它也不能直接使用.

  • 位模式(*对象表示*)包含指针所用的简单可复制类型的值(*值表示*)(§3.9/ 4). (2认同)
  • @curiousguy在销毁对象后指向对象的指针是否仍然"有效"取决于"有效"的确切含义.尝试访问原始对象时取消引用它们是无效的,但是它们可以通过在同一位置构造新对象而变得有效.修改`const int`变量不是这里发生的事情.你用另一个变量替换变量,这当然是合法的.(例如,`a = b;`如果,`a :: some_const_variable`与`b :: some_const_variable`具有不同的值,则可以这样做.) (2认同)
  • @DavidSchwartz和cc Tony D:标准明确说明您可以在哪些条件下使用指向其存储已被重用以创建另一个对象的对象的指针.在OP的情况下,原始对象包含一个`const`数据成员,因此[basic.life] p7.3表示指针**在放置后不会**指向("引用")新对象. (2认同)
  • @curiousguy:如果你想使用C++标准或通用计算科学社区中使用的术语,这些术语都很好,讨论将具有其他读者可以轻易遵循的含义.但是我对使用你发明的术语来讨论C++并不感兴趣 - 这意味着其他人无法在没有跳过这些评论,帖子和答案以及重新阅读所有内容的情况下跟随你,因为他们偶然发现你的术语的回顾性定义. (2认同)
  • @curiousguy我不是在评论是否有一个符合你的确切概念的术语,只是抛出条款就好像它们对其他人意味着相同会产生更多混乱.如果你找不到一个,并且意味着一个可以被解除引用的指针,以便你访问它所拥有的地址的对象,比如说. (2认同)