const对象和成员指针的const正确性,构造函数漏洞
const correctness with const objects and member pointers, constructor vulnerability
class Test
{
public:
Test() : i(0), ptr(&i) {}
int i;
int *ptr;
void change_const (int x) const { *ptr=x; }
};
int main()
{
const Test obj;
obj.ptr = &obj.i; // error
obj.change_const(99);
return 0;
}
虽然在obj
中,ptr
属于int *const
类型,但是构造函数可以使他指向const int
类型的i
。明确的尝试当然是失败的。为什么构造函数会提供有关const正确性的漏洞?其他非直接显而易见的漏洞,如
int *ptr;
const int **c_ptr = &ptr; // error
const int c = 10;
*c_ptr = &c;
*ptr = 20; // because here change of c possible
也被认为是可以预防的。
const
是一个语言级别的概念。也就是说,当您编译代码并将其作为机器代码执行时,所有数据或多或少都被视为数据。请注意,我说"或多或少"是因为我们忽略了这样一个事实,即理论上,const
数据可能存储在只读页面中,并在写入时触发页面故障;但由于页面大小的粒度,这并不常见。因此,正在发生的情况如下:
构造函数初始化ptr
的值以指向i
的地址。由于obj
对象是const
,因此无法直接修改i
的值,也无法更改ptr
指向的位置。但是,您可以访问和操作ptr
指向的内存(在本例中为i
的值)。
因此,由于编译器不检查/知道/关心ptr
是否指向i
,因此它不会捕获const
的冲突。相反,它只是看到您修改ptr
所指向的数据。
显然,构造函数(如果不是ctor的主体,则至少是初始值设定项列表)需要能够向i
写入值。
碰巧,C++实现这一点的方法是使this
成为构造函数(和析构函数)中非常数的指针。基本上,obj
的const
-ness直到构造函数完成执行才开始。这就是漏洞存在的原因,因为对于如何构造const
限定对象的技术问题,有一个简单但不完美的解决方案。
也许原则上可以采取不同的做法。我想你需要一个单独的const
版本的构造函数,编译器在其中应用不同的规则(就像正常的成员函数可以是const
一样),将数据成员视为const
,因此(1)允许它们初始化但不分配,(2)禁止从&i
初始化ptr
,因为后者的类型为int const*
。C++没有做到这一点,因此它有一个你已经钻过的漏洞。如果它真的这样做了,在某些情况下,人们将更难编写构造函数,所以这是一种设计权衡。
请注意,类似地,volatile
限定的对象在其自己的构造函数或析构函数中不是volatile
。
Steve Jessop回答了这个问题,但值得一提的是,这里引用了标准(强调我的):
12.1/4构造函数不应是虚拟的(10.3)或静态的(9.4)。构造函数可以为const、volatile或const-voile对象调用。构造函数不应声明为const、volatile或const volatile(9.3.2)。const和volatile语义(7.1.6.1)不应用于正在构建的对象。当派生最多的对象(1.8)的构造函数结束时,它们就会生效构造函数不应使用ref限定符进行声明。
因此,从构造函数的角度来看,即使创建了常量对象,*this
也不是常量对象。这可能会有不同的设计,但常量对象的构造函数将远不如非常量对象的构造器灵活;例如,它们总是必须初始化初始化器列表中的所有成员;他们不能在构造函数的主体中使用循环等来设置复杂成员的值。
- 如何从构造函数副本 T(const T&)调用对象 T?
- 在 constexpr 构造函数 (c++17) 中赋值到 const char * 在使用 Android NDK 时
- 何时应在构造函数参数中使用 const C++?
- 在 C++ 中声明 const 对象需要用户定义的默认构造函数.如果我有一个可变成员变量,为什么不呢?
- 为什么构造函数 Message(const T&data) 与 Message(T&& data) 冲突,当 T = int&时?
- 如何在构造函数中传递 const 引用时强制编译器不接受右值
- C++将派生类的const值传递给基意外行为的构造函数
- 是否允许使用初始值设定项列表将const数组引用实例化为构造函数参数
- 在C++中,从构造函数中将字符串文本分配给成员const char*变量时会发生什么
- C++:使用委托构造函数时选择"const char *"与"std::string&qu
- 具有字符串文本构造函数的类不适用于 const 引用初始化
- 为什么在使用转换构造函数编译代码时需要 const 复制构造函数?
- C++ 在头文件或构造函数中初始化 const 类成员变量?
- 在没有构造函数的情况下初始化 const c++ 类
- 使用 const T(&x)[N][M] 构造函数类矩阵
- 为什么即使参数标记为"const",也会调用复制构造函数?
- 为什么带有 const 关键字的构造函数可以工作,而没有它就不能工作?
- 在复制构造函数中使用和不使用 const 有什么区别
- 与构造函数中rvalue结合的非const lvalue有关的错误
- const构造函数