Reputation: 703
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
. An explicit try to do this of course fails. Why does a constructor offer this vulnerability regarding const correctness? 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.
Upvotes: 1
Views: 217
Reputation: 279255
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
.
As it happens, the way C++ achieves this is to make this
a pointer-to-non-const in the constructor (and destructor). Basically, the const
-ness of obj
doesn't commence until the constructor has finished executing. That's why the vulnerability exists, because of a straightforward but imperfect solution to the technical problem of how to construct const
-qualified objects.
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*
. C++ doesn't do this, and as a result it has this loophole that you've driven through. 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.
Upvotes: 2
Reputation: 69977
Steve Jessop answered it, but for what it's worth, here is the quote from the Standard (emphasis mine):
12.1/4 A constructor shall not be virtual (10.3) or static (9.4). A constructor can be invoked for a const, volatile or const volatile object. A constructor shall not be declared const, volatile, or const volatile (9.3.2). const and volatile semantics (7.1.6.1) are not applied on an object under construction. They come into effect when the constructor for the most derived object (1.8) ends. A constructor shall not be declared with a ref-qualifier.
So *this
is not a constant object from the point of view of the constructor, even when a constant object is created. 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.
Upvotes: 1
Reputation: 6823
const
is a language-level concept. 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; 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
. 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. However, you can access and manipulate the memory that ptr
points to (in this case, the value of i
).
Therefore, since the compiler doesn't check/know/care that ptr
is pointing to i
, it does not catch the violation of const
. Instead, it just sees you modifying the data pointed to by ptr
.
Upvotes: 4