Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Dynamic Memory Allocation and Generic Programming in C++

Tech 1

Heap Allocation with new and delete

C++ replaces traditional C allocation routines with operators that seamlessly integrate object lifecycles.

Primitive Data Allocation Fundamental types use straightforward syntax. Single items require new/delete, while contiguous blocks require new[]/delete[]. Mismatched pairs result in undefined behavior.

void allocate_fundamentals() {
    int* single_item = new int;           // Uninitialized
    int* initialized = new int{42};       // Initialized via brace-init
    int* sequence = new int[20];          // Contiguous block

    delete single_item;
    delete initialized;
    delete[] sequence; // Brackets mandatory for arrays
}

Custom Object Management The critical distinction from malloc/free is lifecycle automation. new reserves storage and immediately executes the constructor. Conversely, delete triggers the destructor before reclaiming memory.

class Device {
public:
    explicit Device(int port = 0) : port_id(port) {
        // Initialization routines
    }
    ~Device() {
        // Resource teardown
    }
private:
    int port_id;
};

void object_allocation() {
    // malloc only allocates raw bytes, skipping constructors
    Device* raw_storage = static_cast<Device*>(std::malloc(sizeof(Device)));
    std::free(raw_storage);

    // new handles construction automatically
    Device* active = new Device{101};
    delete active; // Destructor runs, then memory frees

    // Arrays require element-wise construction
    Device* cluster = new Device[5];
    delete[] cluster;
}

Underlying Implementation The language operators act as wrappers around global allocation routines: operator new and operator delete.

  • Primitive Types: Functionally idantical to malloc/free, except failure triggers a std::bad_alloc exception rather than a null pointer.
  • Class Instances: The compiler splits the operation into two distinct steps:
    1. operator new acquires raw memory (typically invoking malloc).
    2. The constructor initializes the object at that address.
  • Array Operations: operator new[] calculates the total byte requirement, stores element metadata, and invokes the constructor for each item. Deallocation reverses the process: destructors execute in reverse order, followed by operator delete[].

Placement New This syntax allows object construction within pre-existing memory buffers. It separates allocation from initialization. Syntax: new (target_address) Type(constructor_args)

void manual_construction() {
    void* buffer = std::malloc(sizeof(Device));
    // Construct object directly in the buffer
    Device* instance = new (buffer) Device{200};
    
    instance->~Device(); // Manual destructor invocation required
    std::free(buffer);

    // Alternative using global operator
    void* region = ::operator new(sizeof(Device));
    Device* unit = new (region) Device{201};
    unit->~Device();
    ::operator delete(region);
}

Generic Programming via Templates

Templates enable algorithms to remain independent of specific data types, eliminating redundant code duplication.

Function Templates A template defines a blueprint. The compiler generates type-specific functions during the translation phase based on invocation context.

template <typename DataType>
void invert(DataType& x, DataType& y) {
    DataType temp = x;
    x = y;
    y = temp;
}

Note: typename and class are interchangeable for template parameters, but struct is not permitted.

Instantiation Methods

  1. Implicit Deduction: The compiler infers the type from provided arguments.
  2. Explicit Specification: The developer defines the type within angle brackets.
template <typename Val>
Val combine(Val a, Val b) {
    return a + b;
}

void test_deduction() {
    combine(8, 9);               // Implicit: Val = int
    combine<float>(5.5f, 2.0f);  // Explicit: Val = float

    // Mixed types cause deduction failure
    // combine(7, 3.5); // Error: ambiguous Val

    // Resolution A: Explicit template argument
    combine<int>(7, 3.5); // 3.5 converts to 3

    // Resolution B: Explicit cast
    combine(7, static_cast<int>(3.5));
}

Overload Resolution Rules

  • Regular functions and templates can share identifiers.
  • Exact signature matches prioritize non-template functions.
  • If the template yields a tighter type match without conversion, the compiler selects it.
  • Template parameter deduction forbids implicit type conversions, unlike standard functions.
int precise_sum(int a, int b) { return a + b; }

template <class X, class Y>
X flexible_sum(X a, Y b) { return a + b; }

void resolution_demo() {
    precise_sum(3, 4);       // Calls regular function (exact match)
    // precise_sum(3, 4.5);  // Fails: no implicit conversion allowed for exact match
    
    flexible_sum(3, 4);      // X=int, Y=int
    flexible_sum(3, 4.5);    // X=int, Y=double, compiles successfully
}

Class Templates Structures and classes can also be parameterized. Defining members outside the declaration requires repeating the template header.

template <class Item>
class Container {
public:
    explicit Container(size_t reserve = 10)
        : storage(new Item[reserve]), count(0), max_items(reserve) {}
    
    ~Container();
    void insert(const Item& value);
    Item& fetch(size_t index) {
        return storage[index];
    }

private:
    Item* storage;
    size_t count;
    size_t max_items;
};

template <class Item>
Container<Item>::~Container() {
    delete[] storage;
    count = 0;
    max_items = 0;
}

// Instantiation requires explicit type specification
Container<int> integer_cache;
Container<double> numeric_buffer;

The base template identifier is not a valid type until specialized with template arguments.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.