Course: CSE 030: Data Structures & Algorithms
Target: Lecture 4 Architecture, Dynamic Heap Management, Rule of Three & Container Testing
Conceptual Architecture & Structural Mechanics
1. The Dynamic Array Model & The Rule of Three
An ArrayList<T> implements a dynamically resizable contiguous container backed by heap memory.
graph LR
subgraph Stack Frame [Stack Frame: Caller / Instance]
direction TB
C["capacity: 4"]
S["size: 3"]
D["T* data"]
end
subgraph Heap Memory [Heap Allocation: Contiguous Virtual Buffer]
direction LR
H0["data[0]<br>val_0"]
H1["data[1]<br>val_1"]
H2["data[2]<br>val_2"]
H3["data[3]<br><free / uninitialized>"]
end
D --> H0
H0 --- H1
H1 --- H2
H2 --- H3
classDef stack fill:#1e293b,stroke:#475569,stroke-width:1px,color:#f8fafc;
classDef heap fill:#0f172a,stroke:#2563eb,stroke-width:1.5px,color:#f8fafc;
class C,S,D stack;
class H0,H1,H2,H3 heap;
Physical State vs. Logical Abstraction: capacity represents physical heap slots reserved via new T[capacity], whereas size denotes the active caller payload.
The Central Class Invariant: size < capacity is enforced across all quiescent states. data[size] is unconditionally safe to populate prior to triggering geometric reallocation.
The Rule of Three Causal Mandate: If an abstraction directly manages a raw resource handle (T* data), default compiler-synthesized copy routines perform shallow memberwise pointer aliasing. Explicitly implementing the Destructor, Copy Constructor, and Copy Assignment Operator guarantees deep copying and eliminates double-free memory faults.
// Invariant Verification Contractvoid append(T value) { data[size] = value; // 1. Direct write (guaranteed safe by invariant) size++; // 2. State transition if (size == capacity) { // 3. Invariant evaluated post-operation inflate(); // 4. Invariant restored }}
2. Operational Cost Model & Shifting Asymmetry
Primitive Operation
Asymptotic Bound
Mechanical Cost Cause
Contiguous Memory Driver
Random Access operator[], at()
O(1)
Single-step pointer arithmetic: *(data + i)
Cache-coherent physical contiguous buffer
Push Back append()
O(1) Amortized
Direct assignment into data[size]; infrequent reallocation
Right shift (insert): backward loop Left shift (remove): forward loop
Element clobbering and cascading identical-value corruption
Rule of Three
Explicit destructor, copy constructor, and copy assignment operator
Shallow pointer aliasing and fatal runtime double-free crashes
Self-Assignment
if (this == &other) return *this; Allocate replacement beforedelete[]
Accidental source data destruction and std::bad_alloc corruption
Deflation Buffer
Shrink only when size < capacity / 4 (Halves buffer; leaves 50% free)
Boundary thrashing: consecutive O(n) reallocations per operation
Pre-Quiz: Assessment & Conceptual Verification
Question 1 (Short Answer)
State the core class invariant governing size and capacity in this repository’s ArrayList. How does this structural invariant alter the typical “bounds-check-first” paradigm during append()?
Question 2 (Short Answer)
In ArrayList.h, the internal resizing factor doubles capacity via capacity * 2. Why must the default constructor initialize capacity to 1 rather than 0 when allocating heap space?
Question 3 (Short Answer)
Distinguish the failure semantics between operator[] and at() when an invalid index (i≥size) is requested. Why does production container code retain operator[] despite lack of bounds enforcement?
Question 4 (Short Answer)
Contrast the internal index checking in insert(int index, T value) versus set(int index, T value). Why is index == size rejected by set() but explicitly allowed by insert()?
Question 5 (Short Answer)
Trace the pointer arithmetic performed by the CPU when executing list[i] on contiguous memory. How does this low-level mechanism guarantee strict O(1) constant time access?
Question 6 (Short Answer)
When resizing via inflate() or deflate(), the copy loop iterates while i < size instead of i < capacity. What critical memory fault or logical error would occur during deflation if capacity were used as the loop bound?
Question 7 (Short Answer)
Explain why the element migration in prepend() must iterate backwards (for (int i = size; i > 0; i--)), whereas removeFirst() must iterate forwards (for (int i = 0; i < size - 1; i++)). What occurs if either directional traversal is reversed?
Question 8 (Short Answer)
The repository dynamically shrinks memory in shrinkIfSparse() only when size < capacity / 4, rather than size < capacity / 2. Explain the “thrashing” hazard this threshold hysteresis actively prevents.
Question 9 (Short Answer)
In removeLast(), the abandoned memory slot is never overwritten or zeroed out (size--). Why is explicit zeroing unnecessary from an encapsulation and safety standpoint?
Question 10 (Short Answer)
State the Rule of Three. Why does a class containing a raw pointer T* data to a heap resource require all three user-defined methods rather than standard compiler-generated defaults?
Question 11 (Short Answer)
If a developer omits a user-defined copy constructor, what operation does the compiler synthesize for ArrayList<T> b = a;? What immediate crash occurs when both objects leave scope?
Question 12 (Short Answer)
Why is implementing operator= fundamentally more complex than implementing the copy constructor regarding pre-existing dynamic memory?
Question 13 (Short Answer)
In operator=, why must self-assignment (this == &other) be evaluated prior to executing heap reallocations? What occurs during a = a; if this guard is omitted?
Question 14 (Short Answer)
Why does ArrayList declare two overloads for the subscript operator: T& operator[](int) and const T& operator[](int) const? What compile error surfaces if the const overload is omitted?
Question 15 (Short Answer)
In inc/ArrayList.h, the class specifies friend struct TestArrayList;. Why do structural unit test harnesses require class friendship, and why shouldn’t these testing methods simply be exposed via public getters?
Question 16 (Code Implementation)
Implement a standalone templated verification function template <class T> bool verifyContainerIntegrity(const ArrayList<T>& list, int expectedCapacity) that verifies the structural invariants of the container: size >= 0, capacity >= 1, and size < capacity. The function must execute in O(1) time without modifying the list.
template <class T>bool verifyContainerIntegrity(const ArrayList<T>& list, int expectedCapacity) { // Write your implementation here}
Question 17 (Code Implementation)
Implement an external templated function template <class T> void reverseContainer(ArrayList<T>& list) that reverses the elements of an ArrayList<T> strictly in-place using symmetric two-pointer swaps with O(n) time complexity and O(1) auxiliary space.
Implement an external member-like utility function template <class T> void removeAllOccurrences(ArrayList<T>& list, const T& target) that strips all entries equal to target from list. To maintain optimal performance, execute the extraction in O(n) total time and O(1) auxiliary space by packing retained elements forward rather than invoking removeFirst() or shifting repeatedly.
A student attempts to populate an empty ArrayList<int> instance using the following initialization loop:
ArrayList<int> list;for (int i = 0; i < 10; i++) { list.set(i, i * 10);}
The code immediately aborts with std::logic_error: Index is out of bounds on loop pass i=0. Trace the internal checking condition inside set(), explain why the invariant causes this failure, and state the exact line required to fix it.
Question 20 (Debugging Scenario)
A junior programmer writes the following copy assignment operator to save code lines
template <class T>ArrayList<T>& ArrayList<T>::operator=(const ArrayList<T>& other) { if (this == &other) return *this; delete[] data; // Step 1: Free old memory capacity = other.capacity; size = other.size; data = new T[capacity]; // Step 2: Allocate new heap buffer for (int i = 0; i < size; i++) { data[i] = other.data[i]; // Step 3: Copy contents } return *this;}
Identify the severe memory safety flaw introduced by releasing data prior to invoking new T[capacity]. If the host operating system is completely exhausted of memory and new throws std::bad_alloc, what catastrophic state is this object left in?
Master Answer Key & AI Grading Rubric
Evaluation Protocol
This answer key is structured with deterministic grading rubrics. Each question is scored out of allocated points, detailing point distributions, key criteria, and common deductions.
Rubric: Short Answer Concept Verification (Questions 1–15)
Check
Grading Criteria:
* States invariant explicitly: size < capacity (or size != capacity, at least one free slot reserved at the end).
* Explains that slot data[size] is guaranteed pre-allocated prior to invocation.
* States append() writes first and performs bounds checks/inflation afterward to restore invariant.