I’m studying “The C++ Programming Language” from Bjarne Stroustrup and he talks about logical and physical constness of a class.
The example of logical constness is something like:
class A {
int m;
void func() const { m++; } //forbidden
}
It’s possible to bypass this with a cast, like:
class A {
int m;
void func() const { (A*) this)->m++; } //allowed
}
In his words, logical constness is
“an object that appears constant to its users.”
and physical constness is
“stored in read-only memory”
As a note he says that
physical constness may be enforced by placement of an object in read-only memory only for classes without constructors
I didn’t quite understand this statement. Could someone provide an explanation on how to enforce physical constness and why it does not work if the class has a constructor?
You received several answers already, but I believe most of them (if not all) miss the point.
To better understand the situation with constness in C++ (and also involve the concepts mentioned in the other answers) let’s consider not two, but three possible levels of constness that can be encountered in a C++ program
Hardware/OS level physical constness is the physical constness that other answers seem to be describing. It takes place when the object is placed in memory protected from being written: read-only (RO) memory. The protection can be implemented by hardware-provided means or by OS-provided means or by both. However, the C++ language itself does not separate this kind of constness into a distinctive category. The C++ language does not concern itself with such low-level matters. When the notion of physical constness arises in C++ context, it is usually referring to the next kind of constness.
Language level physical constness. This constness takes place simply when you declare an object as
const. The following objects are physical constants from the point of view of C++ languageNote, that it doesn’t really matter whether these object are really placed in RO memory or not. The language says that any attempts to modify these objects will result in Undefined Behavior (UB), regardless of whether the memory is RO or not. Note also, that if you attempt to modify these objects, the manifestations of that UB are not limited to a mere program crash (if they are really in RO memory). For example, the compiler is free to assume that these objects never change and can optimize the code under that assumption, eliminating access to these objects in situations when it appears to be unnecessary. Because of this, even if the memory occupied by these objects is writeable and even if you manage to “successfully” modify them somehow, your code might still behave as if your modifications never took place. UB is UB. Anything can happen.
From the language point of view this kind of constness is, of course, intended to include the 1st kind.
Finally, to Logical constness. Logical constness in C++ is the constness of so called acces path to the object. Access path is the reference or the pointer that allow you to access to existing object indirectly. Consider this declaration
This is a pointer to
const MyClasstype. Note, however: it doesn’t really mean that the actual object this pointer is pointing to is a constant. It just let’s you “see” it as a constant. The object can easily be either a constantor it might be a non-constant
In other words, having just that pointer
pyou have a constant accss path to some object of typeMyClass. You don’t know and (normally) don’t need to know whether that object is really a constant. Since the access path that was given to you is constant, you have to treat that object as a constant. Of course, if you somehow know that the object on the other end of that access path is not a constant, you can legally cast away the constness of the access pathand perform modifying operations on the object (of course, in general case it is not a good programming practice, but it has its valid uses). If the object on the other end of the path turns out to be a constant after all, the behavior will be undefined for the reasons described above.
Note, that the example in the original post is talking about exactly that. When you declare a method of class
Aasconst, it simply means that the implicitthisparameter passed to that method will have typeconst A*, i.e. it will provide a constant access path to theAobject. This is what I described above as logical constness. Note, again, that if the object was declared as, say,const A a;, it is a language-level physical constant and modifying it as shown in the example is illegal, regardless of whether the object is residing in RO memory or not.Now, to provide one final illustration of the above, consider the follwing declaration
This declaration has 4
constqualifiers in it. One of these qualifiers has a major qualitative diference from the others. It is the rightmost one. The rightmostconstdeclares the physical constness of objectp(constness of the pointer itself), while the remainingconstqualifiers declare logical constness of the objects they will be pointing to (constness of the access path).Again, I believe that in his book Stroustrup meant to talk about the distinction between the 2nd and 3rd concepts of constness, not about the 1st, since the C++ language doesn’t really separate 1st from the 2nd. Note, that the example says that the modification by casting away constness is “allowed”, while the language specification clearly says that modifying the constants of the 2nd kind by this approach is immediately illegal.