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

时间:2011-12-02 12:03:10

标签: c++ memory-management const undefined-behavior const-correctness

在C ++中,可以allocate a const object on heap

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

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

我不知道这样的对象与未声明为const的堆分配对象的区别:

const Class* object = new Class();

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

然而,每当我使用new时,编译器都需要发出operator new()函数调用,并且该函数不可能做任何不同的事情 - 它只是以统一的方式分配内存,而不管是否有{ {1}}在我的代码中。

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

5 个答案:

答案 0 :(得分:9)

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

这是短版本。没有必要成为一个理由。 (如果有的话,反过来就是真的。没有必要成为UB的原因.UB是默认状态。只有当有某种东西变得明确的原因时才会这样做<) / p>

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

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

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

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

答案 1 :(得分:4)

对象没有区别。用于引用的变量的(编译时)类型与存储区域有所不同。

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

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

关于未定义的行为

以非const方式处理const数据可能导致Undefined Behavior,因为允许编译器根据知识进行某些优化,即const变量不会改变 1 。尽可能地改变它(例如通过const_cast<>)会导致错误的结果,因为编译器的假设被主动否定了。

1 请注意,在可以同时修改const变量的情况下, volatile 可以提供帮助。您可以看到const是如何“本地”不会/不能触及 promis,而volatile说:'不要假设这赢了' t更改,即使它没有写入此代码段'。 `

答案 2 :(得分:1)

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

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:-1)

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

让我提醒您,某些字段可能被声明为 mutable ,因此该对象不是整体的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。