Last updated:2026-10-12
C++ const Constants and Qualifiers¶
About the Author¶
WeChat official account:

1. What const Means¶
A const type is a type declared with the type qualifier const; the value of a const-type variable or object cannot be updated.
2. What const Does¶
- It can define constants
-
Type checking
-
The difference between const constants and
#definemacro constants:const constants have a type, so the compiler can perform safety checks; #define macro constants have no data type and are mere string replacements, so no safety check is possible.Thanks to two experts for pointing out the problem here, see: issue -
const constants support all types.
- In other cases it is just a
const-qualified variable; do not confuse it with a constant.
-
-
Prevents modification, provides protection, and increases the robustness of the program
-
It can save space and avoid unnecessary memory allocation
- From an assembly point of view, a constant defined with const only gives the corresponding memory address, whereas
#definegives an immediate value. - A constant defined with const has only one copy while the program runs, while a constant defined with
#definehas several copies in memory.
- From an assembly point of view, a constant defined with const only gives the corresponding memory address, whereas
3. const Objects Are Local to a File by Default¶
Note: non-const variables are extern by default. To make a const variable accessible in other files, you must explicitly specify it as extern in the file.
Accessing a variable not modified by const across different files
// file1.cpp
int ext;
// file2.cpp
#include<iostream>
extern int ext;
int main(){
std::cout<<(ext+10)<<std::endl;
}
Accessing a const constant across different files
// extern_file1.cpp
extern const int ext=12;
// extern_file2.cpp
#include<iostream>
extern const int ext;
int main(){
std::cout<<ext<<std::endl;
}
Summary:
You can see that a variable not modified by const needs no explicit extern declaration! A const constant, on the other hand, needs an explicit extern declaration and must be initialized! Since a constant cannot be modified after being defined, it must be initialized at the point of definition.
4. Defining Constants¶
const int b = 10;
b = 0; // error: assignment of read-only variable ‘b’
const string s = "helloworld";
const int i,j=0 // error: uninitialized const ‘i’
There are two errors above: + b is a constant and cannot be changed! + i is a constant and must be initialized! (Since a constant cannot be modified after being defined, it must be initialized at the point of definition.)
5. Pointers and const¶
There are four kinds of const related to pointers:
const char * a; // 指向const对象的指针或者说指向常量的指针。
char const * a; // 同上
char * const a; // 指向类型对象的const指针。或者说常指针、const指针。
const char * const a; // 指向const对象的const指针。
Summary:
If const is on the left of*, then const qualifies the variable the pointer points to, i.e. the pointee is a constant;
if const is on the right of*, const qualifies the pointer itself, i.e. the pointer itself is a constant.
Another way to read it
Read it in English from right to left, using "to" as the dividing line: what comes before "to" describes the pointer's characteristics, and what comes after "to" describes the target's characteristics
const char * p; //p is a pointer to const char
char const * p; //同上
char * const p; //p is a const pointer to char
const char * const p; //p is a const pointer to const char
When the pointed-to target is char, the content can be modified through the pointer, e.g. *char='y';
When the pointed-to target is const char, the content cannot be modified through the pointer
Concrete usage is as follows:
(1) Pointer to a constant
ptr is a pointer to a const object of type int; the const qualifies the int type — the type of the object ptr points to — not ptr itself, so ptr does not need an initial value. However, you cannot use ptr to modify the value of the object it points to.
Besides that, you also cannot use a void* pointer to hold the address of a const object; you must use a const void* pointer to hold the address of a const object.
Another key point: it is allowed to assign the address of a non-const object to a pointer to a const object.
Assigning the address of a non-const object to a pointer to a const object:
We cannot use the ptr pointer to modify the value of val, even though it points to a non-const object!
We cannot use a pointer to a const object to modify the underlying object; however, if the pointer points to a non-const object, that object can be modified by other means. The value a const pointer points to can be modified — just not through the const object pointer! Modify it as follows:
Summary:
1. For a pointer to a constant, the object's value cannot be modified through the pointer.
2. You cannot use a void*pointer to hold the address of a const object; you must use a const void*pointer to hold the address of a const object.
3. It is allowed to assign the address of a non-const object to a pointer to a const object; if you want to modify the value of the object the pointer points to, you must do so by other means, not directly through the current pointer.
(2) Constant pointer
A const pointer must be initialized; the value a const pointer points to can be modified, but its pointing cannot.
#include<iostream>
using namespace std;
int main(){
int num=0, num1=1;
int * const ptr=# // const指针必须初始化!且const指针的指向不能修改
ptr = &num1; // error! const指针不能修改指向!
cout<<*ptr<<endl;
}
The code produces a compile error: a const pointer cannot change what it points to.
#include<iostream>
using namespace std;
int main(){
int num=0, num1=1;
int * const ptr=# // const指针必须初始化!且const指针的指向不能修改
*ptr = 1;
cout<<*ptr<<endl;
}
Nothing goes wrong with this code; it prints 1 normally.
Finally, when the address of a const constant is assigned to ptr, since ptr points to a variable rather than a const constant, an error occurs: const int* -> int * error!
#include<iostream>
using namespace std;
int main(){
const int num=0;
int * const ptr=# // error! const int* -> int*
cout<<*ptr<<endl;
}
If the above is changed to const int *ptr or to const int *const ptr, both work fine!
(3)Constant pointer to a constant
Having understood the previous two cases, this one is fairly easy to understand:
ptr is a const pointer that points to a const object of type int.
6. Using const in Functions¶
const qualifying a function's return value
This has essentially the same meaning as const qualifying ordinary variables and pointers:
(1)const int
This is meaningless in itself, because the return value is assigned to another variable anyway!
(2)const int*
The content the pointer points to does not change.
(3)int *const
The pointer itself is immutable.
const qualifying function parameters
(1)The parameter passed in, or the pointer itself, is immutable inside the function — meaningless!
This indicates that the parameter cannot be modified inside the function body, but here it carries no meaning: var itself is a formal parameter and will not change inside the function. The same applies when the incoming parameter is a pointer.
For input parameters passed by value, since the function automatically creates a temporary variable to copy the parameter, such an input parameter needs no protection in the first place, so do not add const.
(2)The content pointed to by the parameter pointer is a constant and cannot be changed
Here src is an input parameter and dst is an output parameter. After adding const to src, if a statement inside the function body tries to modify the content of src, the compiler will point out the error. This is one of the effects of adding const.
(3)The parameter is a reference, to increase efficiency while preventing modification.
For parameters of non-inner data types, a function declared like void func(A a) is bound to be less efficient, because the function body will create a temporary object of type A to copy the parameter a, and constructing, copying, and destroying that temporary object all take time.
To improve efficiency, the function declaration can be changed to void func(A &a), because pass-by-reference merely borrows the alias of the parameter and does not need to create a temporary object.
But the function void func(A &a) has a drawback:
Pass-by-reference may change the parameter a, which we do not expect. This problem is easy to solve: just add const, and the function finally becomes void func(const A &a).
By analogy, should void func(int x) be rewritten as void func(const int &x) to improve efficiency? There is no need at all, because parameters of inner data types involve no construction or destruction, and copying is very fast, so pass-by-value and pass-by-reference are almost equally efficient.
Summary:
1. For input parameters of non-inner data types, you should change pass-by-value to const pass-by-reference in order to improve efficiency. For example, change void func(A a) to void func(const A &a).
2. For input parameters of inner data types, do not change pass-by-value to const pass-by-reference. Otherwise you neither achieve the efficiency gain nor keep the function easy to understand. For example, void func(int x) should not be changed to void func(const int &x).
The above settles two interview questions:
- If a function takes a pointer, does that pointer need const, and what is the difference between putting const in different positions of the pointer;
- If the function you are writing takes an instance of a complex type as its parameter, what is the difference between passing it by value or by reference, and when does the incoming reference parameter need const.
7. Using const in Classes¶
In a class, any function that does not modify data members should be declared const. If, while writing a const member function, you accidentally modify a data member, or call another non-const member function, the compiler will point out the error, which undoubtedly improves the robustness of the program.
A member function declared with the const keyword is called a constant member function. Only constant member functions are qualified to operate on constants or constant objects; member functions not declared with the const keyword cannot be used to operate on constant objects.
const member variables in a class must be initialized via the initializer list, as shown below:
class Apple{
private:
int people[100];
public:
Apple(int i);
const int apple_number;
};
Apple::Apple(int i):apple_number(i)
{
}
const objects can only access const member functions, whereas non-const objects can access any member function, including const member functions.
For example:
// apple.cpp
class Apple
{
private:
int people[100];
public:
Apple(int i);
const int apple_number;
void take(int num) const;
int add();
int add(int num) const;
int getCount() const;
};
// apple.cpp
Apple::Apple(int i) : apple_number(i)
{
}
int Apple::add(int num)
{
take(num);
return 0;
}
int Apple::add(int num) const
{
take(num);
return 0;
}
void Apple::take(int num) const
{
std::cout << "take func " << num << std::endl;
}
int Apple::getCount() const
{
take(1);
// add(); // error
return apple_number;
}
int main()
{
Apple a(2);
cout << a.getCount() << endl;
a.add(10);
const Apple b(3);
b.add(100);
return 0;
}
// main.cpp
Compile: bazel run basic_content/const/class_const/first_example:main
This produces an error: the getCount() method above calls an add method, and add is not qualified by const, so a run-time error occurs. In other words, const member functions can only access const member functions.
When the call is changed to:
This proves that const objects can only access const member functions.Besides initializing const constants with the initializer list as above, you can also use the following methods:
First: combine the constant definition with static, i.e.:
Second: initialize it outside:
Of course, if you compile with C++11, you can initialize directly at the declaration, written simply as:
Both of these are supported in C++11!
Just add -std=c++11 when compiling!
This brings up static, so a brief note on it below:
In C++, static member variables cannot be initialized inside the class. Inside the class there is only a declaration; the definition must be outside the class definition body, usually initialized in the class's implementation file.
Declare inside the class:
Use in the class implementation file:
For this item, C++11 cannot declare and initialize at the same time — i.e. the usage shown above.
Related Topics¶
- C++ Pure Virtual Functions and Abstract Classes | C++那些事
- C++ assert | C++那些事
- C++ Bit Fields | C++那些事
- Implementing C++ Polymorphism in C | C++那些事
- The Complete C++ const Family | C++那些事
- C++ Dereference Operator * | C++那些事
- C++ Initialization and Assignment | C++那些事
- Ways to Return Multiple Values in C++ | C++那些事
- C++ mutable Keyword | C++那些事
- C++ restrict and Compiler Optimization | C++那些事