Programming Language & Its Applications β C++ Constructs with Objects and Classes, NEC licence examination syllabus (Nepal Engineering Council).
A compile-time promise not to modify β and the reason it must spread through your whole class.
const std::string&, you know without reading the body that your string is safe β and the compiler enforces it. This matters most in multithreaded code: a genuinely const object can be read by many threads simultaneously with no lock at all, which is why Rust built its entire concurrency safety on the same distinction (&T versus &mut T) and made it unbreakable. Search "const correctness why it matters" and then "Rust shared mutable state" β you will see the same idea taken to its conclusion.const appears#include <iostream>
class Rectangle {
double w, h;
public:
Rectangle(double a, double b) : w(a), h(b) {}
/* read-only: mark const */
double area() const { return w * h; }
double perimeter() const { return 2 * (w + h); }
double width() const { return w; }
/* modifies: must NOT be const */
void scale(double f) { w *= f; h *= f; }
/* a const function CANNOT modify members */
/* double bad() const { w = 0; return w; }
error: assignment of member 'Rectangle::w' in
read-only object */
};
/* const& parameter: the caller's object is guaranteed safe */
void report(const Rectangle &r) {
std::cout << " area=" << r.area()
<< " perim=" << r.perimeter() << "\n";
/* r.scale(2); error: scale() is not const */
}
int main() {
Rectangle a(4, 5);
report(a);
a.scale(2); /* fine: a is not const */
report(a);
const Rectangle b(3, 3);
report(b); /* only const members used */
std::cout << " b.width() = " << b.width() << "\n";
/* b.scale(2); error: b is const */
return 0;
}
Output:
area=20 perim=18
area=80 perim=36
area=9 perim=12
b.width() = 3
#include <iostream>
#include <stdexcept>
class Array {
int data[5];
int n = 5;
public:
Array() { for (int i = 0; i < 5; i++) data[i] = (i+1)*10; }
/* TWO overloads differing ONLY in constness.
This IS a valid overload β the hidden this parameter
has a different type. */
int& at(int i) { /* non-const version */
std::cout << " [non-const at]";
if (i < 0 || i >= n) throw std::out_of_range("bad");
return data[i]; /* WRITABLE */
}
const int& at(int i) const { /* const version */
std::cout << " [const at]";
if (i < 0 || i >= n) throw std::out_of_range("bad");
return data[i]; /* READ-ONLY */
}
void show() const {
for (int i = 0; i < n; i++) std::cout << data[i] << " ";
std::cout << "\n";
}
};
int main() {
Array a;
const Array b;
std::cout << "a.at(2) reading:";
std::cout << " " << a.at(2) << "\n";
std::cout << "a.at(2) writing:";
a.at(2) = 999; /* assign THROUGH the call */
std::cout << "\n";
a.show();
std::cout << "b.at(2) reading:";
std::cout << " " << b.at(2) << "\n";
/* b.at(2) = 5; error: assignment of read-only
location β the const overload
returns const int& */
return 0;
}
Output:
a.at(2) reading: [non-const at] 30
a.at(2) writing: [non-const at]
10 20 999 40 50
b.at(2) reading: [const at] 30
int f() and double f() are illegal overloads while int f() and int f() const are legal β the second pair genuinely differ in their parameter list, once you remember this is a parameter.
#include <iostream>
#include <cmath>
class Polygon {
double sides[4];
int n = 4;
/* mutable members MAY be modified in const functions.
Used for things that are not part of the object's
logical value: caches, counters, mutexes. */
mutable bool cacheValid = false;
mutable double cachedPerimeter = 0.0;
mutable int computeCount = 0;
public:
Polygon(double a, double b, double c, double d)
: sides{a,b,c,d} {}
double perimeter() const {
if (!cacheValid) {
++computeCount;
cachedPerimeter = 0;
for (int i = 0; i < n; i++) cachedPerimeter += sides[i];
cacheValid = true;
std::cout << " (computed)";
} else {
std::cout << " (cached) ";
}
return cachedPerimeter;
}
void setSide(int i, double v) {
sides[i] = v;
cacheValid = false; /* invalidate */
}
int computations() const { return computeCount; }
};
int main() {
const Polygon p(3, 4, 5, 6);
std::cout << " perim=" << p.perimeter() << "\n";
std::cout << " perim=" << p.perimeter() << "\n";
std::cout << " perim=" << p.perimeter() << "\n";
std::cout << "computed only " << p.computations()
<< " time(s) for 3 calls\n";
return 0;
}
Output:
(computed) perim=18
(cached) perim=18
(cached) perim=18
computed only 1 time(s) for 3 calls
constexpr, which does not merely promise not to modify β it demands the function be evaluable at compile time. A constexpr factorial produces a literal in the binary with no runtime work at all, and C++20's consteval makes that mandatory rather than optional. Search "constexpr vs const C++" β they look similar and do completely different jobs, and the difference is a common interview question.const T* const this, which is why it cannot modify members. Const overloading (the same function name with and without const) is a favourite question β use operator[] as the example. Explain mutable as logical versus physical constness with a cache. Also be ready to read the four pointer-const forms right to left.Create a free account to tick topics off, take notes as you read, watch the video lessons and get a day-by-day study plan built around your exam date.
Loadingβ¦