Question

C++ Const Member Functions: Meaning and Usage

cppconstantsfunction-qualifier

Question

In C++, what does the const keyword mean when it appears at the end of a member function declaration, as in this class?

class Foo {
public:
    int Bar(int randomArg) const {
        // code
    }
};

How does this affect what the function can do and how it can be called?

Short Answer

A const after a C++ member function declares a const member function. It promises not to modify the observable state of the object it is called on. By the end, you will know what this promise means, why it matters, and how to write and use const-correct classes.

Concept

A member function has an implicit object: the instance before the dot.

Foo item;
item.Bar(42);

Inside Bar, C++ effectively provides access to that object through a hidden this pointer. When the function ends with const:

int Bar(int randomArg) const;

the hidden pointer behaves conceptually like this:

const Foo* const this;

Therefore, the function cannot change ordinary data members or call non-const member functions on the current object.

This is important because many functions only inspect an object: getters, formatters, comparison functions, and calculations. Marking them const documents that behavior and lets them work with const objects and references.

class Counter {
public:
    int value() const {
        return count;
    }

    void increment() {
        ++count;
    }

private:
    int count = 0;
};

value() reads count, so it is const. increment() changes count, so it cannot be const.

Mental Model

Think of an object as a library book.

  • A non-const member function may write notes in the book, erase text, or add pages.
  • A const member function is allowed only to read the book.

The const after the function name is a promise from the class author: “Calling this function will not change this object's normal state.”

This differs from const on a parameter. In this declaration:

int Bar(int randomArg) const;
  • int randomArg is a regular parameter.
  • The final const applies to the object on which Bar is called.
Take Quiz

Syntax and Examples

The syntax places const after the parameter list and before the function body or semicolon.

class Temperature {
public:
    double celsius() const {
        return celsiusValue;
    }

    void setCelsius(double value) {
        celsiusValue = value;
    }

private:
    double celsiusValue = 0.0;
};

A const member function can:

  • Read data members.
  • Use its parameters and local variables normally.
  • Call other const member functions.
class Temperature {
public:
    double fahrenheit() const {
        return celsius() * 9.0 / 5.0 + 32.0;
    }

    double celsius() const {
        return celsiusValue;
    }

private:
    double celsiusValue = 0.0;
};

It cannot modify a normal member:

class Temperature {
public:
    double celsius() const {
        // celsiusValue = 20.0; // Error: modifies the object
        return celsiusValue;
    }

private:
    double celsiusValue = 0.0;
};

It also allows calls on const objects:

const Temperature roomTemperature;
double current = roomTemperature.celsius(); // Valid

// roomTemperature.setCelsius(21.0); // Error

Step by Step Execution

Consider this class and code:

class Rectangle {
public:
    int area() const {
        return width * height;
    }

    void resize(int newWidth, int newHeight) {
        width = newWidth;
        height = newHeight;
    }

private:
    int width = 4;
    int height = 3;
};

int main() {
    const Rectangle card;
    int result = card.area();
}

Step by step:

  1. card is created with width equal to 4 and height equal to 3.
  2. card is declared const, so code must not modify it.
  3. card.area() is allowed because area() is declared with a trailing const.
  4. area() reads width and height.
  5. It calculates 4 * 3 and returns 12.
  6. result receives 12.

This would fail to compile:

card.resize(10, 5);

resize() can change the object, so it cannot be called on card.

Real World Use Cases

Const member functions are common whenever an object is queried without being changed.

  • Getters: user.id() const, product.price() const
  • Display and logging: order.toString() const, config.print() const
  • Calculations: shape.area() const, cart.total() const
  • Checks: file.isOpen() const, queue.empty() const
  • Comparisons: account.equals(other) const
  • Container access: standard-library containers provide functions such as size() const, empty() const, and begin() const for read-only use.

For example, a web service may receive an application configuration as a const Config&. Handler code can read values such as a database host and timeout, but cannot accidentally alter global configuration.

Take Quiz

Real Codebase Usage

In production C++ code, const member functions are part of const correctness: expressing which code reads data and which code changes data.

A common pattern is to pass objects by const reference when a function only needs to inspect them:

class User {
public:
    const std::string& name() const {
        return name_;
    }

private:
    std::string name_;
};

void printUser(const User& user) {
    std::cout << user.name() << '\n';
}

Because printUser receives const User&, it can call only const member functions. This makes accidental changes impossible at compile time.

Classes often provide two overloads for accessors that return references:

class Document {
public:
    std::string& title() {
        return title_;
    }

    const std::string& title() const {
        return title_;
    }

private:
    std::string title_;
};
  • A non-const Document can receive a writable std::string&.
  • A const Document receives a read-only const std::string&.

This pattern preserves safety while avoiding unnecessary copies.

Common Mistakes

Trying to modify a member in a const function

class Score {
public:
    void reset() const {
        points = 0; // Error
    }

private:
    int points = 10;
};

Remove const if resetting is part of the function's purpose.

Forgetting const on a getter

class Book {
public:
    int pages() { // Missing const
        return pages_;
    }

private:
    int pages_ = 250;
};

void printPages(const Book& book) {
    // book.pages(); // Error: pages() is not const
}

Add trailing const when the function only observes the object.

Confusing trailing const with a const return value

const int value() const;

These two uses are separate:

  • const int applies to the returned value. For built-in values returned by value, this is usually unnecessary.
  • The trailing const applies to the current object.

Assuming const makes all reachable data immutable

A const member function cannot modify normal members directly, but it may still change an object through a non-const pointer member:

class Holder {
public:
    void changePointedValue() const {
        *value_ = 10; // Allowed if value_ points to non-const int
    }

private:
    int* value_;
};

const protects the Holder object's direct state; it does not automatically make every object it points to const.

Using mutable to bypass const without a reason

mutable permits a member to change inside const functions. Use it only for state that does not change the object's logical value, such as a cache or synchronization mechanism.

Comparisons

DeclarationWhat is const?Can it modify the current object?
int size() const;The member function's object (*this)No, except mutable members
int print(const int value);The local parameter valueUnrelated to the current object
const std::string& name() const;The returned referenced string and the current objectNo
void update();Nothing is constYes

A const and non-const member function may have the same name and parameters because their const qualification differs:

class Buffer {
public:
    char& at(std::size_t index) {
        return data_[index];
    }

    const char& at(std::size_t index) const {
        return data_[index];
    }

private:
    std::string data_ = "hello";
};

The compiler selects the appropriate overload based on whether the Buffer object is const.

Take Quiz

Cheat Sheet

class Example {
public:
    int read() const; // Does not modify ordinary members
    void write(int x); // May modify members
};
  • Put const after () in a member function declaration.
  • A const member function may read members.
  • It cannot assign to normal members or call non-const member functions on *this.
  • A const object can call only const member functions.
  • Prefer const for getters, checks, formatting, and calculations that do not change the object.
  • mutable members may change in const functions; use this sparingly.
  • const on a parameter and trailing const on a member function have different meanings.
  • In an overload pair, const objects select the const overload.

FAQ

Can a const member function change local variables?

Yes. Local variables belong to the function call, not to the object.

int doubled() const {
    int result = value_ * 2;
    return result;
}

Can a const member function call another member function?

Yes, but normally only another const member function. A non-const function might modify the object.

Does trailing const make the entire object permanently const?

No. It applies only while executing that particular member function. Non-const functions can still modify a non-const object.

Why does a getter need const?

Without it, the getter cannot be called through a const object or const reference, which is common in function parameters and APIs.

Can constructors be const?

No. Constructors initialize a new object and cannot have a trailing const qualifier.

What is a mutable member in C++?

A mutable data member may be changed even in a const member function. It is commonly used for implementation details such as cached results, not for normal object state.

Is const after a function the same as const before a return type?

No. Trailing const qualifies the member function's object. A leading const qualifies the return type.

Take Quiz

Mini Project

Description

Build a small BankAccount class that separates read-only account operations from operations that change the balance. This reflects real code where reporting code should inspect account data without being able to modify it.

Goal

Create a class whose read-only methods work with a const BankAccount reference while deposit and withdrawal methods remain state-changing operations.

Requirements

Implement a BankAccount class with an owner name and balance.

Add a const owner() method that returns the account owner.

Add a const balance() method that returns the current balance.

Add a deposit() method that increases the balance.

Add a const canAfford() method that checks whether a requested amount is available.

Write a function that prints account details through a const BankAccount&.

Take Quiz

Keep learning

Related questions

View more