0

Here is synthetic example of using custom delete(...) operator overload for a separate class. I am constructing object of class String by allocating memory with std::malloc(...) followed by placement new(...) String. Also i declare custom delete operator for class String that is supposed to use std::free(...) inside it's definition. I know that it is illegal to delete pointers not allocated with new. But

#include <memory>
#include <cstdlib>
#include <utility>

class String{
public:
    /*virtual*/ ~String(){}
    static void operator delete(void* p) noexcept;
};

void foo(size_t extraSize){
    if(void* mem = std::malloc(sizeof(String) + extraSize)){
        String* p = new(mem) String;
        delete p;
    }
}

// The function is defined in some other *.cpp file
/*
void String::operator delete(void* p) noexcept{
    std::free(p);
}
*/

https://godbolt.org/z/7aaj9o43e

Compiling with x86-64 GCC 13.2 results in:

<source>: In function 'void foo(size_t)':
<source>:14:16: warning: 'static void String::operator delete(void*)' called on pointer returned from a mismatched allocation function [-Wmismatched-new-delete]
   14 |         delete p;
      |                ^
<source>:12:31: note: returned from 'void* malloc(size_t)'
   12 |     if(void* mem = std::malloc(sizeof(String) + extraSize)){
      |                    ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiler returned: 0

Any of the following actions can remove the warning:

  1. Compile without optimizations (remove -O flag).
  2. Define 'String::operator delete(void* p)' in this translation unit (uncomment last lines).

I suppose this is GCC's bug. Am I right?

273K
  • 29,503
  • 10
  • 41
  • 64
Cooler
  • 19
  • 3
  • 4
    You can only `delete` what you *allocated* with `new` - you cannot call `delete` on something that was only *created* with placement-new – UnholySheep Aug 10 '23 at 12:12
  • 1
    `new(mem)` doesn't allocate memory, it just creates an object in the memory location `mem`. it is incorrect to `delete` the pointer returned by placement new – NathanOliver Aug 10 '23 at 12:12
  • It's not gcc's bug, it's your misunderstanding of the relationships between memory allocation, object creation, object destruction, and memory deallocation. – molbdnilo Aug 10 '23 at 12:40
  • 1
    I use malloc(...) to allocate memory and CUSTOM operator delete(void*) to free memory. Emphasize CUSTOM. Which must be call by compiler automatically. Inside my custom operator delete(void) i will call free(...). What is wrong here? – Cooler Aug 10 '23 at 12:44
  • What is wrong? Any reference to C++ standard? – Cooler Aug 10 '23 at 12:46
  • `delete p;` is wrong. `p` wasn't allocated with `new`. placement new does not allocate, and you can only use `delete` when `new` allocates. – NathanOliver Aug 10 '23 at 12:48
  • 2
    delete p; where p is a class will call overloaded custom (if any) operator delete(void*) for that class. In that overload i can do everything i want, compiler should not care about that. No prohibition in C++ standard for that. – Cooler Aug 10 '23 at 12:54
  • The bug in the code is not the warning that GCC is squawking about. Given the custom delete calls `free`, GCC is wrong (overly paranoid). But there is still the bug in the code, which can be fixed with `p->~String();` to destruct the object from the placement `new` before freeing the memory out from under a live object. – Eljay Aug 10 '23 at 12:56
  • 1
    That is exactly what I am asking about. As I wrote before the sample code is synthetic, and not from real project. Indeed I even does not define operator delete(void*), but just declare that. Still does not understand where should I put p->~String(). As I know "delete p;" will call destructor ~String() and then custom String::operator delete(void*). Is that rigth? – Cooler Aug 10 '23 at 13:15
  • 1
    *As I know "delete p;" will call destructor ~String() and then custom String::operator delete(void*). Is that rigth* The compiler's `delete` will do that, but your code has replaced the default `delete`. Where in your code do you call destructor `~String()`? – Eljay Aug 10 '23 at 13:30
  • 1
    How are you going to call p->~String() inside delete(void* p) if type of is "void*"? No examples here https://en.cppreference.com/w/cpp/memory/new/operator_delete in "Class-specific overloads" section calls destructor explicitly. – Cooler Aug 10 '23 at 13:41
  • 1
    @Elijay: He's right, it's the difference between the `delete` operator and the `operator delete()` deallocation function. You can't replace the `delete` operator. – Ben Voigt Aug 10 '23 at 14:55
  • 1
    This is not a duplicate of that question, since there is nothing about definition of a class-specific `operator new` there. This may change the whole thing significantly. – Daniel Langr Aug 10 '23 at 15:13
  • The relevant part of the standard is here: http://eel.is/c++draft/expr.delete – Daniel Langr Aug 10 '23 at 15:14
  • @UnholySheep I don't agree. The standard says that `delete` may be applied to a pointer that was produced by `new` expression. And, AFAIK, placement new is a new expression. Link: http://eel.is/c++draft/expr.delete#2.sentence-2 – Daniel Langr Aug 10 '23 at 15:45

0 Answers0