The const
keyword (which stands for constant) is used to specify that the value of a variable cannot be changed anywhere in the program, either intentionally or accidentally. For example,
// makes PI a constant
const double PI = 3.14;
Note: The standard naming convention for constants is that they must all be in uppercase letters. For example,
const int MAGIC_NUMBER = 42;
Create const Variables
A const
variable must be initialized during declaration and cannot be changed later.
// invalid since PI is not initialized
const double PI;
Here, we get an error because the const
variable PI has not been initialized.
Instead, we should create const
variables by initializing them in the following way:
// initialize const with a value
const double PI = 3.14;
Example 1: C++ const
#include <iostream>
using namespace std;
int main() {
// initialize a const PI
const double PI = 3.14;
int radius = 4;
// use the const in a calculation
double area = PI * radius * radius;
cout << "Area of circle with radius " << radius << " is: " << area;
return 0;
}
Output
Area of circle with radius 4 is: 50.24
In the above example, we have initialized a constant variable PI.
const double PI = 3.14;
Then, we have used this constant variable to calculate the area of a circle:
double area = PI * radius * radius;
Const References
We can also make references using the const
keyword. There are different types of such references with different behaviors. These are
Reference Type | Description |
---|---|
Constant reference to a constant variable | Both the variable and the reference are constant. |
Constant reference to a non-constant variable | The variable is non-constant but the reference is constant. |
Const Reference to a Const Variable
In C++, we can create a const
reference to a const
variable using a reference variable. For example,
#include <iostream>
using namespace std;
int main() {
// initialize a constant PI
const double PI = 3.14;
// create a read-only reference to PI
const double &PI_REF = PI;
cout << "PI: " << PI;
cout << "\nPI_REF: " << PI_REF;
return 0;
}
Output
PI: 3.14 PI_REF: 3.14
In the above example, we have created a constant reference to a constant or a constant reference.
So, we'll get an error if we try to modify the value of PI_REF.
// error: assignment of read-only reference 'PI_REF'
PI_REF = 90;
Frequently Asked Question
Yes, a constant reference can refer to a non-constant variable. However, we cannot modify the value using that reference. For example
#include <iostream>
using namespace std;
int main() {
int num = 10;
// constant reference to a non constant variable
const int &NUM_REF = num;
cout << NUM_REF;
return 0;
}
Output
10
In the above example, we have created a constant reference to a non-constant variable.
As a result, we cannot modify the value of num using the reference variable NUM_REF.
// error: assignment of read-only reference 'NUM_REF'
NUM_REF = 67;
Passing as a Constant Reference
We can use a constant parameter to ensure that the passed argument is not modified by the function body.
For instance, we can ensure that the vector we pass is not modified by using the following code:
int sum(const vector<int> nums) {
// function body
}
Although the const
keyword in the above code guarantees that nums cannot be modified, it creates a local copy of nums, which costs memory.
A better way to handle this is passing as a constant reference using the following code:
int sum(const vector<int> &nums) {
// function body
}
Here, &nums is a reference, and the const
keyword signifies that it is a constant.
Example 3: Passing as a Constant Reference
#include <iostream>
#include <vector>
using namespace std;
// take a const reference as argument
int sum(const vector<int> &nums) {
int total = 0;
for(auto num: nums){
total += num;
}
return total;
}
int main() {
vector<int> nums = {1, 2, 3, 4, 5};
cout << "Sum: " << sum(nums);
return 0;
}
Output
Sum: 15
In the above example, we have passed the nums vector as a constant reference:
int sum(const vector<int> &nums) {
// code
}
Here, instead of creating a local copy of the nums vector, we are passing a reference that cannot be modified. This is faster as well as more space efficient.
Const and Pointers
Like with references, we can also make different types of pointers using the const
keyword. These are:
Pointer Type | Description |
---|---|
Pointers to a Const | The pointer is a non-constant but the value being pointed to is a constant. |
Const Pointer | The pointer is a constant but the value being pointed is a non-constant. |
Const Pointer to a Const | Both the pointer and the value being pointed are constant. |
Pointers to a Const
A pointer to a const is a pointer variable that points to a constant variable. These pointers
- allow us to change the address the pointer is pointing to,
- don't allow us to change the value stored in those constant variables.
For example,
#include <iostream>
using namespace std;
int main() {
// initialize a constant TOTAL_MONTHS
const int TOTAL_MONTHS = 12;
// MONTHS_PTR is a pointer to an int const
const int *MONTHS_PTR = &TOTAL_MONTHS;
cout << "TOTAL_MONTHS: " << TOTAL_MONTHS << endl;
cout << "*MONTHS_PTR: " << *MONTHS_PTR << endl;
// create another int constant
const int HALF_MONTHS = 6;
// MONTHS_PTR now points to HALF_MONTHS
MONTHS_PTR = &HALF_MONTHS;
cout << endl;
cout << "HALF_MONTHS: " << HALF_MONTHS << endl;
cout << "*MONTHS_PTR: " << *MONTHS_PTR;
return 0;
}
Output
TOTAL_MONTHS: 12 *MONTHS_PTR: 12 HALF_MONTHS: 6 *MONTHS_PTR: 6
In the above example, we have created an integer constant TOTAL_MONTHS. Then, we created a pointer to that constant called MONTHS_PTR.
// points to const int TOTAL_MONTHS
const int *MONTHS_PTR = &TOTAL_MONTHS;
We'll get an error if we try to modify the value at MONTHS_PTR.
// error: assignment of read-only location '* TOTAL_MONTHS_PTR'
*TOTAL_MONTHS_PTR = 10;
This is because the value TOTAL_MONTHS is a constant and cannot be modified.
However, we can change the address pointed by MONTHS_PTR to point to some other variable without any error.
const int HALF_MONTHS = 6;
MONTHS_PTR = &HALF_MONTHS;
Const Pointer
A const pointer is a pointer in which we can change the value pointed by the pointer, but cannot change the variable it points to. For example,
#include <iostream>
using namespace std;
int main() {
string country1 = "Nepal";
string country2 = "USA";
cout << "Initially, country1: " << country1 << endl;
// PTR1 is a const pointer to country1
string *const PTR1 = &country1;
// change the value of country1 using PTR1
*PTR1 = country2;
cout << "Finally, country1: " << country1;
return 0;
}
Output
Initially, country1: Nepal Finally, country1: USA
In the above example, we have created a const pointer.
// PTR1 is a const pointer to a string
string *const PTR1 = &country1;
Initially, the value of country1 is Nepal
. Then, we have modified the value of country1 using PTR1:
// change the value of variable pointed by PTR1
*PTR1 = country2;
So the new value of country1 becomes USA
.
But we'll get an error if we try changing the variable pointed by PTR1 from country1 to country2:
// error: assignment of read-only variable 'PTR1'
PTR1 = &country2;
Const Pointer to a Const
A const pointer to a const is a pointer through which we can neither change the variable it points to nor its value. For example,
#include <iostream>
using namespace std;
int main() {
// create a const variable
const string COUNTRY1 = "Nepal";
// create a const pointer to const
const string *const PTR = &COUNTRY1;
cout << *PTR;
return 0;
}
Output
Nepal
Frequently Asked Question
A pointer to const refers to a pointer that points to a value that should not be changed through the pointer.
On the other hand, a const pointer is a pointer that cannot point to a different address after its initialization.
For example,
1. pointer to const:
const int* ptr_to_const = &value;
// Error: Can't modify the value pointed to
*ptr_to_const = 20;
// OK: The pointer can point to another address
ptr_to_const = &another_value;
2. const pointer:
int* const const_ptr = &value;
// OK: The value pointed to can be changed
*const_ptr = 30;
// Error: Can't change the address the pointer is pointing to
const_ptr = &another_value;
Const Expression
Constant expressions were introduced in C++ 11. They are expressions whose:
- value cannot change,
- and can be evaluated at compile time.
The reason we want to use them in C++ is to avoid calculation being done multiple times at runtime.
We use the constexpr
keyword to create constant expressions. For example,
// declare a function as constexpr
constexpr int add_numbers(int a, int b) {...}
// declare a variable as constexpr
constexpr int sum = add_numbers(660, 6);
Note: We can't use constexpr
variables to call functions that are not declared as constexpr
:
Example 4: C++ Const Expression
#include <iostream>
using namespace std;
// a constexpr function that
// returns nth fibonacci number
constexpr int fib(int n) {
// returns n if n <= 1
// else, recursively calls fib(n - 1) + fib(n - 2)
return n <= 1 ? n: fib(n - 1) + fib(n - 2);
}
int main() {
// two constexpr variables that store
// the return values of constexpr function
constexpr int fibonacci_five = fib(5);
constexpr int fibonacci_ten = fib(10);
cout << "fib(5) : "<< fibonacci_five << endl;
cout << "fib(10) : "<< fibonacci_ten;
return 0;
}
Output
fib(5) : 5 fib(10) : 55
In the above example, we have created a constant expression (in the form of a function) to evaluate the nth fibonacci number.
constexpr int fib(int n) {
return n <= 1 ? n: fib(n - 1) + fib(n - 2);
}
Notice that we have added the constexpr
keyword before the return type which tells the compiler that the function to be evaluated at compile time.
Then we calculate the 5th and 10th fibonacci numbers using two constexpr
variables:
constexpr int fibonacci_five = fib(5);
constexpr int fibonacci_ten = fib(10);
Now, notice that we are making the normal function call. The only difference is that the calculation is done at compile time.
// function not declared as constexpr
int fib(int n) {...}
// error: call to non-'constexpr' function 'int fib(int)'
constexpr int fibonacci_five = fib(5);
However, we can use non-constexpr variables to call constexpr
functions:
constexpr int fib(int n) {...}
// valid code
int fibonacci_five = fib(5);
Const Member Functions
Const member functions ensure that the data members of an object are not changed. We create such functions with the following syntax:
return_type function_name() const {
// function body
}
Properties of Const Member Functions
- We cannot change the values of the member variables inside a const member function.
- We can only call const member functions from a constant object.
Example 5: Const Member Functions
#include <iostream>
using namespace std;
class Rectangle {
private:
int breadth, length;
public:
Rectangle(int length, int breadth){
this->breadth = breadth;
this->length = length;
}
// constant member function
int get_area() const {
return length * breadth;
}
// non-constant member function
int get_perimeter() {
return 2 * (length + breadth);
}
};
int main() {
// create a const Rectangle object
const Rectangle RECT = Rectangle(7, 6);
// call the const member function
cout << "Area: " << RECT.get_area() << endl;
return 0;
}
Output
Area: 42
In the above example, we have created a class Rectangle
with the following member functions:
get_area()
- a constant member functionget_perimeter()
- a normal member function
In main()
, we created a constant object named RECT:
// create a const Rectangle object
const Rectangle RECT = Rectangle(7, 6);
Then, we called the const member function get_area()
using the constant object RECT:
// code to call const member function
RECT.get_area();
Calling Non-Constant Member Functions
However, we will get an error if we try to call the non-const member function get_perimeter()
:
// error
RECT.get_perimeter();
This is because a constant object cannot call a non-constant member function.
However, a non-constant object can call both constant and non-constant member functions.
The reasoning behind this is simple: we create a constant object so that the data members are not modified. But non-constant member functions have the capability to modify the data members.
Thus, the purpose of a constant object is at odds with the purpose of a non-constant member function, which is why we can't perform the function call.