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 cubeand 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.