常量对象的构造
Construction of const object
C++11 §12.1/14:
在构造 const 对象期间,如果对象或其任何子对象的值是通过 不是直接或间接从构造函数的 this 获得的 指针,这样获得的对象或子对象的值为 未指定。 [示例:
struct C;
void no_opt(C*);
struct C {
int c;
C() : c(0) { no_opt(this); }
};
const C cobj;
void no_opt(C* cptr) {
// value of cobj.c is unspecified
int i = cobj.c * 100;
cptr->c = 1;
// value of cobj.c is unspecified
cout << cobj.c * 100 << 'n';
}
编译上述示例输出100
.我的问题是,为什么在输入构造函数之前初始化列表将其设置为 0
时,应该未指定 cobj.c
的值?如果使用非常量对象,此行为有何不同?
编译器可以将真正const
的对象视为合法常量。它可以假设它们的值永远不会改变,甚至将它们存储在const
内存中,例如.ROM或闪存。因此,只要对象实际上不是恒定的,就需要使用 this
提供的 non-const 访问路径。这种情况仅在对象构建和销毁期间存在。
顺便说一句,我认为不需要对析构函数有相应的要求,因为对象生命周期已经结束,一旦cobj
的析构函数开始,cobj.c
就无法访问。
正如 Matthieu 所提到的,在建造或破坏过程中,除了通过this
访问对象之外,这是一种强烈的"代码气味"。回顾 C++11 §3.8 [basic.life] ¶1 和 6,似乎构造函数内部的cobj.c
是 UB,原因与它在析构函数内部的原因相同,无论对象const
或 §12.1/14,因为它的生存期直到初始化完成(构造函数返回(才开始。
它可能有效,但它会给优秀的C++程序员敲响警钟,而且根据书本,这是非法的。
引用规则的原因是允许编译器使基于对象的恒定性的优化。 例如根据优化情况,编译器可能会替换第二个 cobj.c * 100
与i
no_opt
. 更有可能的是,在这个特定的在这种情况下,优化器将抑制i
及其初始化完全,因此代码看起来可以正常工作。 但这可能不是如果你在更改cptr->c
之前也输出i
;这一切都取决于关于编译器优化的积极性。 但是编译器被允许假设*cptr
不是cobj
的别名,因为cobj
是一个const 对象,其中当你通过 *cptr
进行修改时,所以它不能指向没有未定义行为的 const 对象。
当然,如果对象不是常量,则不会出现问题;编译器必须始终考虑 *cptr
和cobj
.
- 代理对象的常量正确性
- 返回常量对象引用 (getter) 和仅返回字符串有什么区别?
- 如何使用数据对象上的常量指针初始化类
- 为什么C++在将一个对象复制到另一个对象时需要对这两个对象进行低级常量限定
- 为什么当我们有常量引用时创建临时对象?
- 是否可以使用非常量指针调用非常量函数,以及当两个unique_ptrs指向同一个对象时程序的行为方式?
- C++,如何使用常量对象和非常量对象进行比较?
- 通过从构造函数中的'this'复制的指针改变常量对象
- 为什么我可以改变常量对象中的成员变量,这是返回常量对象函数的结果?
- C++将常量字符* 指针数组传递给对象
- C++ 对非常量对象的常量引用和对非常量对象的非常量引用之间的区别
- 未定义的对象(〔basic.life〕/8):为什么允许引用重新绑定(和常量修改)
- 当类具有常量时,将对象插入到向量中
- 算法不适用于非常量对象的const_iterator
- C++:允许临时对象调用非常量成员函数的设计理念是什么?
- 为什么MSVC14允许声明指向动态未初始化常量对象的指针
- 可作为常量调用的比较对象
- C++中的常量对象或私有/常量数据成员(变量)?
- 将值从指针复制到常量对象参数
- 如何正确组合可变对象常量