简体   繁体   English

具有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;
}

Although in obj ptr is of type int *const , the constructor can make him point to i of type const int . 尽管在obj ptr中类型为int *const ,构造函数可以使他指向const int类型的i An explicit try to do this of course fails. 明确地尝试执行此操作当然会失败。 Why does a constructor offer this vulnerability regarding const correctness? 为什么构造函数会提供有关const正确性的漏洞? Other non directly obvious vulnerabilities like 其他非直接显而易见的漏洞,例如

int *ptr;
const int **c_ptr = &ptr; // error
const int c = 10;
*c_ptr = &c;
*ptr = 20; // because here change of c possible

are also well thought prevented. 也被认为是可以预防的。

const is a language-level concept. const是语言级别的概念。 That is, when you compile your code and execute it as machine code, all data is seen as data more or less. 也就是说,当您编译代码并将其作为机器代码执行时,所有数据或多或少都被视为数据。 Note that I say "more or less" because we are ignoring the fact that, in theory, const data could be stored in read-only pages and trigger page faults when written to; 请注意,我之所以说“或多或少”,是因为我们忽略了以下事实:从理论上讲, const数据可以存储在只读页面中,并且在写入时会触发页面错误。 but this is not common due to the granularity of page sizes. 但是由于页面大小的粒度,这种情况并不常见。 So what is happening is the following: 因此,发生了以下情况:

Your constructor initializes that value of ptr to point to the address of i . 您的构造函数将ptr值初始化为指向i的地址。 Since your obj object is const , you could not directly modify the value of i and, further, you could not change where ptr points to. 由于您的obj对象是const ,因此您无法直接修改i的值,而且无法更改ptr指向的位置。 However, you can access and manipulate the memory that ptr points to (in this case, the value of i ). 但是,您可以访问和操纵ptr指向的内存(在这种情况下,为i的值)。

Therefore, since the compiler doesn't check/know/care that ptr is pointing to i , it does not catch the violation of const . 因此,由于编译器不会检查/知道/关心ptr指向i ,因此它不会捕获违反const的行为。 Instead, it just sees you modifying the data pointed to by ptr . 相反,它只是看到您修改了ptr指向的数据。

Clearly the constructor (or at least the initializer list if not the body of the ctor) needs to be able to write a value to i . 显然,构造函数(或者至少是初始化列表,如果不是ctor的主体的话)需要能够向i写入值。

As it happens, the way C++ achieves this is to make this a pointer-to-non-const in the constructor (and destructor). 因为它发生,C的方式++实现这一点是让this一个指针到非const在构造(和析构函数)。 Basically, the const -ness of obj doesn't commence until the constructor has finished executing. 基本上, objconst -ness直到构造函数完成执行才开始。 That's why the vulnerability exists, because of a straightforward but imperfect solution to the technical problem of how to construct const -qualified objects. 这就是存在该漏洞的原因,因为它是如何构造const限定对象的技术问题的直接但不完善的解决方案。

Perhaps it could in principle be done differently. 原则上也许可以以不同的方式进行。 I suppose you'd need a separate const version of the constructor in which the compiler applies different rules (just as normal member functions can be const ), treating data members as const and therefore (1) allowing them to be initialized but not assigned, (2) forbidding the initialization of ptr from &i since the latter would have type int const* . 我想您需要一个单独的const版本的构造函数,在该版本中,编译器将应用不同的规则(就像普通的成员函数可以是const ),将数据成员视为const ,因此(1)允许对其进行初始化但不进行赋值, (2)禁止&i初始化ptr ,因为后者的类型为int const* C++ doesn't do this, and as a result it has this loophole that you've driven through. C ++不会执行此操作,因此它会遇到您所遇到的漏洞。 If it did do it, people would have more difficulty writing constructors in certain cases, so it's a design trade-off. 如果这样做的话,在某些情况下,人们在编写构造函数时会遇到更多困难,因此这是设计的折衷方案。

Be aware that similarly a volatile -qualified object is not volatile in its own constructor or destructor. 请注意,类似地, volatile限定对象在其自己的构造函数或析构函数中也不是volatile

Steve Jessop answered it, but for what it's worth, here is the quote from the Standard (emphasis mine): 史蒂夫·杰索普(Steve Jessop)回答了这个问题,但是对于它的价值,这是Standard(强调我的观点)的引文:

12.1/4 A constructor shall not be virtual (10.3) or static (9.4). 12.1 / 4构造函数不得为虚拟(10.3)或静态(9.4)。 A constructor can be invoked for a const, volatile or const volatile object. 可以为const,volatile或const volatile对象调用构造函数。 A constructor shall not be declared const, volatile, or const volatile (9.3.2). 构造函数不得声明为const,volatile或const volatile(9.3.2)。 const and volatile semantics (7.1.6.1) are not applied on an object under construction. const和volatile语义(7.1.6.1)不适用于正在构造的对象。 They come into effect when the constructor for the most derived object (1.8) ends. 它们在最派生对象(1.8)的构造函数结束时生效。 A constructor shall not be declared with a ref-qualifier. 构造函数不得使用ref限定符声明。

So *this is not a constant object from the point of view of the constructor, even when a constant object is created. 因此*this从构造函数的角度来看, *this不是常量对象,即使创建了常量对象也是如此。 This could have been designed differently, but then constructors of constant objects would be much less flexible than constructors of non-constant objects; 本来可以以不同的方式设计,但是常量对象的构造函数将比非常量对象的构造函数灵活得多。 for example, they would always have to initialize all members in the initializer list; 例如,他们将总是必须初始化初始化器列表中的所有成员; they could not use loops etc. in the body of the constructor to set values for complex members. 他们不能在构造函数的主体中使用循环等为复杂成员设置值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM