简体   繁体   中英

C++ - Are const parameters and class variables pessimization?

I'm trying to figure out when const should be used when writing C++ code. Are these all examples of pessimization or is it beneficial to write code this way?:

Example 1:

int findVal(const int OTHER_VAL) const
{
    switch(OTHER_VAL)
    {
    case 1:
        return 2;
    default:
        return 3;
    }
}

Example 2:

enum class MobType
{
    COW, CHICKEN, DOG, PIG
};

class BaseMob
{
protected:
    BaseMob(const MobType TYPE) : TYPE(TYPE) { }

    const MobType TYPE;
};

Example 3:

void showWorld(const World &world)
{
    auto data = world.getData();
    for (auto &i:data)
        i.print();
}

No, they aren't.

const on local variables with automatic storage (including function args) is purely syntactic sugar to help human programmers set rules for their code. It doesn't help the optimizer at all. Optimizing compilers extract the necessary data-movement from the C source, and optimize that. They generally don't care if you reuse the same tmp variable for many different things, or have 10 different const tmp1 = a+10; in the same function.

And yes, this applies to function args passed by value; they are local variables with automatic storage, passed in registers or on the stack. And no, this doesn't mean the caller can assume that a function didn't modify the stack memory used for arg-passing, so it doesn't help the optimizer much either. (Making a 2nd function call with the same arguments still requires re-writing the args to the stack (if not all args fit in registers), because the const on an arg doesn't change the fact that the called function "owns" that stack space and can use it as scratch space however it wants.)


const on static/global/reference variables does help. static const int foo = 10; can be inlined as an immediate constant instead of loaded from memory. (eg add eax, 10 instead of add eax, [foo] ).


Using const to mark a class method as not changing any class members can also help the compiler avoid re-loading class members after a function call. (ie keep them live in registers). This mostly only applies if the compiler can't see the function definition, otherwise a good optimizing compiler can just look at what the called function does and optimize accordingly. (As long as it's not in a Unix library, where symbol interposition means that it can't assume the called function it sees at compile time will be the one called after dynamic linking.)

Whenever you logically do not alter a value or an object you should make it const . By logically I do not mean every time you are technically allowed to, but every time it is logical in the context of your functions, classes and code.

A simple example could be a simple "get" function as seen in example 1, these functions should not modify the state of the class, and should therefore be marked constant, as this will help document your intent to the user, besides helping you ensure the invariance of the class.

There are situations where it makes sense to make an immutable object, as seen in example 2. It is not that often we see these in C++, but many other languages use them frequently. If it does not add any value to be able to change a certain member during an objects lifetime, you might as well make it const.

Passing const reference parameters gives you the performance benefits of the reference, but at the same time ensures that the source object is kept unmodified, which is both great documentation to the user, but also allows som optimizations to happen.

Having mentioned all these reasons, there are other reasons to use const as briefly mentioned in the last paragraph, optimizations. When the compiler knows that something is constant and is not being altered it can enable some pretty clever optimizations, don't use const for performance reasons though.

This is also why working around constness by (for instance) the const_cast cast, which can cast away const , can lead to some undesired behaviour. As an example check out the following:

#include <stdio.h>

static const int foo = 10;

int constsum(void) {
  return foo + 5;
}

int main(int argc, char* argv[]) {
  int a = constsum();
  int* newFoo = const_cast<int*>(&foo);
  *newFoo = 20;
  int b = constsum();
  printf("%d\n", a + b);
  return 0;
}

As can be seen from this example ( see code running here ) this might not produce the desired result, as the code result in 30 being printed, and not as perhaps expected 40.

When examining the produced assembly we can see why ( compiled into assembly ):

constsum():
        mov     eax, 15
        ret
main:
        mov     eax, 30
        ret

The compiler simply inlines the values, as it can see that they constant, it does not take special care that the const_cast is being used.

So const correctness, and use of const is a valuable tool, that can benefit performance, and stability of your code, but also (and not to forget) it helps documenting your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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