C++ - Variables & Constants
Overview
Estimated time: 40–60 minutes
Understand initialization styles, const and constexpr, references vs pointers, and const-correctness—the building blocks for robust C++ code.
Learning Objectives
- Use direct, copy, and list initialization appropriately.
- Declare constants with const and constexpr; know when to use each.
- Distinguish references and pointers; apply const-correctness to both.
Prerequisites
Initialization styles
#include <iostream>
int main(){
int a = 42; // copy init
int b(42); // direct init
int c{42}; // list init (guards against narrowing)
double d{3.14}; // list init
std::cout << a+b+c+d << "\n";
}
Expected Output (example): 129.14
const and constexpr
#include <iostream>
constexpr int square(int x){ return x*x; }
int main(){
const int N = 10; // runtime constant (cannot modify)
constexpr int M = square(4); // compile-time constant if args are constexpr
std::cout << N << " " << M << "\n";
}
Expected Output: 10 16
References vs pointers and const-correctness
#include <iostream>
int main(){
int x = 5;
int& ref = x; // reference (alias), must bind at init, non-null
int* ptr = &x; // pointer, can be null, can re-point
const int cx = 7; // const object
const int& cref = x; // reference to const (doesn't allow modification via cref)
std::cout << ref << " " << *ptr << " " << cx << "\n";
}
Expected Output: 5 5 7
Pointer constness and nullptr
#include <iostream>
int main(){
int v = 10;
const int* p1 = &v; // pointer to const int (can't modify *p1)
int* const p2 = &v; // const pointer to int (can't change p2, can modify *p2)
int const* const p3 = &v; // const pointer to const int
int* pn = nullptr; // null pointer
std::cout << (pn == nullptr ? "null" : "nonnull") << "\n";
}
Scope, lifetime, and dangling references
#include <string>
#include <iostream>
const std::string& bad(){
std::string s = "temp"; // destroyed at function end
return s; // dangling reference! undefined behavior if used
}
int main(){
// const std::string& r = bad(); // don't do this
std::string safe = []{ std::string s = "ok"; return s; }(); // returns by value
std::cout << safe << "\n";
}
Common Pitfalls
- Narrowing conversions with list initialization:
int x{3.14}
is an error, which is good—avoid loss of data. - Forgetting const on member functions that don’t modify state; this breaks usage with const objects.
Checks for Understanding
- When should you prefer list initialization?
- What’s the difference between const and constexpr?
Show answers
- When you want to avoid narrowing and write uniform initialization.
- const is runtime immutability; constexpr requires compile-time evaluability.
Exercises
- Write a constexpr function
cube
and use it to initialize a constexpr variable. - Create a function that takes a
const std::string&
and prints it; try passing a temporary and a named variable.