通过 const 指针修改用 new 创建的对象是否合法?

Is it legal to modify an object created with new through a const pointer?

提问人:Luchian Grigore 提问时间:4/2/2014 最后编辑:CommunityLuchian Grigore 更新时间:4/6/2014 访问量:691

问:

所以这个答案让我想到了将结果分配给指向 .AFAIK,在这种情况下,您没有理由不能合法地消除恒常性并实际修改对象:newconstconst_cast

struct X{int x;};

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

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

struct X{};

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

它编译了!!

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

C++ 常量 language-lawyer

评论

0赞 Lightness Races in Orbit 4/2/2014
new obviously doesn't create a const object.为什么这么说呢?
2赞 Lightness Races in Orbit 4/2/2014
new doesn't create a const object为什么这么说呢?
0赞 Steve Jessop 4/2/2014
在 Luchian 得到他的启示之前,如果 是非 const 限定类型并且您编写 ,那么显然不会创建 const 对象 :-)在这种情况下,将 const 从中强制转换并使用结果来修改对象是合法的。Tconst T *ptr = new T;newptr
0赞 Lightness Races in Orbit 4/2/2014
@SteveJessop:当然,但这与.好像我在这里没有看到任何令人惊讶的东西。newint obj = 2; const int* ptr = &obj; *const_cast<int*>(ptr) = 3;

答:

11赞 Lightness Races in Orbit 4/2/2014 #1

const是类型的一部分。无论您为对象分配动态、静态还是自动存储持续时间都无关紧要。它仍然是.抛弃这种性并改变对象仍然是一个未定义的操作。constconst

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-expression 创建。] [注意:type-id 可以是 cv 限定类型,在这种情况下,new-expression 创建的对象具有 cv 限定类型。 ] [..]

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

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

不知道你还期待什么。我不能说我自己曾经这样做过,但我看不出有什么特别的理由不这样做。可能有一些技术社会学家可以告诉你,我们很少动态地分配一些东西,只是为了把它当作不可改变的。

评论

0赞 Luchian Grigore 4/2/2014
我只是从未见过任何地方(但我想我知道tbh,只是觉得这很有趣)new const ...
0赞 Lightness Races in Orbit 4/2/2014
@LuchianGrigore:我不能说我自己曾经使用过它,但我不明白为什么它应该被视为比“在堆栈上”创建的对象更无效。const
1赞 zch 4/2/2014
7.1.6.1/4 上也有示例
1赞 Johannes Schaub - litb 4/2/2014
这有效吗? ?int x = 0; const int *px = new (&x) const int(0); x = 1; cout << *px;
2赞 Ben Voigt 4/2/2014
@JohannesSchaub-litb:我认为这是未定义的行为,因为你正在使用一个不再存在的对象(它的存储已被重用)。当然,您必须销毁并使用放置 new 来将原始类型的对象 , 放回它,以免它超出范围。x = 1;*pxint
17赞 R. Martinho Fernandes 4/2/2014 #2

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

如果你要求创建一个对象,你会得到一个对象。newconstconst

你没有理由不能合法地消除恒常性并实际修改对象。const_cast

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

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

评论

0赞 LihO 4/2/2014
+1 指出,因为你可以做某事并不意味着你应该去做
0赞 Luchian Grigore 4/2/2014
那么指向 const 对象的指针是什么?即使不创建新对象?const X* x = new Xnew X
0赞 Mysticial 4/2/2014
需要明确的是,你被允许到那个常量指针,对吧?否则,你如何摆脱它?delete
1赞 LihO 4/2/2014
@Mysticial:我想说的是,不管什么占据了内存,它都会清理内存。AFAIK 该类型仅用于确定应清理对象的方式(析构函数)并确定将要释放的内存块的大小。delete
2赞 Mark Ransom 4/2/2014
@LuchianGrigore,如果你创建一个非常量对象,然后将它分配给一个常量对象指针,我相信你可以把它const_cast回一个非常量指针并修改它。但我同意这个答案,你已经创建了一个 const 对象,所以它必须保留。
0赞 david.pfx 4/6/2014 #3

我看待这个问题的方式是:

  • X和 指向它们的指针是不同的类型const X
  • 有一个从 到 的隐式转换,但不是相反X*const X*
  • 因此,以下情况是合法的,并且在每种情况下都具有相同的类型和行为x

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

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