Mutable Keyword
Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 30 of 104.
You've learned that const member functions cannot modify any member variables. But sometimes you need to change a member that doesn't affect the object's logical state - like a cache or an access counter. The mutable keyword provides an exception to the const restriction.
A mutable member can be modified even inside a const member function:
class DataFetcher {
std::string data;
mutable int accessCount; // Can be modified in const functions
public:
DataFetcher(std::string d) : data(d), accessCount(0) {}
std::string getData() const {
accessCount++; // Allowed because accessCount is mutable
return data;
}
int getAccessCount() const {
return accessCount;
}
};Without mutable, incrementing accessCount inside the const function getData() would cause a compiler error. The mutable keyword tells the compiler: "This member is allowed to change even when the object is considered const."
Common use cases for mutable include caching computed values, tracking access statistics, and synchronization primitives like mutexes. In each case, the modification doesn't change what the object logically represents - a cached value is just an optimization, and an access counter is metadata about usage.
class Circle {
double radius;
mutable double cachedArea;
mutable bool areaCached;
public:
double getArea() const {
if (!areaCached) {
cachedArea = 3.14159 * radius * radius;
areaCached = true;
}
return cachedArea;
}
};Use mutable sparingly - it should only be applied to members that don't affect the object's observable state.
Challenge
EasyLet's build a document viewer that tracks how many times a document has been viewed — without breaking the const-correctness of our read-only methods. This is a perfect use case for the mutable keyword.
You'll create two files to organize your code:
Document.h: Define aDocumentclass that stores content and secretly tracks view statistics. Your class needs:- Private members:
title(string),content(string), and amutable int viewCountthat can be modified even in const methods - A constructor that takes a title and content, initializing
viewCountto0 - A
getTitle()const method that returns the title - A
getContent()const method that incrementsviewCount(allowed because it's mutable) and returns the content - A
getViewCount()const method that returns how many times the content has been accessed - A
getPreview()const method that returns the first 20 characters of the content followed by"..."— this should NOT increment the view count since it's just a preview
- Private members:
main.cpp: Demonstrate how mutable allows tracking access statistics while keeping methods logically const. Read a document title and content from input (each on a separate line), then:- Create a
Documentwith the input values - Print
"Title: <title>" - Print
"Preview: <preview>" - Print
"Views after preview: <count>" - Access the full content using
getContent()and print"Content: <content>" - Print
"Views after first read: <count>" - Access the content again and print
"Content: <content>" - Print
"Views after second read: <count>" - Create a helper function
void displayDocument(const Document& doc)that prints"[Const Access] <title>: <content>"— this proves our const methods work with const references - Call
displayDocument()with your document - Print
"Final view count: <count>"
- Create a
The view count should be 0 after the preview (since previews don't count as full views), then increment each time getContent() is called. The mutable keyword makes this possible — without it, you couldn't modify viewCount inside const methods like getContent().
If the content is shorter than 20 characters, getPreview() should return the entire content followed by "...".
Cheat sheet
The mutable keyword allows a member variable to be modified even inside const member functions:
class DataFetcher {
std::string data;
mutable int accessCount; // Can be modified in const functions
public:
DataFetcher(std::string d) : data(d), accessCount(0) {}
std::string getData() const {
accessCount++; // Allowed because accessCount is mutable
return data;
}
int getAccessCount() const {
return accessCount;
}
};Without mutable, modifying a member variable inside a const function would cause a compiler error. The keyword tells the compiler that this member is allowed to change even when the object is considered const.
Common use cases include caching computed values, tracking access statistics, and synchronization primitives. The modification should not change what the object logically represents:
class Circle {
double radius;
mutable double cachedArea;
mutable bool areaCached;
public:
double getArea() const {
if (!areaCached) {
cachedArea = 3.14159 * radius * radius;
areaCached = true;
}
return cachedArea;
}
};Use mutable sparingly - only for members that don't affect the object's observable state.
Try it yourself
#include <iostream>
#include <string>
#include "Document.h"
using namespace std;
// TODO: Implement the displayDocument function
// It should print: "[Const Access] <title>: <content>"
void displayDocument(const Document& doc) {
// TODO: Implement this function
}
int main() {
// Read input
string title;
string content;
getline(cin, title);
getline(cin, content);
// TODO: Create a Document with the input values
// TODO: Print "Title: <title>"
// TODO: Print "Preview: <preview>"
// TODO: Print "Views after preview: <count>"
// TODO: Access full content and print "Content: <content>"
// TODO: Print "Views after first read: <count>"
// TODO: Access content again and print "Content: <content>"
// TODO: Print "Views after second read: <count>"
// TODO: Call displayDocument() with your document
// TODO: Print "Final view count: <count>"
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container