问题 堆分配的const对象与非const对象有何不同?


在C ++中,它是可能的 在堆上分配一个const对象

const Class* object = new const Class();
const_cast<Class*>( object )->NonConstMethod(); // UB

因此,写入对象的尝试将是UB。

我不知道这样的对象将如何与未声明的堆分配对象不同 const

const Class* object = new Class();

我的意思是当我在堆栈上分配一个对象时,它会去 自动存储 这是特定于实现的,因此可能存在一些允许分配的特定于实现的方法 const 对象以某种特殊的方式在写入对象时会产生UB。

然而每当我使用时 new 编译器需要发出 operator new() 函数invokation和该函数不可能做任何不同的事情 - 它只是以统一的方式分配内存,无论是否存在 const 在我的代码中。

怎么样? const 堆分配的对象不同于非const 一,如果我尝试修改它,未定义的行为怎么可能?


4771
2017-12-02 12:03


起源

“这会产生UB“我不认为你理解”UB“是什么意思。 - curiousguy


答案:


没有区别 物体。使用的变量的(编译时)类型有所不同 参考 到记忆区。

这只是语义摩擦:变量不同,数据位使用的实际内存是const / volatile不可知的。

对于一个非常 有趣和启发 描述类似语义摩擦的故事,请参阅Eric Lippert撰写的这一史上最喜欢的答案:

关于未定义的行为

以非const方式处理const数据可能会导致Undefined Behavior,因为允许编译器根据 知识 const变量不会改变1。改变它(例如通过 const_cast<>)可能导致错误的结果,因为编译器的假设被积极否定。

1 注意 volatile 在const变量可以同时修改的情况下可以提供帮助。你可以看到如何 const 是一个'本地' 不会/不会碰 promis,而 volatile 说: '不要假设这不会改变,即使它没有被写入此代码段'。 `


4
2017-12-02 12:12



为UB索赔增加了理由 - sehe


它是不同的,因为创建的对象具有不同的类型(const Class 而不仅仅是 Class),它是未定义的行为,因为标准是这样说的。

那是短版本。没有理由。 (如果有的话,反过来就是真的。没有理由让某些东西成为UB。UB是默认状态。只有当有某种原因得到明确的定义时才会出现这种情况)

至于它意味着什么 在实践中,或者如果将对象视为非const,它是否真的会导致问题,硬件不太可能做任何不同的事情。 const对象显然不会写入某种只读内存(因为这是不可能的),并且一旦分配了对象,它所在的内存页面可能不会被标记为只读。

但是允许编译器 承担 该对象是const。因此,如果保证对象不变,它可以以合法的方式优化或转换代码,但如果对象在中途被修改则会中断。

它实际上并不是关于对象如何存储在硬件中。 Const或没有const很少会对硬件级别产生影响。但它在类型系统中有所不同,它对编译器如何转换代码产生了影响。

如果你告诉编译器一个对象是const,那么编译器会相信你,并在假设对象是const的情况下生成代码。


9
2017-12-02 12:10



+1,但我认为这远远超出“假设”。一个常数 指针 意味着你保证不会修改它(通过那个指针)所以它可能是常量,通过该指针查看。一个常数 目的另一方面,就是这样,不变。你这么说,明确。这意味着它 不能 永远改变,(除非程序员故意愚蠢)。这意味着编译器可能不仅仅是假设,而是必须知道并且必须考虑到这一点,例如多次调用同一个成员将始终是具有相同副作用的完全相同的结果(否则是对象 不是 常量)。 - Damon
...这带来了另一个有趣的问题。您可以从非常不是const的类型创建一个const对象(例如,从成员函数内部修改全局状态的东西)。尽管如此,创建一个这种类型的const对象仍然是合法的:( - Damon
编译器没有 有 考虑调用相同的成员函数将返回相同的结果(并且对于const对象也不能保证这是真的)。 - jalf
@Damon:jalf是对的;当使用相同的参数调用时始终返回相同结果的方法称为“纯”方法;没有任何要求const对象上的每个方法都是纯粹的!想象一下例如一个带有返回当前时间的方法的const对象。对象中的任何内容都不会发生变异,但该方法仍然可以返回任意不同的结果。 - Eric Lippert
@Damon“const指针意味着您保证不会修改它“不,不。 - curiousguy


使用当前的编译器,没有技术差异。未定义的行为包括奇迹般的工作。

我朦胧地记得,有一个建议让const限定的构造函数允许特殊的大小写实例 const 施工后立即;这将是有用的,例如对于字符串类,如果它们不需要期望字符串增长,它将分配更少的内存。


1
2017-12-02 12:11





它依赖于实现,但它可能没有任何不同。它 可以 被修改。但编译器会拒绝尝试的代码。

const 更多的是拒绝编译修改对象的代码,而不是实际上无法通过任何方式进行修改。编译器的一个注释是“不要让我试图错误地改变它”。


1
2017-12-02 12:14



“这是编译器的一个注释“声明一个const对象也是 诺言 到编译器。 - curiousguy


const和非const对象之间没有任何区别。在你的例子中也没有未定义的行为。您期望UB是什么?通过调用非const函数,您将获得您期望的行为。

让我提醒你,可以声明一些字段 易变的 这样对象就不是整体的const。没有提到你甚至可以在syuch中滥用编译器不会意识到你的成员函数的具体非常量:

class A {
public:
    A() : value_(0) {}
    void abuse() const { const_cast<A*>(this)->value_  = 1 ; }
    int value_;
    };

void test() {
    const A a;
    std::cout << a.value_ << std::endl;
    a.abuse() ;
    std::cout << a.value_ << std::endl;
    }

我们可能会得到UB。


-1
2017-12-02 17:02



请提供标准报价。 - curiousguy