如果更改const对象是未定义的行为,那么构造函数和析构函数如何使用写访问操作?

sha*_*oth 9 c++ constructor destructor const undefined-behavior

C++标准说修改最初声明的对象const是未定义的行为.但那么构造函数和析构函数如何运作?

class Class {
public:
    Class() { Change(); }
    ~Class() { Change(); }
    void Change() { data = 0; }
private:
    int data;
};

//later:
const Class object;
//object.Change(); - won't compile
const_cast<Class&>( object ).Change();// compiles, but it's undefined behavior
Run Code Online (Sandbox Code Playgroud)

我的意思是构造函数和析构函数与调用代码完全相同,但是允许它们更改对象并且不允许调用者 - 他会遇到未定义的行为.

它应该如何在实施和标准下工作?

Mic*_*urr 15

该标准明确允许构造函数和析构函数处理const对象.从12.1/4"构造者":

构造函数可以被调用用于const,volatileconst volatile对象.... constvolatile语义(7.1.5.1)不适用于正在构建的对象.只有当派生的对象(1.8)的构造函数结束时,这种语义才会生效.

和12.4/2"析构函数":

析构函数可以调用一个const,volatileconst volatile对象.... constvolatile语义(7.1.5.1)不适用于被破坏的对象.一旦最派生对象(1.8)的析构函数启动,这种语义就会停止生效.

作为背景,Stroustrup在"C++的设计和演变"(13.3.2对定义的细化const)中说:

为了确保某些(但不是全部)const对象可以放置在只读存储器(ROM)中,我采用了一个规则,即任何具有构造函数的对象(即,所需的运行时初始化)都不能放在ROM中,而是其他const对象可以.

...

声明的对象const在构造函数完成之前被认为是不可变的,直到它的析构函数开始.在这些点之间写入对象的结果被认为是未定义的.

在最初的设计中const,我记得认为理想const是一个在构造函数运行之前可写的对象,然后由一些硬件魔法变为只读,最后进入析构函数后再次变为可写.可以想象一个实际上以这种方式工作的标记架构.如果有人可以写入定义的对象,这样的实现会导致运行时错误const.另一方面,有人可以写入未定义的const已作为const引用或指针传递的对象.在这两种情况下,用户都必须先抛弃const.这种观点的含义是,抛弃const最初定义的对象const然后写入它最多是未定义的,而对最初未定义的对象执行相同操作const是合法且定义良好的.

请注意,通过对规则的这种改进,其含义const并不取决于类型是否具有构造函数; 原则上,他们都这样做.const现在声明的任何对象可以放在ROM中,放在代码段中,受访问控制等保护,以确保它在收到初始值后不会发生变异.但是,这种保护并不是必需的,因为目前的系统通常不能保护每const一种形式的腐败.

  • +1!我发现更重要的是它在同一段落中说"const和volatile语义(7.1.5.1)不适用于正在构建/破坏的对象". (4认同)
  • @STingRaySC:`mutable`怎么样?`const`对象的`mutable`组件可以修改 - 所以那些部分不是`const`. (2认同)