【问题标题】:Overwriting an object with an object of same type用相同类型的对象覆盖对象
【发布时间】:2015-11-09 16:08:29
【问题描述】:

以下定义是否明确?

#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 不会在语义上(故意?)指向新对象,并且第一个已经消失,因此它可以“作为void*”使用。但是第二个对象是在完全相同的地址构造的,所以p的位模式代表了新对象的地址。

评论

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

我想将p 的值恢复为Const*

评论 2

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

这里旧对象的拆除是由新对象的构造函数完成的,但我认为这没有什么区别。

我的直觉是:无论如何,旧对象已经消失了,p 指向的是旧对象,而不是新对象。

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

另见

我在 C 和 C++ 中问过关于指针的基本相同的问题:

在回答“这很荒谬”之前,请阅读这些讨论。

【问题讨论】:

  • memcpy 应该等价于 q = p 简单的指针赋值,不是吗?
  • "赋值复制指针值,我只想复制它的位模式。"指针值您使用 memcpy 复制的位的值。
  • N4430 解决了类似的问题。
  • @curiousguy 有人可能会争辩说,未初始化的 int 是无效的,因为它是 UB 读取它。但是一旦初始化,它仍然有效。
  • @ChrisBeck:原始对象的析构函数没有被调用......只要程序的其余部分不依赖于析构函数的副作用,它就没有未定义的行为。请参阅 3.8/1 “T 类型对象的生命周期在以下情况下结束:...对象占用的存储空间被重用或释放”,以及 3.8/4 的 “任何依赖于关于析构函数产生的副作用有未定义的行为”.

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


【解决方案1】:

(将 community-wiki 纳入 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-&gt;i 成员,即2

比较值得注意的是:

  • std::memcpy 是一种将p 分配给q 的丑陋方式...尽管在 3.9/3 下是合法的:

对于任何可简单复制的类型T,如果指向T 的两个指针指向不同的T 对象obj1obj2,其中obj1obj2 都不是基类子对象,如果构成obj1 的底层字节(1.7)被复制到obj2obj2 应随后保持与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(2) 覆盖旧的Const(1) 对象,而它不依赖。

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

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

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

至于你的评论:

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

假设您在p 中包含的地址处放置了一个新对象,p 肯定会指向新创建的对象,并且是有意的,但它不能用于在 3.8/ 下操作该对象7 同上。

鉴于您似乎有一个 C++ 中未定义的“语义指向”概念,因此该部分陈述的真实性在您自己的脑海中。

'在构造第二个Const 之后,p...is usable "as a void*' 没有意义...它没有比之前更有用的东西了.

但是第二个对象是在完全相同的地址构造的,所以p的位模式代表了新对象的地址。

当然,但是您的 cmets 表明您认为“位模式”与指针的 不同,适用于 = 的赋值,这是不正确的。

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

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

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

大部分情况都是如此,如果“只能使用p”,您的意思是当时p的值,而不是指针本身(当然也可以分配给它)。而且你试图对 void* / char* 事情变得有点太聪明了 - p 仍然是 Const*,即使它只用于不关心指针类型的放置 new。

“我想将p 的值恢复为Const*。”

p 的值在首次初始化后未更改。 Placement-new 使用该值 - 它不会修改它。没有什么可以挽回的,因为什么都没有丢失。也就是说,dyp 强调不需要使用p 来操作对象,因此虽然该值没有丢失,但它也不能按需要直接使用。

【讨论】:

  • 位模式(对象表示)包含普通可复制类型的值(值表​​示)(§3.9/4),它是一个指针是。
  • @curiousguy 在对象被销毁后,指向对象的指针是否仍然“有效”取决于您所说的“有效”。如果取消引用以尝试访问原始对象,它们将是无效的,但它们可以通过在同一位置构造新对象而变得有效。修改 const int 变量并不是这里发生的事情。您正在用另一个变量替换变量,这当然是合法的。 (例如,a=b; 可以做到这一点,如果 a::some_const_variableb::some_const_variable 具有不同的值。)
  • @DavidSchwartz and cc Tony D:该标准明确规定了在哪些条件下您可以使用指向一个对象的指针,该对象的存储空间已被重用于创建另一个对象。在 OP 的情况下,原始对象包含一个 const 数据成员,因此 [basic.life]p7.3 表示指针 在新展示位置。
  • @curiousguy:如果您想使用 C++ 标准或通用计算科学社区中使用的术语,那很好,并且讨论将具有其他读者可以轻松理解的含义。但我只是对使用您发明的术语讨论 C++ 不感兴趣——这意味着其他人无法跟随您而不跳过这些 cmets、帖子和答案并重新阅读所有内容,因为他们偶然发现了您的术语的回顾性定义。
  • @curiousguy 我不是在评论是否有一个术语与您的确切概念相匹配,只是将术语四处乱扔,好像它们对其他人意味着相同会造成更多混乱。如果找不到,并且指的是可以取消引用的指针,以便您访问其地址所在的对象,请说。
【解决方案2】:

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

new (p) Const(2) 擦除存储在p 的旧对象

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

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

[N4431 §1.8/1]

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

所以在p 指向的位置有一个有效的对象,当从它分配qq 指向它。尽管如另一个答案中所述,不允许通过 p 访问它。

【讨论】:

  • 所以这应该可行,因为我没有更改类型?
  • 我猜是的,是的。如果你正在构造一个不同类型的对象,那么 UB 会使用错误类型的指针来访问它。
  • const int 采用不同的值这一事实可以吗?
  • const int 不会改变值。这是两个不同的const ints,恰好位于内存中的同一位置(在不同的时间)。
  • 从指针的角度看还是一个不断变化的const。
猜你喜欢
  • 2018-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-17
  • 2015-04-06
  • 2023-04-11
  • 1970-01-01
相关资源
最近更新 更多