堆分配的 const 对象与非 const 对象有何不同?
在 C++ 中,可以在堆上分配 const 对象:
const Class* object = new const Class();
const_cast<Class*>( object )->NonConstMethod(); // UB
这样尝试写入对象将是 UB。
我不明白这样的对象与未声明 const 的堆分配对象有何不同:
const Class* object = new Class();
我的意思是,当我在堆栈上分配一个对象时,它会进入自动存储 em> 这是特定于实现的,因此可能有一些特定于实现的方法,允许以某种特殊的方式分配 const 对象,当我写入对象时,这会产生 UB。
然而,每当我使用 new 时,编译器都需要发出operator new() 函数调用,并且该函数不可能做任何不同的事情 - 它只是以统一的方式分配内存不管我的代码中是否有 const 。
const
堆分配对象与非 const
对象有何不同?如果我尝试修改它,未定义的行为如何可能发生?
In C++ it is possible to allocate a const object on heap:
const Class* object = new const Class();
const_cast<Class*>( object )->NonConstMethod(); // UB
so that attempt to write into an object will be UB.
I don't get how such an object will be different from a heap-allocated object that is not declared const
:
const Class* object = new Class();
I mean when I allocate an object on stack it goes to automatic storage which is implementation-specific and so there might be some implementation-specific means that would allow allocating const
objects in some special way that would yield UB when I write to an object.
Yet whenever I use new
the compiler is required to emit operator new()
function invokation and that function can't possibly do anything different - it just allocates memory in a uniform manner regardless of whether there was const
in my code.
How is a const
heap-allocated object different from a non-const
one and how is undefined behavior possible if I try to modify it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
它取决于实现,但可能没有任何不同。它可以被修改。但编译器会拒绝尝试这样做的代码。
const 更多的是拒绝编译修改对象的代码,而不是实际上使其无法以任何方式修改。这是给编译器的一条注释,上面写着“不要让我尝试错误地更改它”。
It's implementation-dependent, but it probably isn't any different. It could be modified. But the compiler will reject code that tries to.
const
is more about refusing to compile code that modifies the object than about actually making it impossible to modify by any means. It's a note to the compiler that says "don't let me try to change this by mistake."const 对象和非 const 对象之间没有任何区别。您的示例中也没有未定义的行为。你期望什么UB?通过调用非常量函数,您将获得您期望的行为。
让我提醒您,某些字段可能被声明为可变的,以便对象整体上不是 const。更不用说你甚至可以以编译器不知道你的成员函数的具体非常量的方式滥用:
我们可能会得到 UB。
There is no difference whatsoever between const and non-const object. There is no undefined behaviour neither in your exemple. What UB do you expect ? By calling the non-const function, you'll get the behaviour you expect from it.
Let me remind you that some fields may be declared mutable so that the object is not const as a whole. Not mentioning that you can even abuse in syuch a way that the compiler will not be aware of concrete non-constness of your member function:
There we maybe get UB.
这是不同的,因为创建的对象具有不同的类型(
const Class
而不仅仅是Class
),并且它是未定义的行为,因为标准是这么说的。这是简短的版本。不一定有理由。 (如果有的话,反之亦然。某些事物成为 UB 不一定有原因。UB 是默认状态。只有当有原因时某些事物才会变得明确)
至于它的含义 在实践中,或者如果您将对象视为非常量,是否实际上会导致问题,硬件不太可能做任何不同的事情。 const 对象显然不会被写入某种只读内存(因为这是不可能的),并且一旦分配了该对象,它所在的内存页可能不会被标记为只读。
但编译器可以假设该对象是const。因此,如果保证对象不变,它可能会以合法的方式优化或转换代码,但如果对象中途修改,则会中断。
这实际上与对象如何存储在硬件中无关。常量或不常量在硬件级别上很少产生差异。但它对类型系统产生了影响,并且对编译器转换代码的方式产生了影响。
如果您告诉编译器某个对象是 const,那么编译器就会相信您,并假设该对象是 const 来生成代码。
It's different because the created object has a different type (
const Class
instead of justClass
), and it is undefined behavior because the standard says so.That's the short version. There doesn't have to be a reason. (if anything, the inverse is true. There doesn't have to be a reason for something being UB. UB is the default state. It's only when there is a reason that something becomes well-defined)
As for what it means in practice, or whether it can actually cause problems if you treat the object as non-const, the hardware is unlikely to do anything different. The const object obviously won't be written into some kind of read-only memory (because that's not possible), and the memory page it's in probably isn't going to be flagged as read-only once the object has been allocated.
But the compiler is allowed to assume that the object is const. So it may optimize or transform the code in a way that is legal if the object is guaranteed to be unchanging, but which breaks if the object is modified halfway through.
It's really not about how the object is stored in the hardware. Const or no const rarely makes a difference on the hardware level. But it makes a difference in the type system, and it makes a difference in how the compiler is able to transform the code.
If you tell the compiler that an object is const, then the compiler believes you, and generates code on the assumption that the object is const.
对象没有区别。用于引用内存区域的变量(编译时)类型存在差异。
这只是语义摩擦:变量不同,数据位使用的实际内存与常量/易失性无关。
对于描述类似语义摩擦的非常有趣且具有启发性的故事,请参阅埃里克·利珀特 (Eric Lippert) 一直以来最受欢迎的答案:
关于未定义行为
以非常量方式处理 const 数据可能会导致未定义行为,因为编译器允许基于 const 变量不会改变的知识进行某些优化1。尽管如此,更改它(例如通过
const_cast<>
)可能会导致错误的结果,因为编译器的假设被主动否定。1 请注意,
易失性
可以在 const 变量可以同时修改的情况下提供帮助。您可以看到const
如何成为“本地”不会/无法触及的承诺,而volatile
则表示:'don “不要假设这不会改变,即使它没有被写入此代码段中”。`
There is no difference in the object. There is a difference in the (compile-time) type of the variable(s) used to refer to the memory area.
This is semantic friction only: the variable is different, the actual memory used by the data bits is const/volatile agnostic.
For a very amusing and enlightening story describing similar semantic friction see this all-time-favourite answer by Eric Lippert:
On the Undefined Behaviour
Treating const data in a non-const way can lead to Undefined Behaviour, because the compiler is allowed to do certain optimizations based on the knowledge that a const variable won't change1. Changing it none-the-less (e.g. by
const_cast<>
) can lead to erronous results because the compiler's assumptions are actively negated.1 Note that
volatile
is there to help in cases where const variables can get modified concurrently. You can see howconst
is a 'local' won't/can't touch promis, whereasvolatile
says: 'don't assume this won't change, even though it is not being written to in this code segment'.`
对于当前的编译器来说,不存在技术差异。未定义的行为包括奇迹般地工作的事情。
我依稀记得有人建议使用 const 限定的构造函数,允许特殊情况下的实例,其中对象在构造后立即为 const ;这对于例如字符串类来说很有用,如果它们不需要预期字符串增长,那么它们将分配更少的内存。
With current compilers, there is no technical difference. Undefined Behaviour includes things miraculously working.
I dimly remember that there was a proposal to have const-qualified constructors that would allow special-casing instances where the object would be
const
immediately after construction; that would be useful e.g. for string classes that would allocate less memory if they didn't need to expect the string to grow.