CodeNebula
C++98corebeginner

Dynamic Memory Management

C++98 · The Foundation · Compiler flag: -std=c++98

Explanation

Allocates and deallocates memory at runtime using `new` and `delete`.

Real-World Scenario

Allocate memory for a dynamic list of user inputs.

Quick Snippet

snippet.cpp
int* ptr = new int; delete ptr;

Full Example

dynamic_memory.cpp
// CodeNebula - C++98 Example: Dynamic Memory Management
// Compile: g++ -std=c++98 -Wall -Wextra -o example dynamic_memory.cpp

// Dynamic memory in C++98 uses new/delete for single objects and
// new[]/delete[] for arrays. Unlike stack allocation, dynamic memory
// persists until explicitly freed. This example demonstrates allocation
// patterns, RAII, and common pitfalls.

#include <iostream>
#include <string>
#include <cstring>

// ------------------------------------------------------------------
// A class that tracks its construction/destruction
// ------------------------------------------------------------------
class Resource {
private:
    std::string name;
    int id;
    static int nextId;

public:
    Resource(const std::string& n) : name(n), id(nextId++) {
        std::cout << "  [Resource #" << id << " \"" << name << "\" constructed]" << std::endl;
    }

    ~Resource() {
        std::cout << "  [Resource #" << id << " \"" << name << "\" destroyed]" << std::endl;
    }

    void use() const {
        std::cout << "  Using Resource #" << id << " \"" << name << "\"" << std::endl;
    }
};

int Resource::nextId = 1;

// ------------------------------------------------------------------
// Simple RAII wrapper (C++98 style smart pointer)
// Demonstrates the principle behind std::auto_ptr
// ------------------------------------------------------------------
class IntPtr {
private:
    int* ptr;

    // Prevent copying (C++98 style: declare private, don't define)
    IntPtr(const IntPtr&);
    IntPtr& operator=(const IntPtr&);

public:
    explicit IntPtr(int* p = 0) : ptr(p) {}

    ~IntPtr() {
        std::cout << "  IntPtr destructor: deleting pointer" << std::endl;
        delete ptr;
    }

    int& operator*() const { return *ptr; }
    int* get() const { return ptr; }
};

// ------------------------------------------------------------------
// Dynamic string buffer class (manages char array)
// ------------------------------------------------------------------
class DynString {
private:
    char* data;
    int length;

public:
    DynString(const char* str) {
        length = std::strlen(str);
        data = new char[length + 1];
        std::strcpy(data, str);
        std::cout << "  DynString allocated " << (length + 1) << " bytes" << std::endl;
    }

    // Copy constructor (deep copy)
    DynString(const DynString& other) {
        length = other.length;
        data = new char[length + 1];
        std::strcpy(data, other.data);
        std::cout << "  DynString copy: allocated " << (length + 1) << " bytes" << std::endl;
    }

    // Assignment operator (deep copy)
    DynString& operator=(const DynString& other) {
        if (this != &other) {
            delete[] data;  // Free old memory first
            length = other.length;
            data = new char[length + 1];
            std::strcpy(data, other.data);
            std::cout << "  DynString assign: reallocated " << (length + 1) << " bytes" << std::endl;
        }
        return *this;
    }

    ~DynString() {
        std::cout << "  DynString freed " << (length + 1) << " bytes" << std::endl;
        delete[] data;
    }

    void print() const {
        std::cout << "  \"" << data << "\" (len=" << length << ")" << std::endl;
    }
};

int main() {
    std::cout << "=== C++98 Dynamic Memory ===" << std::endl;

    // --- 1. new / delete for single objects ---
    std::cout << "\n--- 1. new/delete (single object) ---" << std::endl;
    int* pi = new int(42);
    std::cout << "Allocated int: " << *pi << std::endl;
    // Expected: Allocated int: 42

    double* pd = new double(3.14);
    std::cout << "Allocated double: " << *pd << std::endl;
    // Expected: Allocated double: 3.14

    delete pi;
    delete pd;
    std::cout << "Single objects deleted." << std::endl;

    // Set pointers to NULL after delete (good practice in C++98)
    pi = 0;
    pd = 0;

    // --- 2. new[] / delete[] for arrays ---
    std::cout << "\n--- 2. new[]/delete[] (arrays) ---" << std::endl;
    int size = 5;
    int* arr = new int[size];

    // Initialize the array
    for (int i = 0; i < size; ++i) {
        arr[i] = (i + 1) * 10;
    }

    // Print the array
    std::cout << "Dynamic array: ";
    for (int i = 0; i < size; ++i) {
        std::cout << arr[i];
        if (i < size - 1) std::cout << ", ";
    }
    std::cout << std::endl;
    // Expected: Dynamic array: 10, 20, 30, 40, 50

    delete[] arr;  // MUST use delete[] for arrays, not delete
    arr = 0;
    std::cout << "Array deleted." << std::endl;

    // --- 3. Dynamic objects with constructors/destructors ---
    std::cout << "\n--- 3. Dynamic objects ---" << std::endl;
    Resource* r1 = new Resource("Database");
    Resource* r2 = new Resource("Network");

    r1->use();
    r2->use();

    delete r1;  // Destructor called here
    delete r2;

    // --- 4. Array of objects ---
    std::cout << "\n--- 4. Array of objects ---" << std::endl;
    {
        // new[] calls default constructor for each element
        // (Resource needs a default constructor for this, so we use pointers)
        const int N = 3;
        Resource* resources[N];
        resources[0] = new Resource("File");
        resources[1] = new Resource("Socket");
        resources[2] = new Resource("Timer");

        for (int i = 0; i < N; ++i) {
            resources[i]->use();
        }

        // Must delete each one
        for (int i = 0; i < N; ++i) {
            delete resources[i];
        }
    }

    // --- 5. RAII pattern ---
    std::cout << "\n--- 5. RAII wrapper ---" << std::endl;
    {
        IntPtr smart(new int(99));
        std::cout << "Smart pointer holds: " << *smart << std::endl;
        // Expected: Smart pointer holds: 99

        // No need to call delete -- IntPtr destructor handles it
    }
    // Expected: IntPtr destructor: deleting pointer
    std::cout << "After RAII scope: memory automatically freed." << std::endl;

    // --- 6. Deep copy with dynamic memory ---
    std::cout << "\n--- 6. Deep copy (DynString) ---" << std::endl;
    {
        DynString s1("Hello, C++98!");
        s1.print();

        // Copy constructor
        DynString s2(s1);
        s2.print();

        // Assignment operator
        DynString s3("temporary");
        s3 = s1;
        s3.print();
    }
    // All DynStrings destroyed here, each freeing its own memory

    // --- 7. Checking for NULL before delete (safe) ---
    std::cout << "\n--- 7. Safe delete patterns ---" << std::endl;
    int* maybe = 0;
    delete maybe;  // Deleting NULL/0 is safe and does nothing
    std::cout << "Deleting null pointer is safe (no-op)." << std::endl;

    // --- 8. 2D dynamic array ---
    std::cout << "\n--- 8. 2D dynamic array ---" << std::endl;
    int rows = 3, cols = 4;

    // Allocate array of row pointers
    int** matrix = new int*[rows];
    for (int i = 0; i < rows; ++i) {
        matrix[i] = new int[cols];
    }

    // Fill with values
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            matrix[i][j] = i * cols + j + 1;
        }
    }

    // Print
    std::cout << "2D array:" << std::endl;
    for (int i = 0; i < rows; ++i) {
        std::cout << "  ";
        for (int j = 0; j < cols; ++j) {
            std::cout << matrix[i][j];
            if (j < cols - 1) std::cout << "\t";
        }
        std::cout << std::endl;
    }

    // Free in reverse order
    for (int i = 0; i < rows; ++i) {
        delete[] matrix[i];
    }
    delete[] matrix;
    std::cout << "2D array freed." << std::endl;

    return 0;
}

/*
Expected Output:
=== C++98 Dynamic Memory ===

--- 1. new/delete (single object) ---
Allocated int: 42
Allocated double: 3.14
Single objects deleted.

--- 2. new[]/delete[] (arrays) ---
Dynamic array: 10, 20, 30, 40, 50
Array deleted.

--- 3. Dynamic objects ---
  [Resource #1 "Database" constructed]
  [Resource #2 "Network" constructed]
  Using Resource #1 "Database"
  Using Resource #2 "Network"
  [Resource #1 "Database" destroyed]
  [Resource #2 "Network" destroyed]

--- 4. Array of objects ---
  [Resource #3 "File" constructed]
  [Resource #4 "Socket" constructed]
  [Resource #5 "Timer" constructed]
  Using Resource #3 "File"
  Using Resource #4 "Socket"
  Using Resource #5 "Timer"
  [Resource #3 "File" destroyed]
  [Resource #4 "Socket" destroyed]
  [Resource #5 "Timer" destroyed]

--- 5. RAII wrapper ---
Smart pointer holds: 99
  IntPtr destructor: deleting pointer
After RAII scope: memory automatically freed.

--- 6. Deep copy (DynString) ---
  DynString allocated 14 bytes
  "Hello, C++98!" (len=13)
  DynString copy: allocated 14 bytes
  "Hello, C++98!" (len=13)
  DynString allocated 10 bytes
  DynString assign: reallocated 14 bytes
  "Hello, C++98!" (len=13)
  DynString freed 14 bytes
  DynString freed 14 bytes
  DynString freed 14 bytes

--- 7. Safe delete patterns ---
Deleting null pointer is safe (no-op).

--- 8. 2D dynamic array ---
2D array:
  1	2	3	4
  5	6	7	8
  9	10	11	12
2D array freed.
*/
Run command
g++ -std=c++98 -Wall -Wextra -o example dynamic_memory.cpp