Const Class Objects and Const Member Functions

Just as variables can be const, objects can be const too. A const object's member variables cannot be modified after initialization.

Const objects

class Position
{
public:
    int x{};
    int y{};
};

int main()
{
    const Position spawn{ 100, 200 };
    spawn.x = 50;  // Error: cannot modify const object

    return 0;
}

Once spawn is created, neither x nor y can be changed.

The problem with const objects

Const objects can't call regular member functions—even functions that don't modify the object:

class Position
{
public:
    int x{};
    int y{};

    void display()  // Not marked const
    {
        std::cout << "(" << x << ", " << y << ")\n";
    }
};

int main()
{
    const Position spawn{ 100, 200 };
    spawn.display();  // Error!

    return 0;
}

Why? The compiler doesn't know whether display() modifies the object. To be safe, it forbids the call.

Const member functions

Mark member functions that don't modify the object as const member functions using the const keyword after the parameter list:

class Position
{
public:
    int x{};
    int y{};

    void display() const  // Now a const member function
    {
        std::cout << "(" << x << ", " << y << ")\n";
    }
};

int main()
{
    const Position spawn{ 100, 200 };
    spawn.display();  // OK now!

    return 0;
}

Const member functions promise not to modify the object. This lets const objects call them safely.

Rules for const member functions

Const member functions:

  • Cannot modify member variables
  • Cannot call non-const member functions
  • Can be called on both const and non-const objects
class Level
{
public:
    int number{};

    void advance()  // Non-const
    {
        ++number;
    }

    int getNumber() const  // Const
    {
        return number;
    }

    void badFunction() const
    {
        number = 10;      // Error: can't modify members
        advance();        // Error: can't call non-const function
    }
};

int main()
{
    Level stage{};
    stage.advance();       // OK
    stage.getNumber();     // OK

    const Level finalStage{ 99 };
    finalStage.advance();     // Error: can't call non-const on const object
    finalStage.getNumber();   // OK

    return 0;
}

When to use const member functions

Mark a member function const if it:

  • Doesn't modify any member variables
  • Only calls other const member functions
  • Observes or queries the object's state without changing it
class Shape
{
public:
    double width{};
    double height{};

    double area() const  // Doesn't modify
    {
        return width * height;
    }

    bool isSquare() const  // Doesn't modify
    {
        return width == height;
    }

    void scale(double factor)  // Modifies
    {
        width *= factor;
        height *= factor;
    }
};

Const objects with mutable

Sometimes you need to modify a member variable even in a const function (like caching a computed value). Use the mutable keyword:

class PathFinder
{
public:
    int startX{};
    int startY{};
    mutable int cachedDistance{};
    mutable bool isCached{};

    int computeDistance() const
    {
        if (!isCached)
        {
            cachedDistance = startX + startY;  // Simplified calculation
            isCached = true;  // OK: mutable
        }
        return cachedDistance;
    }
};

This is advanced—use sparingly.

Best Practice
Mark member functions const by default. Only make them non-const if they actually need to modify the object. This documents intent (this function is read-only), enables use with const objects, prevents accidental modifications, and enables compiler optimizations.
class Enemy
{
private:
    int health{};
    int damage{};

public:
    // Setters: non-const (they modify)
    void setHealth(int hp) { health = hp; }

    // Getters: const (they don't modify)
    int getHealth() const { return health; }
    int getDamage() const { return damage; }
    bool isAlive() const { return health > 0; }
};

Summary

Const objects: Objects declared const cannot have their member variables modified after initialization. This ensures object immutability and prevents accidental modifications.

Const member functions: Member functions marked with the const keyword promise not to modify the object. These functions can be called on both const and non-const objects, while non-const functions can only be called on non-const objects.

Const correctness rules: Const member functions cannot modify member variables, cannot call non-const member functions, but can be called on any object. This creates a clear contract about which operations are read-only versus which modify state.

The mutable keyword: The mutable keyword allows specific member variables to be modified even in const member functions. This is useful for implementation details like caching, but should be used sparingly to maintain const correctness.

Const member functions are essential for creating robust, predictable classes. By marking functions const by default and only making them non-const when they truly need to modify state, you create self-documenting code that prevents bugs and enables compiler optimizations.