35

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.

iammilind
  • 68,093
  • 33
  • 169
  • 336
Daniel
  • 924
  • 1
  • 11
  • 16

5 Answers5

38

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.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • +1, why there is no upvote ? (at least this answer discusses about `mutable` which many of the others are missing). – iammilind Jul 28 '11 at 03:06
  • @iammilind: thanks for the support... I added my answer a couple hours after the others - I think interest had already "washed across" this question, but I had some angles on it I wanted to express. Cheers, Tony – Tony Delroy Jul 28 '11 at 04:48
  • sometimes, it also depends on the questioner. I always see the reputation of the questioner while answering; it's a tendency that the beginners (less than 200 rep) usually don't have patience/maturity to read through the lengthy answers; they always look for `short and sweet`. Your answer covers many aspects of the question, but unfortunately it seems OP doesn't really want that) :) – iammilind Jul 28 '11 at 04:57
  • @iammilind: It's correct what you speculated about my reasoning: I indeed chose the most concise, correct answer as the 'accepted' one. And Kerrek wrote it very quickly after my post. It answered my question fully to the extent which I needed to go on programming. But I do appreciate the extra detail and background which you gave! I had forgotten about the `mutable` keyword (never used it myself; long time since I learned that), and it's good to explicitly mention the word 'side-effect'. – Daniel Jul 28 '11 at 15:45
20

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.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
7

There are two aspects to this question:

  1. what does const mean to the compiler?
  2. 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.

Community
  • 1
  • 1
André Caron
  • 44,541
  • 12
  • 67
  • 125
  • 1
    I was momentarily confused by the difference in Kerrek's and André's answers. But both are right: the type of `this` is `const T *` (pointer to const) inside `const` member functions, where `T` denotes the class. And member variables of any type `U` become `U const`. Accordingly, if `U` is a pointer type `V *`, it becomes `V * const` (constant pointer). André used the same letter `T` as Kerrek, but they mean different things. – Daniel Jul 28 '11 at 02:43
0

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).

Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Hi Martin. I think it would help to differentiate what the compiler enforces and what's best practice. For example, "the state of the object will not be changed by the method". That's a best-practice for the use of `const`, but not a compiler requirement: you can make observable state `mutable`, you can cast away constness.... – Tony Delroy Jul 28 '11 at 01:54
  • @Tony: Anything that is mutable is not part of the state of an object. Mutable members are supposed to hold cached calculations that can be computed from the state. An easy way to think about it is if you persist the member (when storing) it is part of the state mutable members are rarely persisted. Casting away constness is is the first sign that something is wrong (there are a few good exceptions (as usual)). – Martin York Jul 28 '11 at 16:17
  • "Anything that is not mutable..." effectively restates your answer, still without saying that's a best practice and not compiler-enforced. But, hopefully the combination of our comments will satisfy readers. Re "cached calculations" - other uses too, like statistics/instrumentation and locking. The former you may or may not want persisted, but I agree persistence is a good indicator. Anyway, cheers. – Tony Delroy Jul 29 '11 at 01:53
-1

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.

littleadv
  • 20,100
  • 2
  • 36
  • 50
  • I didn't (and won't) downvote, but IMHO this is a good point about `const`'s general utility, but completely ignores the thrust of the question - which is about modifying pointed-to / referenced data from a const member.... – Tony Delroy Jul 28 '11 at 02:15
  • I'm not trying to be difficult here - just to explain why I think someone may have downvoted. "prevents changing the class instance members' values inside the function" doesn't specifically address pointed-to/referenced data, which from a programmer's logical perspective may be considered to be owned by the instance (and hence to be a members' value in a loose sense). It certainly doesn't address why C++ might have decided to allow non-`const` access through pointers/references from within `const` functions... again something the question seems to me to be asking.... – Tony Delroy Jul 28 '11 at 04:54
  • Your example is misleading, `B` has no access to `i` as it is `A`'s private member. – Killzone Kid Sep 29 '18 at 14:25