Reputation: 1086
Is it a good practice, in C++, to add const at the end of a member function definition every time the function does not modify the object, i.e., every time the function is 'eligible' for const? I know that it's necessary in this case:
class MyClass {
public:
int getData() const;
};
void function(const MyClass &m) { int a = m.getData(); dosomething... }
but other than this, and other uses of const for actual functionality, does adding const to the end actually change the way code is executed (faster/slower) or is it just a 'flag' for the compiler to handle cases such as the one above? In other words, if const (at the end) is not needed for functionality in a class, does adding it make any difference?
Upvotes: 14
Views: 9548
Reputation: 1009
I disagree with some of the responses here. Simply declaring a function const because you can is not a good idea. As mentioned in the top response Herb Stutter writes in this article "const is mainly for humans, rather than for compilers and optimizers." 20 years later and this is still true. A const declaration tells the caller that this function can be safely be called without a state modification. Now, the compiler only cares about state modifications to the object to which the function belongs.
C++ has no good notion of purity. That is there is no clean way for the compiler to really know if a function has side-effects. A simple example would be a member function that increments a column in a database. The compiler would tell you it's safe to declare this function as const since it does not modify the state of the object itself. But doing so could result in misuse of this function. I don't think you should just rely on the compiler to tell you if you should declare a function as const. Instead you should consider what the function does and how you expect it to be used.
Upvotes: 0
Reputation: 33202
Please see this excellent article about const correctness by Herb Sutter (C++ standards committee secretary for 10 years.)
Regarding optimizations, he later wrote this article where he states that "const is mainly for humans, rather than for compilers and optimizers." Optimizations are impossible because "too many things could go wrong...[your function] might perform const_casts."
However, const correctness is a good idea for two reasons: It is a cheap (in terms of your time) assertion that can find bugs; and, it signals the intention that a function should theoretically not modify the object's external state, which makes code easier to understand.
Upvotes: 22
Reputation: 361352
every time the function does not modify the object, i.e., every time the function is 'eligible' for const?
In my opinion, Yes. It ensures that you call such functions on const
objects or const
expressions involving the object:
void f(const A & a)
{
a.inspect(); //inspect must be a const member function.
}
Even if it modifies one or few internal variables once or twice, even then I usually make it const
member function. And those variables are declared with mutable
keyword:
class A
{
mutable bool initialized_cache; //mutable is must!
public:
void inspect() const //const member function
{
if ( !initialized_cache)
{
initialized_cache= true;
//todo: initialize cache here
}
//other code
}
};
Upvotes: 5
Reputation: 2285
The 'const' system is one of the really messy features of C++. It is simple in concept, variables declared with ‘const’ added become constants and cannot be altered by the program, but, in the way is has to be used to bodge in a substitute for one of the missing features of C++, it gets horridly complicated and frustratingly restrictive. The following attempts to explain how 'const' is used and why it exists. Of the mixes of pointers and ‘const’, the constant pointer to a variable is useful for storage that can be changed in value but not moved in memory and the pointer (constant or otherwise) is useful for returning constant strings and arrays from functions which, because they are implemented as pointers, the program could otherwise try to alter and crash. Instead of a difficult to track down crash, the attempt to alter unalterable values will be detected during compilation.
For example, if a function which returns a fixed ‘Some text’ string is written like
char *Function1()
{ return “Some text”;}
then the program could crash if it accidentally tried to alter the value doing
Function1()[1]=’a’;
whereas the compiler would have spotted the error if the original function had been written
const char *Function1()
{ return "Some text";}
because the compiler would then know that the value was unalterable. (Of course, the compiler could theoretically have worked that out anyway but C is not that clever.) When a subroutine or function is called with parameters, variables passed as the parameters might be read from to transfer data into the subroutine/function, written to to transfer data back to the calling program or both to do both. Some languages enable one to specify this directly, such as having ‘in:’, ‘out:’ & ‘inout:’ parameter types, whereas in C one has to work at a lower level and specify the method for passing the variables choosing one that also allows the desired data transfer direction.
For example, a subroutine like
void Subroutine1(int Parameter1)
{ printf("%d",Parameter1);}
accepts the parameter passed to it in the default C & C++ way which is a copy. Therefore the subroutine can read the value of the variable passed to it but not alter it because any alterations it makes are only made to the copy and lost when the subroutine ends so
void Subroutine2(int Parameter1)
{ Parameter1=96;}
would leave the variable it was called with unchanged not set to 96.
The addition of an ‘&’ to the parameter name in C++ (which was a very confusing choice of symbol because an ‘&’ infront of variables elsewhere in C generate pointers!) like causes the actual variable itself, rather than a copy, to be used as the parameter in the subroutine and therefore can be written to thereby passing data back out the subroutine. Therefore
void Subroutine3(int &Parameter1)
{ Parameter1=96;}
would set the variable it was called with to 96. This method of passing a variable as itself rather than a copy is called a ‘reference’ in C.
That way of passing variables was a C++ addition to C. To pass an alterable variable in original C, a rather involved method using a pointer to the variable as the parameter then altering what it pointed to was used. For example
void Subroutine4(int *Parameter1)
{ *Parameter1=96;}
works but requires the every use of the variable in the called routine so altered and the calling routine altered to pass a pointer to the variable which is rather cumbersome.
But where does ‘const’ come into this? Well, there is a second common use for passing data by reference or pointer instead of copy. That is when copying a the variable would waste too much memory or take too long. This is particularly likely with large compound user-defined variable types (‘structures’ in C & ‘classes’ in C++). So a subroutine declared
void Subroutine4(big_structure_type &Parameter1);
might being using ‘&’ because it is going to alter the variable passed to it or it might just be to save copying time and there is no way to tell which it is if the function is compiled in someone else’s library. This could be a risk if one needs to trust the the subroutine not to alter the variable.
To solve this, ‘const’ can be used the in the parameter list like
void Subroutine4(big_structure_type const &Parameter1);
which will cause the variable to passed without copying but stop it from then being altered. This is messy because it is essentially making an in-only variable passing method from a both-ways variable passing method which was itself made from an in-only variable passing method just to trick the compiler into doing some optimization.
Ideally, the programmer should not need control this detail of specifying exactly how it variables are passed, just say which direction the information goes and leave the compiler to optimize it automatically, but C was designed for raw low-level programming on far less powerful computers than are standard these days so the programmer has to do it explicitly.
Upvotes: 1
Reputation: 106086
Making member functions const
ensures that calling code that has const
objects can still call the function. It is about this compiler check - which helps create robust self-documenting code and avoid accidental modifications of objects - and not about run-time performance. So yes, you should always add const if the nature of the function is such that it doesn't need to modify the observable value of the object (it can still modify member variables explicitly prefixed with the mutable
keyword, which is intended for some quirky uses like internal caches and counters that don't affect the client-visible behaviour of the object).
Upvotes: 0
Reputation: 4780
Yes, it is a good practice.
At the software engineering level it allows you to have read-only objects, e.g. you can prevent objects from being modified by making them const. And if an object is const, you are only allowed to call const functions on it.
Furthermore, I believe the compiler can make certain optimizations if it he knows that an object will only be read (e.g., share common data between several instances of the object as we know they are never being modified).
Upvotes: 1
Reputation: 2917
My understanding is that it is indeed just a flag. However, that said, you want to add it wherever you can. If you fail to add it, and a function elsewhere in your code does something like
void function(const MyClass& foo)
{
foo.getData();
}
You will run into issues, for the compiler cannot guarantee that getData does not modify foo.
Upvotes: 0
Reputation: 95489
Yes. In general, every function that is logically const should be made const. The only gray areas are where you modify a member through a pointer (where it can be made const but arguably should not be const) or where you modify a member that is used to cache a computation but otherwise has no effect (which arguably should be made const, but will require the use of the keyword mutable to do so).
The reason why it's incredibly important to use the word const is:
It is important documentation to other developers. Developers will assume that anything marked const does not mutate the object (which is why it might not be a good idea to use const when mutating state through a pointer object), and will assume that anything not marked const mutates.
It will cause the compiler to catch unintentional mutations (by causing an error if a function marked const unintintionally calls a non-const function or mutates an element).
Upvotes: 3