Introduction
In C++, const
keyword is short for constant. A constant variable/object doesn’t change/mutate during program runtime. Constantness also means read-only access to a variable/object.
You can simply avoid using constant in your C++ program and make money with no problem at all. For example, the Python language is very viable and doesn’t support constant variables. However, if you use const
in C++ you get the below advantages:
- Better performance: compiler injects nitro in your code engine.
- Avoiding bugs: You set a rule,
A
is constant, if mistakenly you try to changeA
, the compiler empties an ice bucket on you. - Easy to read and use API: you create a library that accepts an object like an iPhone 12, the user of the library will be happy if it is received as
const
rather than being tested for 100 meters drop test.
Primitive types
Basic types can be defined constant, which means don’t you dare change them :
const int i=1;
i=2; // Error: Hey YOU, stop changing i
const double j=1.1;
double k = j+1; // Great, just read j
const std::string s="Cheer up!";
std::cout<< s; // Good boy :), just read s
s = "HA HA"; // 3 pages of Error: don't mess with s, pal!
Objects of class
A constant object doesn’t change during runtime:
class Car{
public:
string color;
Car(string c):color(c){};
};
const Car myCar{"blue"};
string color = myCar.color; // OK: I tell you my car's color
myCar.color = "white"; // Error: don't chage my car's specs
So, the constructor is the only way to set your const
object, myCar
.
Const is contagious
A const
member function doesn’t change the object of a class. So, we have this rule you can only call a const
member function of a const
object.
So, if I say my car is const
, I cannot do something to change it:
class Car{
string driverSeat;
public:
Car(string ds):driverSeat(ds){};
void AdjustDriverSeat(string ds){
driverSeat=ds;}
};
int main(){
const Car myCar{"Seat is set for Sorush"};
myCar.AdjustDriverSeat("Move it forward"); //Error: don't change the car
}
However, I can do things that do not change the state of my car i.e. I can call const
member functions:
class Car{
string driverSeat;
public:
Car(string ds):driverSeat(ds){};
void PrintDriverSeat() const{ // note const !!!!
std::cout<<driverSeat;
}
void Drive() const {/* drive my car but don't crash it */}
};
int main(){
const Car myCar{"Seat is set for Sorush"};
myCar.PrintDriverSeat(); // you can look at my car
myCar.Drive(); // Drive Safe :)
}
Function Argument
Sometimes we want to pass an object to a function and say “hey function, read it but be nice and do not change it”. You have some ways as follows.
Pass by const reference
I lend my Bose headphones to my friend, “Rose”:
class Headphones{
public:
void SmashIt(){}
void Skype() const {} // note const!!!
};
void RoseUse(const Headphones& h){
h.Skype(); // amazing girl
h.SmashIt(); // Error: Not a good pal :(
}
int main(){
Headphones myHeadphones;
RoseUse(myHeadphones);
}
with const &
I am sure Rose can not smash my headphones.
Pass by value
We know when we pass to a function by value, a copy constructor is called and a new variable/object is created.
So, I give my headphones to Rose, she will buy the same Bose headphones. She does whatever with her new headphones, she may dip it in a cup of tea like a teabag, who cares! it’s not my headphones:
class Headphones{
public:
void BreakIt(){}
void DipInTea(){}
void Skype(){}
};
void RoseUse(Headphones hers){
hers.Skype();
hers.BreakIt();
hers.DipInTea();
}
int main(){
Headphones myHeadphones;
RoseUse(myHeadphones);
// my heaphone is not touched
}
Just note that in this case if you defined void RoseUse(const Headphones hers)
it will just limit the clone, hers
, and wouldn’t add value to the purpose: my headphones mustn’t change.
Pass by const pointee
You can also pass the address of the object, but emphasise that it is read-only with const
.
Therefore, I tell Rose the address of my headphone: “on my desk, please don’t compromise our friendship, bring it back as you received it”.
class Headphones{
public:
void SmashIt(){}
void Skype() const{} // note const!!!
};
void RoseUse(const Headphones* HeadphonesAddress){
HeadphonesAddress->Skype(); // OK, my BFF
HeadphonesAddress->SmashIt(); // Error: Noooo :(
}
int main(){
Headphones myHeadphones;
RoseUse(&myHeadphones);
// my heaphone is not touched
}
Return member
You can return an alias to a member via const
function but the return must be const
too. I told you const is contagious.
class Car{
string color;
public:
Car(string c):color(c){};
const string& GetColor () const {
return color;
}
};
If you drop const
from const string& GetColor()
, you get an error.
So, if I give my car as const
to Steven, he can see it and enjoy the view but cannot spray paint its body:
void StevenCheck(const Car& car){
std::cout<<car.GetColor(); // OK :)
car.GetColor()="red";// Error: don't change my car
}
int main(){
Car myCar{"blue"};
StevenCheck(myCar);
}
const overload
You can overload a member function just by const
:
class Car{
string color;
public:
Car(string c):color(c){};
const string& GetColor () const {
return color;
}
string& GetColor () {
return color;
}
};
int main(){
Car jackCar{"white"};
jackCar.GetColor() = "red"; // non-const GetColor() used
const Car roseCar{"blue"};
// const GetColor() is called
std::cout<<roseCar.GetColor(); // OK: read color
roseCar.GetColor()="red"; // Error: Don't change it
}
Member pointer
A pointer member of a constant object will be constant but not its target i.e. it will be shallow constant. Can you guess the output of the below example?
#include <memory>
#include <vector>
#include <iostream>
struct A{
void print() const { std::cout<<"const\n";}
void print() {std::cout<<"non const\n";}
};
struct B {
B() {
p=new A{};
up=std::make_unique<A>();
v.push_back(A{});
}
A* p;
std::unique_ptr<A> up;
A a;
std::vector<A> v;
};
int main() {
B b;
b.p->print();
const B& bref=b;
bref.a.print();
bref.v[0].print();
bref.p->print();
bref.up->print();
//Uncomment below, you get error of changing constant pointer.
//bref.p=nullptr;
}
// Output:
// b.p->print(); non const
// bref.a.print(); const
// bref.v[0].print(); const
// bref.p->print(); non const
// bref.up->print(); non const
For bref
, the raw pointer p
and unique pointer up
become constants but their pointees are not constant. For the STL container member, v
, the constantness of bref
is kept to the level of accessing v
elements.
Latest Posts
- A C++ MPI code for 2D unstructured halo exchange
- Essential bash customizations: prompt, ls, aliases, and history date
- Script to copy a directory path in memory in Bash terminal
- What is the difference between .bashrc, .bash_profile, and .profile?
- A C++ MPI code for 2D arbitrary-thickness halo exchange with sparse blocks