【问题标题】:Is it legal to modify an object created with new through a const pointer?通过 const 指针修改用 new 创建的对象是否合法?
【发布时间】:2014-05-12 23:24:14
【问题描述】:

所以this answer 让我想到了将new 的结果分配给指向const 的指针的场景。 AFAIK,没有理由你不能合法地 const_cast constness 在这种情况下实际修改对象:

struct X{int x;};

//....
const X* x = new X;
const_cast<X*>(x)->x = 0; // okay

但后来我想 - 如果你真的想要 new 创建一个 const 对象怎么办。所以我尝试了

struct X{};

//....
const X* x = new const X;

它编译好了!!!

这是 GCC 扩展还是标准行为?我在实践中从未见过这种情况。如果它是标准的,我会尽可能开始使用它。

【问题讨论】:

  • new obviously doesn't create a const object. 为什么这么说?
  • @LightnessRacesinOrbit 已编辑。
  • new doesn't create a const object 为什么这么说?
  • 在 Luchian 得到他的启示之前,如果 T 是一个非 const 限定类型并且你写 const T *ptr = new T;那么 new 显然没有' t 创建一个 const 对象 :-) 在这种情况下,将 const 从ptr 中转换出来并使用结果来修改对象是合法的。
  • @SteveJessop:当然,但这与new 无关。就像int obj = 2; const int* ptr = &amp;obj; *const_cast&lt;int*&gt;(ptr) = 3; 我在这里没有看到任何令人惊讶的地方。

标签: c++ constants language-lawyer


【解决方案1】:

new 显然不会创建 const 对象(我希望如此)。

如果您要求new 创建一个const 对象,您将获得一个const 对象。

没有理由你不能合法地 const_cast 去掉常量并实际修改对象。

有。原因是语言规范明确地将其称为未定义的行为。所以,在某种程度上,你可以,但这几乎没有任何意义。

我不知道您对此有何期望,但如果您认为问题在于是否分配到只读内存中,那远不是重点。那没关系。编译器可以假设这样的对象不能相应地更改和优化,最终会得到意想不到的结果。

【讨论】:

  • +1 指出因为你可以做某事并不意味着你应该去做。
  • 所以const X* x = new X 是一个指向常量对象的指针?即使new X 没有创建新对象?
  • 明确一点,你允许delete那个const指针对吧?不然怎么摆脱呢?
  • @Mysticial:我会说delete 会清理内存,不管它占用的是什么。 AFAIK 该类型仅用于确定应清理对象的方式(析构函数)并确定即将释放的内存块的大小。
  • @LuchianGrigore,如果您创建一个非常量对象,然后将其分配给一个常量对象指针,我相信您能够将其 const_cast 回一个非常量指针并对其进行修改。但我同意这个答案,你已经创建了一个 const 对象,所以它必须保留。
【解决方案2】:

const 是类型的一部分。无论您为对象分配动态、静态还是自动存储持续时间都没有关系。它仍然是const。抛弃 constness 并改变对象仍然是未定义的操作。

constness 是类型系统为我们提供的一种抽象,用于在非可变对象周围实现安全性;它这样做在很大程度上是为了帮助我们与只读存储器进行交互,但这并不意味着它的语义仅限于这种存储器。事实上,C++甚至不知道什么是只读内存,什么不是只读内存。

除了可以从所有常规规则中推导出来,对于动态分配的对象也不例外 [lol],标准明确提到了这一点(尽管在注释中):

[C++03: 5.3.4/1]: new-expression 尝试创建 type-id (8.1) 或 new-type-id 的对象它被应用到。该对象的类型是分配的类型。此类型应为完整的对象类型,但不是抽象类类型或其数组(1.8、3.9、10.4)。 [注意: 因为引用不是对象,所以引用不能由 new-expressions 创建。 ] [注意: type-id 可能是 cv-qualified 类型,在这种情况下, 创建的对象>new-expression 具有 cv-qualified 类型。 ] [..]

[C++11: 5.3.4/1]: new-expression 尝试创建 type-id (8.1) 或 new-type-id 的对象它被应用到。该对象的类型是分配的类型。此类型应为完整的对象类型,但不是抽象类类型或其数组(1.8、3.9、10.4)。是否支持过度对齐的类型是实现定义的(3.11)。 [注意:因为引用不是对象,引用不能由new-expressions创建。 —结束注] [注: type-id 可能是 cv-qualified 类型,在这种情况下,对象由new-expression 有一个 cv-qualified 类型。 —end note ] [..]

[C++11: 7.1.6.1/4] 中还给出了一个用法示例。

不确定您还期望什么。我不能说我自己曾经这样做过,但我看不出有什么特别的理由不这样做。可能有一些技术社会学家可以告诉您统计数据,说明我们很少动态分配某些东西,只是将其视为不可变的。

【讨论】:

  • 我从来没有在任何地方看到过new const ...(但我想我知道,只是觉得这很有趣)
  • @LuchianGrigore:我不能说我自己曾经使用过它,但我不明白为什么它应该被视为比在堆栈上创建的 const 对象更有效",原来如此。
  • 7.1.6.1/4也有例子
  • 这有效吗? int x = 0; const int *px = new (&amp;x) const int(0); x = 1; cout &lt;&lt; *px; ?
  • @JohannesSchaub-litb:我认为x = 1; 是未定义的行为,因为您使用的对象不再存在(其存储已被重用)。当然,您必须销毁*px 并使用placement new 将原始类型的对象int 放回它超出范围之前。
【解决方案3】:

我的看法是:

  • Xconst X 以及指向它们的指针是不同的类型
  • 存在从 X*const X* 的隐式转换,但反之则不然
  • 因此以下是合法的,x 在每种情况下都具有相同的类型和行为

    常量 X* x = 新 X; 常量 X* x = 新常量 X;

剩下的唯一问题是在第二种情况下是否可能调用不同的分配器(可能在只读内存中)。答案是否定的,标准中没有这样的规定。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多