C++ methods allow a const qualifier to indicate that the object is not changed by the method. But what does that mean? Eg. if the instance variables are pointers, does it mean that the pointers are not changed, or also that the memory to which they point is not changed?
Concretely, here is a minimal example class
class myclass {
int * data;
myclass() {
data = new int[10];
}
~myclass() {
delete [] data;
}
void set(const int index) const {
data[index] = 1;
}
};
Does the method set correctly qualify as const? It does not change the member variable data, but it sure does change the content of the array.
What can a 'const' method change?
Without explicitly casting away constness, a const member function can change:
mutable data members, and
any data the class has non-const access to, irrespective of whether that data's accessible:
via member variables that are pointers or references,
via pointers or references passed as function arguments,
via pointers or references returned by functions,
directly in the namespace or class (for static members) containing it.
These restrictions apply to operations of data members and bases (in the OO sense) too. More explicitly, a const member function operating on *this's data members or bases, when they're of class/struct/union type, can only call their const member functions (if any), and can only write to their mutable data members (if any).
(A const member function can also change any non-const local variables and by-value parameters, but I know that's not what you're interested in).
const data members can call other const member functions, which will have these same abilities and restrictions.
Eg. if the instance variables are pointers, does it mean that the pointers are not changed, or also that the memory to which they point is not changed?
It means the pointers can't be (easily/accidentally) changed. It does not mean that the pointed-to memory can't be changed.
What you've stumbled on is the logical incorrectness of a const function changing pointed-to or referenced data conceptually owned by the object. As you've found, the compiler doesn't enforce the const correctness you may want or expect here. That's a bit dangerous, but means constness doesn't need to be explicitly removed for pointers/references to other objects which may be changed as a side-effect of the const function. For example, a logging object. (Typically, such objects are not logically "owned" by the object whose const function is operating on them.) The key point is that the compiler can't reliably distinguish the type of logical ownership an object has over pointed-to data, so it's got to guess one way or the other and allow the programmer to either override, or not be protected by const-ness. C++ forgoes the protection.
Interesting, I've heard Walter Bright's D language flips this default, making pointed-to data const by default in const functions. That seems safer to me, though it's hard to imagine how often one would end up needing to explicitly cast away constness to allow wanted side-effects, and whether that would feel satisfyingly precise or annoyingly verbose.
Most succinctly, it means that the type of this is const T * inside const member functions, where T is your class, while in unqualified functions it is T *.
Your method set does not change data, so it can be qualified as const. In other words, myclass::data is accessed as this->data and is of type int * const.
There are two aspects to this question:
what does const mean to the compiler?
how does const apply when it cannot be validated by the compiler?
Question 1
The first is rather simple. The compiler validates that no data members are modified (unless they are qualified as mutable). It validates this recursively: for any user-defined types, it checks that no non-const methods are invoked. For built-in types, it validates that they are not assigned.
The transformation for pointers is T* to T*const (const pointer), not const T* (pointer to const). This means that the compiler does not validate that the object pointed to is not modified. Obviously, this leads to question 2.
Question 2
How does const apply when not validate by the compiler? It means whatever it should mean to your application. This is usually referred to as logical const. When to use const with respect to logical const-ness is subject to debate.
const when applied to a method means:
This means that the state of the object will not be changed by the method.
This means any members that are part of the objects state can not be modified, nor can any functions that are not also const be called.
As this relates to pointers. It means the pointer (if it is part of the state) can not be changed. But the object the pointer points at is part of another object so that means you can call non cost methods on this object (as it is not part of the state of this object).
const basically prevents changing the class instance members' values inside the function. This is useful for more clearer interface, but pose restrictions when using inheritance for example. It's sometimes a little bit deceiving (or a lot actually), as in the example you posted.
const would be most appropriate for Get functions, where it is obvious that the caller is reading a value and has no intentions of changing the object state. In this case you would want to limit the inherited implementations as well to adhere to constness, to avoid confusion and hidden bugs when using polymorphism.
For example
class A{
int i;
public:
virtual int GetI() {return i;};
}
class B : public A{
public:
int GetI() { i = i*2; return i;}; // undesirable
}
changing A to:
virtual int GetI() const {return i;};
solves the problem.
Related
Accordingly to C++ best practices on IsoCpp we shouldn't have a const or reference data member: C.12: Don’t make data members const or references
But it does not specify if a pointer to a const object is allowed or not. Consider the example:
class Connection {
gsl::not_null<const Network*> m_network;
...
public:
[[nodiscard]] const Network* getNetwork() const;
...
}
// Implementation
const Network* Connection::getNetwork() const {
return m_network;
}
Is it still conformant with the best practice described? The pointer isn't const but the data the pointer points to is.
Should we remove const from member declaration but still mark the getter return type as a pointer to const?
It depends.
The reason this recommendation exists, is to make your type copyable and assignable, with both having matching semantics.
So consider this:
struct Connection {
Network const& network;
/**/
};
Copying this is perfectly fine, right? But then assigning breaks because network will not be reseatable.
So in this case, replacing that member with an e.g. std::shared_ptr<Network const> is fine. Presumably several connections can use the same network.
But -- if you do not want sharing but each object to have its own copy of the member, then you are left with reconstructing that member on assignment, because the old object cannot be changed. Depending on the object that might not be what you want to do.
For example:
struct Connection {
std::shared_ptr<Network const> network;
std::unique_ptr<std::vector<Entry> const> properties;
};
Aside from a pointer-to-vector being bad form (double indirection for no good reason). Reconstructing a vector anew requires a new memory allocation, while just reusing the old will not (assuming there was enough memory allocated).
Granted, this is a bit of constructed example, and there would maybe be a way to go around that. But the point is, that the similarty of your copy and assignment will heavily depend on the quality of the copy and assignment of the member object.
In such a case the same reasoning that applied to the situation without the pointer similarly applies to the situation with the pointer. You will have to decide whether that is acceptable for your type.
However, I hold that not every type must be assignable. You can make the conscious decision to have a type that just cannot be assigned to. Then and only then, just have a const member without any indirection.
Yes, you can
If you have a const data member, the copy and move constructor are deleted. But this only applies if the pointer itself is const, not the pointed value.
const & or const * are only fine when the class is uncopyable (like a "manager class" of some sort)
I would like to ask a question about methods' const-correctness. Let me illustrate the situation.
class MyClass
{
public:
...
void DiscussedMethod() { otherClass->NonConstMethod(); }
private:
OtherClass *otherClass;
};
I have a class MyClass which keeps a pointer to OtherClass. In DiscussedMethod it calls OtherClass::NonConstMethod which modifies some visible data.
I would like to know, whether it would be a good practice to make the DiscussedMethod const (since it doesn't modify any member data)? Would it be a bad practice? Or is both fine?
What if the OtherClass kept a pointer to the MyClass and in NonConstMethod modified some of the MyClass' data (meaning that the MyClass member data would change during the DiscussedMethod call). Would it be a bad practice to make the DiscussedMethod const then?
As far as I've been able to find out, the const on a method is mostly a code documenting thing, so I would probably lean toward to not making the DiscussedMethod const, but I would like to hear your opinions.
EDIT: Some replies take the into account whether the object pointed to by otherClass is owned by the MyClass object. This is not the case in the scenario I'm working with. Lets say that both objects exist independently side by side (with the ability to modify each other). I think this analogy describes my situation quite well.
For example consider something like doubly-linked list, where each element is a class that keeps pointer to its neighbours and member variable color. And it has method MakeNeighboursRed which changes the color of its neighbours but doesn't affect the calling object's state itself. Should I consider making this method const?
And what if there was some possibility that MakeNeighboursRed would call neighbour's MakeNeighboursRed. So in the end the state of the object for which MakeNeighboursRed has been called originally would change as well.
And I would like to thank you all for your opinions :-)
If MyClass owns the OtherClass instance i wouldn't make DiscussedMethod constant.
The same goes for classes, managing resources. I.e. the standard containers do not return non const references or pointers to the managed memory using const functions, although it would be "possible" (since the actual pointer holding the resource is not modified).
Consider
class MyClass
{
public:
bool a() const { return otherClass->SomeMethod(); }
void b() const { otherClass->NonConstMethod(); }
private:
OtherClass *otherClass;
};
void foo (MyClass const &x)
{
cout << boolalpha << x.a() << endl;
x.b(); // possible if b is a const function
cout << boolalpha << x.a() << endl;
}
The foo could print two different values although an implementor of foo would probably expect that two function calls on a const object will have the same behaviour.
For clarification:
The following is invalid according to the standard since the const version of operator[] returns std::vector<T>::const_reference which is a constant reference to the value type.
std::vector<int> const a = { /* ... */ };
a[0] = 23; // impossible, the content is part of the state of a
It would be possible if there was only one signature of this function, namely referece operator[] (size_t i) const;, since the operation does not alter the internal pointers of the vector but the memory they point to.
But the memory, managed by the vector is considered to be part of the vectors state and thus modification is impossible through the const vector interface.
If the vector contains pointers, those pointer will still be unmodifiable through the public const vector interface, although the pointers stored in the vector may well be non const and it may well be possible to alter the memory they point to.
std::vector<int*> const b = { /* ... */ };
int x(2);
b[0] = &x; // impossible, b is const
*b[0] = x; // possible since value_type is int* not int const *
In OOP object should be fully described by its state, available through its interface. Thus, const methods should not alter object's state, if these changes might be observed through the interface.
A good example is a mutable mutex inside your class to guard some shared resources. It might be modified from const method, since it does not introduce any changes observable via class interface.
General rule of thumb is, that if you can make a member function const, you probably should. The reason for that is that it allows you to catch unintended behaviour and bug easier.
Another argument in favor would be that if you have this function as const you are allowed to call it on const object, so it isn't really a documentation thing.
Overall it depends what the other class is. It's not black and white...
If otherClass is a log object (for example) and you want to log the operation of the current object then it's perfectly fine calling it from a const function.
If the otherClass is a container that for design (or implementation) purposes is implemented as a separate object than effectively a const function modifies the object making this a very bad idea.
I hope this helps.
It's totaly incorrect to make DiscussedMethod const as it changes it's *this state. The only loophole to this is making non-logically-part-of-object's-state member data mutable so they can be changed in const functions. This would be things like a member that hold a count for "number of times function x() has been called". Any thing else is part of the object's state, and if a function changes it (at any level), that function isn't const.
I would like to know, whether it would be a good practice to make the DiscussedMethod const (since it doesn't modify any member data)?
otherClass is member data, and it (or rather, the object it points to) gets modified.
Consider the semantics should the pointer to otherClass be refactored to a fully-owned object... whether something is held as a pointer, reference, or object doesn't change the semantical ownership, IMO.
I have a member function which is declared const and modifies data via a pointer. This seems misleading. Should I remove the const keyword?
I would like to know how others handle this situation in their code. Do people just add a comment to clarify what is going on? Do they not add the const keyword to the member function? Maybe something else completely?
Any advice is appreciated.
You have essentially two choices:
Deep constness:
class Foo
{
T * ptr;
public:
T & operator*() { return *ptr; }
T const & operator*() const { return *ptr; }
T * operator->() { return ptr; }
T const * operator->() const { return ptr; }
};
Shallow constness:
class Foo
{
T * ptr;
public:
T & operator*() const { return *ptr; }
T * operator->() const { return ptr; }
};
It's really up to you, and to the purpose of your class. If the class is a smart pointer, it would seem reasonable to have shallow constness semantics, since the class is supposed to be as similar to a raw pointer as possible (and you can of course have a constant raw pointer to a non-constant pointee).
Otherwise, you should ask yourself why you would be exposing access to a member pointer object at all. It's certainly possible that you want to give mutable access via constant references to your class, but I imagine those are special and rare circumstances. There shouldn't really be that many raw pointers in your code in the first place. Returning a deeply-const reference by dereferencing a pointer should be fine, but usually in better encapsulated "getter" functions which hide the fact that there is a pointer inside your class, like T const & get() const { return *ptr; }.
Generally, yes. Its deceptive to modify something you are declaring constant, even though you can do it.
If someone uses your code and sees const, they expect const. Modification, even though sensible to you, might cause them severe problems -- even crashing a program.
Consider a std::vector<Blah> member versus a Blah* member used to implement a dynamic array. Most often it makes sense to replace the latter with the former. With the Blah* memeber a const method is allowed to modify the data in the array, while with the std::vector<Blah> member the const method is not allowed to modify data there.
Also consider a matrix class with an indexing method that returns a proxy that allows assignment to an element. Assigning via the proxy changes the matrix, not the proxy object itself. Thus, the proxy object’s assignment operator can be (and should be) const, in order to impose the most constraints possible on its effect, while its primary job is to modify things.
That’s another example that the design level is different from the coding level.
In the first example, with a member array, const was all about expressing a design level constraint, but in the second example, with the assignment proxy, const was all about expressing a coding level constraint.
These usages are not incompatible, however. The key idea is to provide a reader of the code with as many constraints as possible (because that greatly reduces how many varying things that must be considered to understand or deal with the code). Upshot: add const wherever you practically can.
class A
{
...
public:
shared_ptr<Logger> GimmeLogger () const
{
return m_logger;
}
private:
shared_ptr<Logger> m_logger;
};
In class A, should GimmeLogger be const or non-const?
It would make sense to be const because it is a simple getter that doesn't modify *this (syntactic const).
But on the other hand, it returns a non-const pointer to another object that it owns (semantically non-const).
If you make that non-const, then you cannot write this:
void f(const A & a)
{
auto v = a.GimmeLogger(); //error
}
So if you want to write this; that is, if you want to call GimmeLogger on const object, then make GimmeLogger a const member function, because you cannot invoke a non-const member function, on const object. However, you can invoke a const member function, on non-const object (as well as on const object).
Inside a const member function, every member is semantically const objects. So the type of m_logger in the function becomes const share_ptr<const m_logger>. So change the return type accordingly.
Because const is a keyword, it is checked syntactically, but it should be used semantically, that is, in your design operations that don't change the visible state of your class should be marked as const.
That is the whole idea behind the mutable keyword: adding the ability to mark a member as this does not take part of the visible state of the object so that the syntactic check matches the semantic meaning. In your particular case, because you are copying a pointer, you don't even need to use mutable there (this is one of the weak points of const-correctness actually, as returning a non-const pointer does not trigger errors while compiling, even though you are opening a door for changes in your object)
In this particular case, on the other hand, I don't see a good reason by which the object would publicize it's logger... That is, const-correctness aside, why do you need to grant access to the logger?
Yes, it should be const. The const-ness of the function has nothing to do with the const-ness of the return type.
I get your point, but I think the function remains const either way.
Generally you shouldn't return a handle to a member data when you can avoid it. Try hard to review your design and find a way around this. That said, if you must, it should be const. This allows you to call the function on const objects as well as non-const objects. See for example std::string::c_str(). You can also overload the function so you get both, like standard containers do with iterators.
When in doubt, look in the standard library for a hint.
Given the following code:
class foo;
foo* instance = NULL;
class foo
{
public:
explicit foo(int j)
: i(j)
{
instance = this;
}
void inc()
{
++i;
}
private:
int i;
};
Is the following using defined behavior?
const foo f(0);
int main()
{
instance->inc();
}
I'm asking because I'm using a class registry, and as I don't directly modify f it would be nice to make it const, but then later on f is modified indirectly by the registry.
EDIT: By defined behavior I mean: Is the object placed into some special memory location which can only be written to once? Read-only memory is out of the question, at least until constexpr of C++1x. Constant primitive types for instance, are (often) placed into read-only memory, and doing a const_cast on it may result in undefined behavior, for instance:
int main()
{
const int i = 42;
const_cast<int&>(i) = 0; // UB
}
Yes, it is undefined behavior, as per 7.1.5.1/4:
Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.
Note that object's lifetime begins when the constructor call has completed (3.8/1).
This may be one of the rare cases where the not very known mutable keyword could be used:
mutable int i;
i can now be changed even if the object is const. It's used when logically the object doesn't change, but in reality it does.
For example:
class SomeClass
{
// ....
void DoSomething() { mMutex.lock(); ...; }
mutable Mutex mMutex;
}
In DoSomething() the object doesn't logically change and yet mMutex has to change in order to lock it. So it makes sense to make it mutable, otherwise no instance of SomeClass could be const (assuming you lock the muetx for every operation).
If you define a const instance of the object, then cast away the const-ness, and modify the contents of the object, you get undefined behavior.
From the sound of things, what you want is exactly the opposite: create a non-const instance of the object, then return a const pointer to that object to (most of) the clients, while the "owner" retains a non-const pointer to the object so it can modify members as it sees fit.
You'd typically manage a situation like this by defining the class with a private ctor, so most clients can't create objects of the type. The class will then declare the owner class as a friend, so it can use the private ctor and/or a static member function to create instances (or often only one instance) of the object. The owner class then passes out pointers (or references) to const objects for clients to use. You need neither a mutable member nor to cast away constness, because the owner, which has the "right" to modify the object, always has a non-const pointer (or, again, reference) to the object. Its clients receive only const pointers/references, preventing modification.
Calling a non-const (by declaration) member function on a const object is not illegal per se. You can use whatever method you wish to work around the compiler restrictions: either an explicit const_cast or a trick with constructor as in your example.
However, the behavior is only defined as long as the member function you are calling does not make an attempt to actually physically modify the object (i.e. modify a non-mutable member of the constant object). Once it makes an attempt to perform a modification, the behavior becomes undefined. In your case, method inc modifies the object, meaning that in your example the behavior is undefined.
Just calling the method, again, is perfectly legal.
It's hard to tell the intent with these arbitrary names. If i is intended as just a use counter, and it isn't really considered part of the data, then it is perfectly appropriate to declare it as mutable int i; Then the const-ness of an instance is not violated when i is modified. On the other hand, if i is meaningful data in the space being modeled, then that would be a very bad thing to do.
Separately from that, though, your example is a bit of a mess for what you seem to be asking. foo* instance = NULL; is effectively (if confusingly) using a NULL as a numeric zero and initializing instance, which is not const; then you separately initialize f, which is const, but never reference it.
Under GCC, at least, your constructor should be explicit foo(int j) with the word int.
However, it's perfectly fine to have two pointers to the same value, one const and the other not.
Why dont you make use of const cast ?
Any reason to make object as const eventhough its state is not constant?
Also make following change :
explicit foo(int j = 0) : i(j)
{ instance = this; }