Why are these custom new and delete allocators behaving like this?

Viewed 47

There are two problems I'm having with the following code. One is that my custom operator delete function is not being called, particularly the array version, but only for a custom class as opposed to an int type. The other issue is I'm is that I want to understand why there's an extra 8 bytes allocated only for the custom class array but not the built-in type array:

#include <stdlib.h>
#include <iostream>
#include <exception>
using namespace std;


struct TestClass
{
    uint32_t m;
    TestClass() {
        std::cout << "TestClass Constructor called\n";
    }
    ~TestClass()
    {
        std::cout << "TestClass Destructor called\n";
    }
};


size_t allocatedMemory = 0;

// Single version new
void* operator new(size_t count)
{
    void* mem = malloc(count);
    if (!mem) 
        throw bad_alloc();
    else
    {
        allocatedMemory += count;
        cout << "void operator new(size_t count) called with " << count << " size\n";

        return mem;
    }
}
// Array version new
void* operator new[](size_t count)
{
    void* mem = malloc(count);
    if (!mem)
        throw bad_alloc();
    else
    {
        allocatedMemory += count;
        cout << "void operator new[](size_t count) called with " << count << " size\n";

        return mem;
    }

}

// Single version delete
void operator delete(void* mem, size_t size)
{
    allocatedMemory -= size;
    free(mem);
    cout << "void operator delete(void* mem, size_t size) called with " << size << " size\n";
}
// Array version delete
void operator delete[](void* mem, size_t size)
{
    allocatedMemory -= size;
    free(mem);
    cout << "void operator delete[](void* mem, size_t size) called with " << size << " size\n";
}


int main()
{
    // WITH CUSTOM CLASS TYPE
    //=====================================
    cout << "Single TestClass:\n";
    TestClass* testObj = new TestClass; // operator new(size_t count) called with size of 4
    delete testObj; // operator delete(void* mem, size_t size) called with size of 4
    cout << "\n";

    cout << "TestClass array:\n";
    TestClass* testObjArray = new TestClass[5]; // operator new[](size_t count) called with size of 28
                                                // An extra 8 bytes
    delete[] testObjArray; // operator delete[](void* mem, size_t size) called with size of 28;
                            // An extra 8 bytes

    cout << "\n";

    // WITH BUILT-IN TYPE INT
    //=====================================
    cout << "Single uint32_t:\n";
    uint32_t* fourByteInt = new uint32_t; // operator new(size_t count) called with size of 4
                                        // Same as with single class TestClass object
    delete fourByteInt; // operator delete(void* mem, size_t size) called with size of 4
                        // Same as with single class TestClass object

    cout << "\n";

    cout << "uint32_t array:\n";
    uint32_t* fourByteIntArray = new uint32_t[5]; // operator new[](size_t count) called with size of 20
                                                // 8 bytes less than with the array of TestClass
    
    delete[]fourByteIntArray; // None of my operator functions called at all


}

I have two questions:

  1. Why isn't my void delete[](void* mem, size_t size) function called at all when deleting the array of the built-in type uint32_t? My function IS called when deleting the array of the TestClass objects.

  2. Why is it allocating 28 bytes instead of 20 bytes only for the TestClass array? I assume the extra 8 bytes holds information about how long the array is so that it knows how much memory to free, but isn't this information required whether the array is of a built-in/POD/whatever it's called data type or a custom type?

1 Answers

You also need to define

void operator delete(void* mem)

and

void operator delete[](void* mem)

From cppreference

5-6) Called instead of (1-2) if a user-defined replacement is provided, except that it's unspecified whether (1-2) or (5-6) is called when deleting objects of incomplete type and arrays of non-class and trivially-destructible class types.

Here (1-2) are the versions of delete without a size parameter, and (5-6) are the versions of delete with a size parameter. So it's unspecified which version of delete is called for an array of uint32_t.

Related