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

时间:2015-08-17 05:26:39

标签: c++ const language-lawyer memcpy placement-new

以下是否定义明确?

#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;
}

请注意,在构建第二个Const之后,p在语义上(故意?)指向新对象,而第一个已经消失,因此可以使用&#34; as一个void*&#34;。但是第二个对象是在完全相同的地址构造的,因此p的位模式表示新对象的地址。

COMMENT

new (p) Const(2)删除存储在p 的旧对象,因此指针不再有效,除非作为指向存储的指针(void*)。< / p>

我希望将p的值恢复为Const*

评论2

p->~Const()memset (p, 0, sizeof *p)之后,很明显p未指向有效对象,因此p只能用作存储指针({{ 1}}或void*),例如重建另一个对象。此时不允许char*

这里旧物体的拆除是由新物体的构造者完成的,但我不认为这会产生影响。

我的直觉是:在任何情况下,旧对象都消失了,p->get0()指向旧对象,而不是新对象。

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

另见

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

请在回答之前阅读这些讨论&#34;这很荒谬&#34;。

2 个答案:

答案 0 :(得分:5)

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

Const *q,*p = new Const(1);
new (p) Const(2);

new(p) Const(2);行会覆盖使用Const(1)构建的对象。

memcpy (&q, &p, sizeof p);

这相当于q = p;

cout << q->i;

这会访问q->i成员2

有些值得注意的事情是:

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

对于任何简单的可复制类型T,如果指向T的两个指针指向不同的T对象obj1obj2,则obj1 } obj2是基类子对象,如果构成obj1的基础字节(1.7)被复制到obj2obj2随后将保持与{相同的值{1}}。 [例如:

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 覆盖旧的Const(1)对象。没有按&#39;吨

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

  

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

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

至于你的评论:

  

请注意,在构建第二个i之后,Const在语义上(故意?)指向新对象,而第一个已经消失,因此可以使用&#34; as p&#34;。

鉴于您在void*中包含的地址放置了一个新对象,p肯定会指向新创建的对象,并且非常有意,但它无法用于如上所述在3.8 / 7下操纵该对象。

鉴于你似乎有一个&#34;语义指向&#34;那些在C ++中没有定义的那部分声明的真实性在你自己的脑海里。

&#39;在构建第二个p后,Const ... 可用&#34;作为p&#39; 制作没有任何意义......它不像以前那样可以用作任何东西。

  

但第二个对象是在完全相同的地址构造的,因此void*的位模式代表新对象的地址。

当然,但你的评论表明你认为&#34;位模式&#34;在某种程度上与指针的不同,适用于p的赋值,这是不正确的。

  

=删除存储在new (p) Const(2)的旧对象,因此指针不再有效,除非作为指向存储的指针(p)。

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

  

pp->~Const()之后,很明显memset (p, 0, sizeof *p)未指向有效对象,因此p只能用作存储指针({{ 1}}或p),例如重建另一个对象。此时不允许void*

大部分内容都是正确的,如果只有&#34; char*才能使用&#34;你的意思是那时p->get0()的值而不是指针本身(当然也可以指定)。并且您尝试使用p / p事物过于聪明 - void*仍为char*,即使它仅使用p通过放置new并不关心指针类型。

  

&#34;我希望将Const*的值恢复为p。&#34;

Const*的值在首次初始化后未更改。展示位置 - p使用该值 - 它不会修改它。因为什么都没有丢失,没有什么可以恢复的。也就是说,dyp强调不需要使用new来操纵对象,因此虽然价值没有丢失,但也不能直接使用。

答案 1 :(得分:4)

这只是作为@Tony D答案的附录,关于

  

new (p) Const(2)删除存储在p

的旧对象

我认为你需要区分一个对象和一个“实例”的概念。

  

[...]对象是存储区域。[...]

     

[N4431§1.8/ 1]

因此指针p指向一个存储区域,其中包含一个“实例”的位模式,在放置新的之前,一些不同的位模式是一个不同的,但构造良好的“实例”的正确(相同)类型。

因此,在p指向的位置,有一个有效的对象,当从q分配q时,它指向它。虽然如其他答案所述,但不会通过p访问它。