Source: Lecture slides, lab_intro, lab_debug, lab_memory, mp_intro
Tags: C++ classes, constructors, copy constructor, namespaces, stack memory, heap memory, pass by value, pass by reference, pass by pointer, UIUC CS225, data structures
Difficulty: Intermediate | Prerequisites: Basic programming experience in any language. Familiarity with variables, functions, and control flow.
C++ is the implementation language for CS 225. The first three weeks build the foundation you will use for every data structure in the course: how classes encapsulate data, how memory is laid out on the stack and the heap, and how data moves (or does not move) when you call a function. These are not isolated topics. Exam questions routinely combine class mechanics with memory layout and parameter passing into a single code-reading problem, so treat them as one interconnected system rather than three separate chapters.
C++ classes bundle data and behaviour together; constructors control how objects are born and copied. The stack holds local variables that disappear when a function returns, while the heap holds dynamically allocated memory that persists until you explicitly free it. Whether a function receives a copy, a reference, or a pointer determines whether the original data can be changed by the callee.
Class
A user-defined type that groups data (member variables) and operations (member functions) into a single unit. Access control (public vs. private) decides what code outside the class can touch.
In simple terms, a class is a blueprint: it describes what data an object holds and what you can do with it.
Public member function
A function declared in the public section of a class. Any code that has access to an object can call its public member functions.
Think of it as the buttons on the outside of a machine: the interface other code uses.
Private helper function
A function declared in the private section. Only other member functions of the same class can call it.
In simple terms, it is internal plumbing that the outside world never needs to see.
Private variable (member variable)
A variable declared in the private section of a class. Only the class's own member functions can read or write it directly.
Think of it as data stored inside a locked box; the class's public functions are the only keys.
Constructor
A special member function called automatically when an object is created. It initialises the object's member variables.
In simple terms, the constructor is the setup routine that runs the moment a new object comes into existence.
Automatic default constructor
The compiler-generated constructor that takes no arguments. It is provided only when you have written no constructors at all.
Think of it as the compiler saying, "You didn't tell me how to build this, so I'll use the bare minimum."
Custom constructor (default and non-default)
A constructor you write yourself. A custom default constructor takes no arguments (or all arguments have defaults). A non-default constructor requires at least one argument with no default.
In simple terms, you are giving explicit instructions for how an object should be set up.
Copy constructor
A constructor that creates a new object as a copy of an existing object of the same type. Its signature is ClassName(const ClassName & other).
Think of it as a photocopy machine for objects.
Automatic copy constructor
The compiler-generated copy constructor. It performs a shallow, member-by-member copy of every field.
In simple terms, the compiler copies each member variable one-for-one, which is fine for simple types but dangerous when pointers are involved, because two objects end up pointing to the same heap memory.
Custom copy constructor
A copy constructor you write yourself, typically to perform a deep copy so each object owns its own independent copy of any dynamically allocated data.
Think of it as making a true duplicate rather than just copying the address of shared storage.
Namespace
A named scope that groups identifiers to prevent naming collisions. using namespace std; brings all standard-library names into the current scope.
In simple terms, namespaces are like surnames: two people can both be called "sort" as long as they live in different namespaces.
Stack memory
The region of memory that stores local variables and function call frames. Allocation and deallocation are automatic: variables are created when a function is called and destroyed when it returns.
Think of it as a stack of trays in a cafeteria, last in, first out.
Heap memory
The region of memory used for dynamic allocation via new. Memory on the heap persists until you explicitly call delete.
In simple terms, the heap is a warehouse: you rent space when you need it and must return it yourself, or it leaks.
Pass by value
The function receives a copy of the argument. Changes inside the function do not affect the caller's variable.
Think of it as handing someone a photocopy of a document. They can scribble on it, but your original stays clean.
Pass by reference
The function receives an alias (reference) to the caller's variable. Changes inside the function directly modify the original.
In simple terms, you hand someone the actual document. Whatever they write on it, you see.
Pass by pointer
The function receives a pointer, the memory address of the caller's variable. The function can modify the original by dereferencing the pointer, and can also be passed nullptr.
Think of it as giving someone the street address of your house. They can go there and rearrange the furniture, or they might have been given a null address (no house at all), which your code must handle.
A class declaration typically lives in a .h header file; definitions go in a .cpp file.
The public section exposes the interface. The private section hides implementation details.
Member functions can access all private data of their class directly.
If you define any constructor, the compiler no longer provides an automatic default constructor. This is a common exam trap: code that previously compiled may break when you add a parameterised constructor without also adding a default one.
No constructors written at all: the compiler provides an automatic default constructor and an automatic copy constructor.
Any constructor written: the automatic default constructor disappears. The automatic copy constructor remains unless you write your own.
Custom copy constructor written: the compiler's shallow copy is replaced entirely by your version.
Initialiser lists (: member(value)) are preferred over assignment inside the constructor body, because they initialise directly rather than default-constructing then overwriting.
std::cout is the fully qualified name; using namespace std; lets you write cout directly.
Exam questions may test whether code compiles when using namespace std; is missing.
Defining your own namespace prevents collisions when two libraries export the same function name.
A local variable exists only within the block {} where it is declared.
A variable declared inside a loop body is created and destroyed on every iteration.
Static local variables retain their value across function calls.
Stack: fast allocation, automatically managed, limited size. Every function call pushes a new frame; returning pops it.
Heap: slower allocation via new, manually managed via delete, much larger capacity.
Forgetting delete causes a memory leak. Calling delete twice on the same pointer is undefined behaviour.
A dangling pointer is a pointer that still holds the address of memory that has already been freed.
By value void f(int x) – safe, no side effects, but copies can be expensive for large objects.
By reference void f(int & x) – efficient, allows modification of the original. Use const int & x when you want efficiency without modification.
By pointer void f(int * x) – similar to by reference, but the pointer itself can be null and can be reassigned to point elsewhere.
Exam code-reading questions frequently ask: "After calling f(a), what is the value of a?" The answer depends entirely on whether f takes its parameter by value, by reference, or by pointer.
No formulas per se, but draw stack and heap diagrams for every code-tracing question. A quick sketch showing which variables live on the stack, which point to heap objects, and which frames exist at each point in execution will prevent most errors.
Memory management in C++ maps directly to how operating systems allocate resources. Understanding the stack and the heap is how you reason about program crashes (stack overflow), memory leaks in long-running servers, and why languages like Java and Python introduced garbage collection: to avoid the manual bookkeeping that C++ demands.
"The automatic copy constructor always works correctly." It does not. When a class has pointer members, the automatic copy constructor copies the pointer value (the address), not the data it points to. Two objects then share one block of heap memory, and deleting one corrupts the other.
"Adding a constructor with parameters still leaves the default constructor available." It does not. Once you define any constructor, the compiler-provided default constructor is gone. You must write one explicitly if you still need it.
"Pass by reference and pass by pointer are the same thing." They are similar in effect but differ in syntax, nullability, and reassignability. A reference cannot be null and cannot be reseated; a pointer can be both.
"Stack memory is slower than heap memory." The opposite. Stack allocation is extremely fast (just moving a pointer), while heap allocation involves more overhead.
⚠️ Code-reading questions that combine constructors, memory, and parameter passing in a single snippet are the most common exam pattern. Trace them line by line.
⚠️ Know exactly when the compiler provides a default constructor and when it does not.
⚠️ Shallow copy vs. deep copy is a perennial exam topic. If a class has a pointer member, expect a question about what happens with the automatic copy constructor.
⚠️ Distinguishing pass by value, pass by reference, and pass by pointer in code output questions is nearly guaranteed.
True or false: If you write a constructor that takes two int parameters, you can still create an object with no arguments using the compiler-provided default constructor.
Fill in the blank: Memory allocated with new lives on the ________ and must be freed with ________.
True or false: A reference parameter can be nullptr.
True or false: The automatic copy constructor performs a deep copy of pointer members.
Fill in the blank: using namespace std; allows you to write cout instead of ________.
(Answers: 1. False. 2. heap, delete. 3. False. 4. False. 5. std::cout.)
Q: You have a class Cube with a private int * size_ member. You do not write a custom copy constructor. What happens when you copy a Cube object and then delete the original?
A: The automatic copy constructor copies the pointer value. Both the original and the copy point to the same heap-allocated integer. Deleting the original frees that memory, leaving the copy with a dangling pointer. Accessing it is undefined behaviour.
Q: Given void f(int x) { x = 10; } and int a = 5; f(a);, what is the value of a after the call?
A: a is still 5. The function receives a copy (pass by value), so the assignment inside f does not affect the caller's variable.
Q: Given void g(int & x) { x = 10; } and int a = 5; g(a);, what is the value of a after the call?
A: a is 10. The function receives a reference, so the assignment modifies the caller's variable directly.
Q: Why might you prefer a const reference parameter over pass by value for a large object?
A: A const reference avoids the cost of copying the entire object while still preventing the function from modifying the original. You get efficiency and safety together.
Q: A class defines a custom constructor Cube(int size). A line of code reads Cube c;. Does this compile?
A: No. Defining Cube(int size) removes the compiler-provided default constructor. Cube c; requires a default constructor, which no longer exists. You would need to add Cube() = default; or Cube() {} explicitly.
This material connects directly to the data structures portion of the course (Weeks 4–5). Every linked list node is heap-allocated, so understanding new, delete, and pointer management is essential for implementing and debugging list operations. The copy constructor topic reappears when you implement deep copies of entire data structures.
C++ classes, constructors, default constructor, copy constructor, deep copy, shallow copy, namespaces, stack memory, heap memory, dynamic memory allocation, new, delete, dangling pointer, memory leak, pass by value, pass by reference, pass by pointer, const reference, UIUC CS225, data structures exam 1