Search

8.10 — Const class objects and member functions

In lesson 2.9 -- Const, constexpr, and symbolic constants, you learned that fundamental data types (int, double, char, etc…) can be made const via the const keyword, and that all const variables must be initialized at time of creation.

In the case of const fundamental data types, initialization can be done through copy, direct, or uniform initialization:

Const classes

Similarly, instantiated class objects can also be made const by using the const keyword. Initialization is done via class constructors:

Once a const class object has been initialized via constructor, any attempt to modify the member variables of the object is disallowed, as it would violate the const-ness of the object. This includes both changing member variables directly (if they are public), or calling member functions that set the value of member variables. Consider the following class:

Both of the above lines involving variable something are illegal because they violate the constness of something by either attempting to change a member variable directly, or by calling a member function that attempts to change a member variable.

Const member functions

Now, consider the following line of code:

Perhaps surprisingly, this will also cause a compile error, even though getValue() doesn’t do anything to change a member variable! It turns out that const class objects can only explicitly call const member functions, and getValue() has not been marked as a const member function. A const member function is a member function that guarantees it will not modify the object or call any non-const member functions (as they may modify the object).

To make getValue() a const member function, we simply append the const keyword to the function prototype, after the parameter list, but before the function body:

Now getValue() has been made a const member function, which means we can call it on any const objects.

For member functions defined outside of the class declaration, the const keyword must be used on both the function prototype in the class declaration and on the function definition:

Futhermore, any const member function that attempts to change a member variable or call a non-const member function will cause a compiler error to occur. For example:

In this example, resetValue() has been marked as a const member function, but it attempts to change m_value. This will cause a compiler error.

Note that constructors cannot be marked as const. This is because const objects need to be able to initialize their member variables, and a const constructor would not be able to do so. Consequently, the language disallows const constructors.

It’s worth noting that a const object is not required to initialize its member variables (that is, it’s legal for a const class object to call a constructor that initializes all, some, or none of the member variables)!

rule: Make any member function that does not modify the state of the class object const

Const references

Although instantiating const class objects is one way to create const objects, a more common way is by passing an object to a function by const reference.

In the lesson on passing arguments by reference, we covered the merits of passing class arguments by const reference instead of by value. To recap, passing a class argument by value causes a copy of the class to be made (which is slow) -- most of the time, we don’t need a copy, a reference to the original argument works just fine, and is more performant because it avoids the needless copy. We typically make the reference const in order to ensure the function does not inadvertently change the argument, and to allow the function to work with R-values (e.g. literals), which can be passed as const references, but not non-const references.

Can you figure out what’s wrong with the following code?

The answer is that inside of the printDate function, date is treated as a const object. And with that const date, we’re calling functions getYear(), getMonth(), and getDay(), which are all non-const. Since we can’t call non-const member functions on const objects, this will cause a compile error.

The fix is simple: make getYear(), getMonth(), and getDay() const:

Now in function printDate(), const date will be able to successfully call getYear(), getMonth(), and getDay().

Overloading const and non-const function

Finally, although it is not done very often, it is possible to overload a function in such a way to have a const and non-const version of the same function:

The const version of the function will be called on any const objects, and the non-const version will be called on any non-const objects:

Overloading a function with a const and non-const version is typically done when the return value needs to differ in constness. In the example above, the non-const version of getValue() will only work with non-const objects, but is more flexible in that we can use it to both read and write m_value (which we do by assigning the string “Hi”).

The const version of getValue() will work with either const or non-const objects, but returns a const reference, to ensure we can’t modify the const object’s data.

This works because the const-ness of the function is considered part of the function’s signature, so a const and non-const function which differ only in const-ness are considered distinct.

Summary

Because passing objects by const reference is common, your classes should be const-friendly. That means making any member function that does not modify the state of the class object const!

8.11 -- Static member variables
Index
8.9 -- Class code and header files

49 comments to 8.10 — Const class objects and member functions

  • Shri

    In the following statement const is mentioned twice, what does this implies.
    const int& GetValue() const { return m_nValue; }

    • The leftmost const applies to the return value of the function. GetValue() is returning a const reference to an int. If the return value were not const, we could do something like this:

      cSomething.GetValue() = 5;

      This works because cSomething.GetValue() would return a reference to an int, which is set to m_nValue. So this essentially becomes:

      m_nValue = 5;

      Obviously this defeats the spirit of the function, so we make the return value const to ensure people don’t modify the member variables via assignment this way.

      The rightmost const applies to the function. This means the GetValue() can be called on const objects because it’s guaranteed not to modify any of the member values.

  • Tony

    So I guess const objects can have their variables changed, so long as they are changed by a constructor or member functions called by the constructor when the object is instantiated?

  • Alex

    Nice explanation … If possible please update the const section with the mutable keyword
    as both the topics go along please update the tutorial also πŸ™‚

    Anand

    • Alex

      I agree, this would be a good place to discuss the mutable keyword thematically. However, the mutable keyword is used so infrequently, I’m not sure it’s even worth the mention at all. I’ll revisit the topic later.

  • LOLY

    should I understand from ur last reply and Tony’s comment that we can modify the const objects ?

    I think we can’t .. can we ?

    • It’s better to think about it this way:

      Const objects MUST be initialized with a value, but CANNOT have a value assigned to them beyond that (without doing stuff you shouldn’t be doing, like casting away the constness of a variable).

  • Fraz

    Im using Borland C++ 5.0 compiler and it allows non constant member functions to call constant datamembers. would u explain why above two violated rules are not caught by my compiler?

    • C

      Scary. I compiled this code using g++ and all the errors were caught.

    • Alex

      Because your compiler sucks? πŸ™‚

  • Anand Kumar

    Hello Alex,

    I am quite new to C++, Whatever I have read that Overloading the function doesn’t depend on Return type of function,
    Overloading the function only depend on Signature of fucntion(i.e. no of args or types of args.)

    Here one thig is confusing to me, Overloading function GetValue

    and

    Please help me over here.

    Thanks a lot.
    Anand.

    • Alex

      The const-ness of the function itself is considered part of the function’s signature. Thus, a const and non-const function are considered different even if their parameters are identical.

  • C

    Thanks, Alex. It may be good to mention sneaky tricks you can do to get around const-ness. Example:

    I got this from Stephen Dewhurst’s “C++ Common Knowledge”. This code will compile, since Cheat() is modifying what buffer refers to, not buffer itself, even though Cheat() is declared const!

    • Alex

      Interesting. However, instead of cheating, you could always use the mutable keyword to make member variable m editable via the Cheat() function.

      • Darren

        I seem to remember reading something in Scot Meyer’s book (55 specific ways to improve your C++ programming) about there being a difference between logical constness and bit-wise constness. I think this might be an example of that. I’ll have to re-read that item.

  • gans

    Hi Alex,
    I strange thing about Const object:

    lets say I have a class A, and no default or parameterised constructor. but in the main I try to create a const object of this.

    const A a;
    it does not throw any compilation error.
    but if i do it for built in datatype like const int i, it tells us initialise at the time of creation.

    Thanks,

    • Alex

      Yes, because class A has no constructors, the instantiation of variable a will call a default constructor that the compiler creates for you, and that satisfies the object’s requirement to be initialized (even if it does nothing!)

  • abhi4556

    const class objects can only call const member functions
    Note: that constructors should not be marked as const. This is because const objects should initialize their member variables, and a const constructor would not be able to do so.

    I think the above lines contradict each other. Please explain.

    • Alex

      You don’t explicitly call a constructor, it’s called implicitly when the object is instantiated. I’ve updated the lesson to note that const class objects can only explicitly call const member functions.

  • Abhishek

    Please correct the below mentioned code as it won't be compiled due to uninitialized const:
    Something cSomething;
    cSomething.GetValue(); // calls non-const GetValue();

    const Something cSomething2;
    cSomething2.GetValue(); // calls const GetValue();

  • Janez

    I still don’t get that private default constructor. What does it do, why is it there?

  • Chris

    Hi, really loving the tutorials but I’m a bit confused on the overloading section of this one.
    In the section on overloading const functions you wrote this:
        const int& GetValue() const { return m_nValue; }
        int& GetValue() { return m_nValue; }

    Is the first const on the first line required? I found that my program wouldn’t work if I failed to include the second const, but the first one appeared to have no effect.

    • Alex

      It should be required. In a const member function, the member variables are treated as const. You shouldn’t be able to return a non-const reference to a const variable.

  • Quang

    Hi Alex thank you for the tutorial!i have 1 question:
    - In the last example, why do we use "void PrintDate(const Date &cDate)" instead of just "void PrintDate(const Date cDate)".
    I try to re-study 7.3 but its pretty unclear to me. Can you somehow use an example to make me understand? Ty so much

    • Alex

      void PrintDate(const Date cDate) passes Date by value. This means PrintDate gets a copy of the Date argument.
      void PrintDate(const Date &cDate) passes Date by const reference. This means cDate is a const reference to the actual Date argument.

      They’ll both work, but passing by const reference is faster for classes, because we don’t have to make a copy of the Date class every time when the function is called.

  • Gopal

    Overloading a function with a const and non-const version is typically done when the return value needs to differ in constness. In the example above, the const version of GetValue() returns a const reference, whereas the non-const version returns a non-const reference.

    -- I think non-const version returns a non-const "valve" not a non-const "refernce". Correct me if im wrong.

  • Danny

    I am totally lost after reading this tutorial…. I think I should come back tomorrow after getting enough sleep

    • Alex

      Sure. Get a good night’s sleep, and if you still have any questions after reading it, ask away.

      • Danny

        This is what I get after reading again….once a constant object is created, it invokes the default constructor created by the programmer(or parameterized if any), which in turn initializes the data members to constant values and cannot be changed henceforth. Am I right or something…..

        • Alex

          Yes. When a class object is created, the appropriate constructor is called, which can initialize any const (or reference) parameters that need to have values upon initialization. In the case of const values, those can’t be changed later because they’re const.

  • Meghana

    int m_value;

        const int& getValue() const { return m_value; }
        int& getValue() { return m_value; }

    Should the return type be int& or int?

    • Alex

      It could be either. In this case, if the intent were to use getValue() as an access function, it would probably be better to return an int or const int, since returning fundamental variables by value is fine.

      But imagine we were returning some class object instead, and we didn’t want to make a copy of for performance reasons. In that case, it would make sense to have the non-const version return a non-const reference, and the const version return a const reference.

      I’ve updated the example to use a std::string instead of an int, since that’s more reflective of a case you’d want to use a reference instead of a return by value.

  • Shiva

    That’s lotsa ‘const’ in this chapter… πŸ˜€

    I got the concept, thanks to Alex, but this made me dizzy. The const member function syntax couldn’t be weirder. I looked at it for sometime, and realised there was no other way the language designers could implement it. Bless them. πŸ™‚

    Did a ⌘F, there are 215 ‘const’s on this page including this comment. πŸ˜€

    • Alex

      I only got 128 const’s, and this includes instances of the word “constructor” which shouldn’t really be included. πŸ™‚

      But thanks a lot for adding to the const count! πŸ˜›

  • Darren

    Overloading a member function on the const keyword alone can create a read version of the function and a write version of the function. Taking your example Alex:

    the first version of getValue() provides a read-only function. The second version of getValue() provides a write version such that we can change the value of m_value (in a non-const instance of Something) by writing

    I think this is allowable even if m_value is declared private. (this is probably what the .at() member functions for std::vector and others looks like).

  • ADRIA THERIOT

    My business partners were requiring It 215 form 2015 earlier this week and located an excellent service that has a huge forms library . If people are searching for It 215 form 2015 too , here’s

  • Ajay Beniwal

    You said that
    "Note that constructors should not be marked as const. This is because const objects need to be able to initialize their member variables, and a const constructor would not be able to do so."
    It means someone can try to do this.

    But cv qualifiers like const, volatile, const volatile can be only apply to methods and to storage not to constructor or destructor. You simply can’t do that.

  • Shekhar

    Hi Alex, thanks for the great tutorial !
    Can you please address one query I had

    In the example of the last section on "Overloading const and non-const function", the getvalue() function returns a public class variable by reference which we then modify in the main function (in the case of the non const object only).

    I thought that this was only possible because the class variable is public. However, when I tried the code with the m_value class variable as private, the code still compiles without an error. Why is that ? The getvalue function returns a reference to a private class member which we then modify in the main. Why is this allowed to happen ?

    Thanks
    Shekhar

    • Alex

      Access specifiers only determine who can DIRECTLY access a member -- this is enforced at compile time.

      However, if we have a reference or pointer to a private member and someone outside of the class gets a hold of it, they can directly modify the private member. This assignment and modification happens at runtime, so the compiler can’t stop/enforce it.

  • Shekhar Sharma

    Thanks Alex.

  • Mauricio Mirabetti

    Dear Alex, two things:
    1. I guess it was a small typo on the following line:
    [CODE]
    something.m_nValue = 5; // compiler error: violates const <- should be m_Value;
    [\CODE]

    2. Regarding the non-const version of getValue on this example:
    [CODE]
    Something something;
    something.getValue() = "Hi"; // calls non-const getValue();
    [\CODE]
    I got really confused, but only till I noticed it returns a reference of the member variable. Is it usual to build access functions allowing read/write to member variables? Could this effect be used, for example, to read the value and, atomically, change it, as on a semaphore or a state machine status control?

    Best regards.

    Mauricio

    • Alex

      1) Fixed, thanks!
      2) The whole point of access functions is to allow you to read/write to member variables. However, what’s more in debate is whether it’s okay for a getter to return a reference to a variable (providing direct non-encapsulated access to the internal member). I do this above to prove that the value being returned is non-const (and can thus be changed).

      It’s not uncommon to see people writing getters that return member variables by non-const reference. However, my personal opinion is that this is not a great thing to do, as it allows the user to avoid encapsulation by changing the state of the class without the classes knowledge. It’s better to have getters return by value or const reference.

      You can use access functions along with a semaphore to enforce atomic changes if you’re writing a multithreaded application.

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter