0 / 13
00:00

Exercise 2: Dynamic Memory

Heap tracing, bug hunting, and smart pointers — ~30–35 min

Heap Tracing

For each snippet, fill in the state of the heap and stack pointers after each line executes.

A1 Alias & delete
1int* p = new int(10);
2int* q = p;
3*q = 20;
4delete p;
After lineHeapStack pointers
Line 1
Line 2
Line 3
Line 4
A2 Copy value & nullptr
1int* a = new int(5);
2int* b = new int(10);
3*a = *b + 3;
4delete b;
5b = nullptr;
After lineHeapStack pointers
Line 1
Line 2
Line 3
Line 4
Line 5
A3 Array & pointer arithmetic
1int* arr = new int[3];
2arr[0] = 10; arr[1] = 20; arr[2] = 30;
3int* p = arr + 1;
4cout << *p;    // prints 20
5delete[] arr;
After lineHeapStack pointers
Line 1
Line 2
Line 3
Line 5
A4 Pointer to pointer
1int** pp = new int*(new int(42));
2cout << **pp;  // prints 42
3delete *pp;
4delete pp;
After lineHeapStack pointers
Line 1
Line 3
Line 4

Memory Bug Hunt

Each snippet has a memory bug. Identify the bug type from the dropdown, then write the corrected code.

B1 What goes wrong?
void process(int n) {
    int* data = new int[n];
    if (n <= 0) return;
    // ... use data ...
    delete[] data;
}
The early return exits before delete[] runs. What happens to the allocated memory?
B2 What goes wrong?
int* createValue() {
    int val = 42;
    return &val;
}
int* p = createValue();
cout << *p;
val is a local variable on the stack. What happens to it when the function returns?
B3 What goes wrong?
int* a = new int(10);
int* b = a;
delete a;
delete b;  // boom!
a and b point to the same address. What happens when you delete both?
B4 What goes wrong?
int* arr = new int[100];
// ... fill array ...
delete arr;  // wrong!
Allocated with new[] but freed with delete (no brackets). What should it be?

Build a Dynamic Array Class

Fill in the three missing methods. The class manages a heap-allocated array that grows when full.

Class skeleton — you will implement the constructor, destructor, and push_back.
class DynArray {
    int* data;
    int size;
    int capacity;
public:
    DynArray(int cap);      // Task 1
    ~DynArray();             // Task 2
    void push_back(int val); // Task 3
    int get(int index) { return data[index]; }
};
C1 Constructor

Implement DynArray(int cap): allocate data on the heap, initialize size and capacity.

Use new int[cap] to allocate, set size = 0, and capacity = cap.
C2 Destructor

Implement ~DynArray(): free the heap memory.

data was allocated with new[], so you need delete[].
C3 push_back (with resize)

Implement push_back(int val): if the array is full, double the capacity (allocate new array, copy elements, delete old). Then append val.

Steps when full: (1) capacity *= 2, (2) int* newData = new int[capacity], (3) loop to copy old elements, (4) delete[] data, (5) data = newData. Then always: data[size++] = val.

Rewrite with Smart Pointers

Convert each snippet from raw pointers to smart pointers. Remove manual delete.

D1 unique_ptr

Original:

void process() {
    int* p = new int(42);
    cout << *p;
    delete p;
}

Rewrite using unique_ptr:

Use auto p = make_unique<int>(42); — no delete needed.
D2 shared_ptr

Original:

Widget* w = new Widget();
componentA->use(w);
componentB->use(w);
// who deletes?
delete w;

Rewrite using shared_ptr:

Use auto w = make_shared<Widget>(); — pass w to both components. No delete needed; last owner frees it.

Final Score

0 / 13