1. Concept
The keyword const in C++ is used to define read-only variables or to restrict the modification behavior of objects/functions. Its core function is to protect data from being accidentally modified while enhancing code readability and safety. const can be applied to variables, pointers, function parameters, function return values, class members, and more.
2. Defining Constants (const Variables)
When const modifies a regular variable, that variable becomes a read-only constant, which must be initialized at the time of definition and cannot be modified afterward.
Syntax:
const type variable_name = initial_value; // or type const variable_name = initial_value; // equivalent (the former is recommended for clarity)
Example:
#include <iostream>using namespace std;int main() { const int MAX_NUM = 100; // Define a constant, cannot be modified // MAX_NUM = 200; // Error: const variable cannot be assigned a new value cout << MAX_NUM << endl; // Output: 100 const double PI = 3.1415926; cout << PI << endl; // Output: 3.1415926 return 0;}
Note:
-
const variables have type checking, making them safer than macro definitions (#define), which are simple text replacements without type checking);
-
Local const variables are stored on the stack, while global const variables are stored in the read-only data segment by default.
3. Modifying Pointers (const Pointers)
When const modifies a pointer, it is essential to distinguish between pointers to constants and constant pointers, primarily based on the position of const:
1. Pointer to a Constant (const modifies the pointed content)
The content pointed to by the pointer cannot be modified, but the pointer itself can point to other addresses.
Syntax:
const type* pointer_name; // or type const* pointer_name; // equivalent
Example:
int main() { int a = 10; const int* p = &a; // p points to content that cannot be modified // *p = 20; // Error: cannot modify the pointed constant content a = 20; // Allowed (a itself is not const, only p restricts modification through it) cout << *p << endl; // Output: 20 int b = 30; p = &b; // Allowed: pointer itself can point to another address cout << *p << endl; // Output: 30 return 0;}
2. Constant Pointer (const modifies the pointer itself)
The address of the pointer itself cannot be modified (the pointer points to a fixed address), but the content pointed to can be modified.
Syntax:
type* const pointer_name = initial_address;
Example:
int main() { int a = 10; int* const p = &a; // Pointer p itself is constant, pointing to a fixed address *p = 20; // Allowed: modify the pointed content cout << *p << endl; // Output: 20 // int b = 30; // p = &b; // Error: constant pointer cannot point to another address return 0;}
3. Pointer to a Constant Constant (Double const)
Neither the address of the pointer itself nor the content pointed to can be modified.
Syntax:
const type* const pointer_name = initial_address;
Example::
int main() { int a = 10; const int* const p = &a; // Both the pointer and the pointed content are immutable // *p = 20; // Error: content cannot be modified // p = &b; // Error: pointer address cannot be modified cout << *p << endl; // Output: 10 return 0;}
4. Modifying Function Parameters
When const modifies function parameters, it indicates that the function will not modify the value of that parameter, commonly used to protect the passed arguments (especially reference/pointer parameters).
Scenario 1: Regular Parameters (Pass by Value)
When passing by value, the parameter is copied, and the effect of const is limited (only restricts modifications to the copy within the function), generally not recommended.
Example:
void print(const int num) { // num = 100; // Error: const parameter cannot be modified cout << num << endl;}int main() { print(50); // Output: 50 return 0;}
Scenario 2: Reference/Pointer Parameters (Avoid Copy + Protect Actual Arguments)
Adding const to reference or pointer parameters can prevent the function from accidentally modifying the actual arguments while avoiding copy overhead (suitable for large objects).
Example:
#include <string>using namespace std;// const reference parameter: protects the actual argument from being modified, and avoids copyingvoid printString(const string& str) { // str = "hello"; // Error: const reference cannot be modified cout << str << endl;}// const pointer parameter: protects the pointed content from being modifiedvoid printArray(const int* arr, int size) { for (int i = 0; i < size; ++i) { // arr[i] = 0; // Error: const pointer points to content that cannot be modified cout << arr[i] << " "; }}int main() { string s = "C++ const"; printString(s); // Output: C++ const int arr[] = {1, 2, 3}; printArray(arr, 3); // Output: 1 2 3 return 0;}
5. Modifying Function Return Values
<span><span>const</span></span> modifies function return values, restricting the return value from being modified, commonly used in scenarios returning pointers/references.
Example:
// Returning const reference: prohibits modification of the returned objectconst int& getMax(const int& a, const int& b) { return (a > b) ? a : b;}int main() { int x = 10, y = 20; const int& max_val = getMax(x, y); // max_val = 30; // Error: return value is const reference, cannot be modified cout << max_val << endl; // Output: 20 return 0;}
Note:
-
When the return value is a value type (like const int),
consthas no practical significance (as the return value is a temporary copy, modifying it does not affect the original data); -
When returning pointers/references, adding
constcan prevent the caller from modifying the original data through the return value.
6. Modifying Class Members
const can modify class member variables and member functions to restrict the modification behavior of class objects.
1. const Member Variables
When a class’s member variable is modified by const, it must be initialized in the constructor’s initialization list and cannot be modified afterward.
Example:
class Circle {private: const double PI; // const member variable double radius;public: // Must initialize const member in the initialization list Circle(double r) : PI(3.1415926), radius(r) {} double getArea() { return PI * radius * radius; }};int main() { Circle c(5); cout << c.getArea() << endl; // Output: 78.5398 return 0;}
2. const Member Functions
Adding const after a class’s member function indicates that the function will not modify any member variables of the class (except for mutable members) and can only call other const member functions.
Syntax:
return_type function_name(parameters) const;
Example::
class Person {private: string name; int age;public: Person(string n, int a) : name(n), age(a) {} // const member function: cannot modify member variables string getName() const { // name = "Tom"; // Error: const function cannot modify members return name; } // Non-const member function: can modify member variables void setAge(int a) { age = a; } int getAge() const { // const member function return age; }};int main() { const Person p("Alice", 20); // const object cout << p.getName() << endl; // Allowed: call const member function cout << p.getAge() << endl; // Allowed // p.setAge(21); // Error: const object cannot call non-const member function Person p2("Bob", 25); p2.setAge(26); // Allowed: non-const object can call non-const function return 0;}
Note:
-
const objects can only call const member functions, while non-const objects can call any member function;
-
Mutable member variables can be modified in const member functions (for special scenarios, such as caching, counting).
7. Differences Between const and constexpr
C++11 introduced constexpr for defining constants, but it differs from const:
-
constindicates read-only, and its value can be determined at runtime (like local const variables); -
constexprindicates compile-time constants, and its value must be determined at compile time, usable in template parameters, array sizes, and other compile-time contexts.
Example:
void test9() { // ========== const variable ========== // const variable is read-only, but its value can be determined at runtime const int a = 10; // Although initialized with a literal, a is a local const variable // Before C++11, local const variables could not be used for array sizes (needed compile-time constant expressions) // In C++11 and later, if a const variable is initialized with a literal, some compilers (like GCC) allow it to be used as an array size // But this depends on compiler extensions, not standard C++ behavior, and may cause errors in other compilers (like MSVC) // int arr[a]; // May compile, but relies on compiler extensions, not recommended // ========== constexpr variable ========== // constexpr explicitly indicates compile-time constant, value must be determined at compile time constexpr int b = 20; // Compile-time constant, standard C++ allows int arr2[b]; // Standard C++ allows: constexpr is a compile-time constant expression // ========== Runtime const vs Compile-time constexpr ========== int input = 5; const int c = input; // Value determined at runtime, cannot use constexpr // constexpr int d = input; // Error: input is not a compile-time constant constexpr int e = 30; // Compile-time constant constexpr int f = e * 2; // Compile-time calculation, also a compile-time constant int arr3[f]; // Standard allows: f is a compile-time constant expression // ========== Summary ========== // const: read-only, value can be determined at runtime // constexpr: compile-time constant, value must be determined at compile time, usable in scenarios requiring constant expressions // For array sizes, template parameters, and other scenarios requiring compile-time constants, use constexpr instead of const}
8. Conclusion
The core value of const is **”read-only constraint”**, and its usage in different scenarios can be summarized as:
-
Modifying variables: read-only constants;
-
Modifying pointers: distinguishing between “pointer to constant” and “constant pointer”;
-
Modifying function parameters/return values: protecting data from being modified;
-
Modifying class members: restricting the modification behavior of member variables/functions.
Proper use of const can significantly enhance code safety and readability, representing an important aspect of good C++ programming style.