1

The (C++14) class below has an overloaded new operator to allocate continuous memory for an object and some bytes as additional storage. The object stores the size of the additional storage in a member variable. The additional storage size should be passed as a parameter to the new operator. E.g. to allocate 10 bytes as storage:

Storage* ptr = new(10)Storage;

The implementation below leads to an error: "placement new expression refers to non-placement function":

class Storage
{
private:
    std::size_t storage_size;
public:
    Storage(){};
    ~Storage() {};
    static void* operator new(std::size_t object_size, std::size_t storage_size)
    {
        // Allocate memory for the object and the storage
        char* ptr = new char[object_size + storage_size];
        // Placement new
        Storage* ptr2 = ::new(ptr)Storage;
        // Set member variable
        ptr2->storage_size = storage_size;
        // Return 
        return ptr2;
    }
    static void operator delete(void* ptr, std::size_t storage_size)
    {
        // How?
    }
};

How can I implement the overloaded delete operator?

UPDATE

The following code is based on the comments and example of @Igor Tandetnik (thanks!) and works as expected. I could manage to set the member variable as well.

class Storage
{
private:
    std::size_t storage_size;
public:
    Storage(bool do_throw) { 
        std::cout << "Storage(): this = " << this << std::endl; 
        if (do_throw) {
            std::cout << "Throwing exception!" << std::endl;
            throw 42;
        }
    };
    ~Storage() { std::cout << "~Storage(): this = " << this << std::endl; };
    void print_storage() { std::cout << "Storage size: " << storage_size << std::endl; }
    static void* operator new(std::size_t object_size, std::size_t storage_size)
    {
        char* ptr = new char[object_size + storage_size]; // Allocate memory
        std::cout << "Overloaded new: ptr = " << static_cast<void*>(ptr) << std::endl;
        Storage* ptr2 = static_cast<Storage*>(static_cast<void*>(ptr));
        ptr2->storage_size = storage_size;
        return ptr;
    }
    static void operator delete(void* ptr, std::size_t storage_size)
    {
        std::cout << "Overloaded delete: ptr = " << ptr << std::endl;
        delete[] ptr;
    }
    static void operator delete(void* ptr)
    {
        std::cout << "Delete: ptr = " << ptr << std::endl;
        delete[] ptr; // Destructor called automatically
    }
};


int main()
{
    try {
        auto ptr = new(10) Storage(false);
        ptr->print_storage();
        delete ptr;

        ptr = new(10) Storage(true);
        ptr->print_storage();
        delete ptr;
    }
    catch (...) {
        std::cout << "Got exception" << std::endl;
    }

    return 0;
}
G. C.
  • 45
  • 6
  • 2
    @GC to implement `delete`, overload the standard `delete` operator` to call the `Storage` destructor and `delete[]` the `char[]` memory you allocated, eg: `static void operator delete(void* ptr) noexcept { static_cast(ptr)->~Storage(); delete[] static_cast(ptr); }` – Remy Lebeau Oct 03 '19 at 19:18
  • 1
    Placement `new` constructs an object on memory that is passed to it, it does not allocate. Sample usage would be `char *tptr = new char[sizeof(Storage) + 10]; Storage *ptr = new (tptr, (size_t)10);` and the implementation of the placement `operator new()` function would construct the object on `ptr` and then return `ptr`. The implementation of the placement `operator delete()` is hopefully obvious from there. – Peter Oct 03 '19 at 19:18
  • 1
    @Peter [not according to this](https://en.cppreference.com/w/cpp/memory/new/operator_new), which shows an example `X* p1 = new (true) X;` using a custom `new` operator `static void* operator new(std::size_t sz, bool b)` that allocates. What GC is trying to do should be able to work. – Remy Lebeau Oct 03 '19 at 19:22
  • 1
    @GuillaumeRacicot the question clearly says C+14, and the code matches what C++14 would use. But even in C++17, overloaded new/delete operators don't have to be alignment-aware, overload resolution does 2 passes, first looking for an alignment-aware overload, and then a 2nd pass if needed looking for an alignment-unaware overload. – Remy Lebeau Oct 03 '19 at 20:22
  • Possible duplicate https://stackoverflow.com/questions/44161611/exception-cleanup-error-on-gcc-6-3-with-c14 – n. m. could be an AI Oct 03 '19 at 20:41
  • `operator new` should only allocate memory, it should not construct the object - the `new` expression will call the constructor after `operator new` returns. In your example, the constructor runs twice on the same `this` pointer. – Igor Tandetnik Oct 04 '19 at 03:53
  • 1
    You need both `operator delete(void*, std::size_t)` and `operator delete(void*)`. Placement `delete` is called in only one case - if the `new` expression calls the placement `new`, then the constructor, and that constructor throws an exception; then placement `delete` is called to free the memory. Normal `delete` expression always calls regular non-placement `delete`. [Live demo](https://rextester.com/MZNO84260) (which also shows the constructor being called twice). – Igor Tandetnik Oct 04 '19 at 03:56
  • 1
    [Another demo](https://rextester.com/BKTEJ61113) that explores the interactions between all these special members in more detail. – Igor Tandetnik Oct 04 '19 at 04:01

0 Answers0