-1

I know that C++ allows creating a class with a deleted or inaccessible destructor:

struct Foo{
    Foo() = default;
    ~Foo() = delete;
};

Foo f;// error: ~Foo is deleted
Foo* pf = new Foo();// ok
  • Now it's OK to create a dynamic object but it leaks.

  • We know that I should'n mix delete with free and new with malloc but we can cast a pointer of any type to a pointer to void:

    void* p = (void*)pf; free(p);

  • It is safe now to free the dynamic object pointed to by pf via free after being cast to void*?

  • If it is not safe then if my class that has a deleted dtor there's no way to free objects allocated on the free store?

Itachi Uchiwa
  • 3,044
  • 12
  • 26
  • 1
    This is probably an [XY problem](https://en.wikipedia.org/wiki/XY_problem). If the destructor is `delete`d - you have no business trying to `delete` it. Who knows _why_ it's `delete`d and what will happen if you `free` the memory it's occupying? The whole setup sounds really dodgy. – Ted Lyngmo Oct 12 '21 at 16:45
  • 5
    An object that can be constructed but not destructored is extremely unusual. What you're supposed to do with it depends entirely on how `Foo` is designed to be used. There is no answer to this hypothetical code, it is synthetic and doesn't fulfill a need so doesn't define how it should be used. – François Andrieux Oct 12 '21 at 16:49
  • 5
    `Is it safe?` -> nope. It is Undefined Behaviour to `free()` what was allocated with `new`, simple as that. – alter_igel Oct 12 '21 at 16:52
  • And to be clear, without extra work, instances of your class will always leak any resources they hold _by design_ due to the deleted destructor. – alter_igel Oct 12 '21 at 16:54
  • 1
    `operator new` is not required to call `malloc`, so the pointer you get back from it is not necessarily something you can `free`. However, the standard goes out of its way to ensure that implementing `operator new` with `malloc` will work correctly, and in fact, every implementation (okay, every implementation that I know of) does that. – Pete Becker Oct 12 '21 at 17:10
  • The cast would have happened implicitly if you called `free` directly anyway. That's not the issue at hand. The problem is that `new` doesn't necessarily call `malloc`. – Aykhan Hagverdili Oct 12 '21 at 17:22

2 Answers2

2

You can malloc memory, placement new it, then free the memory.

This ends the lifetime of the object without destroying it. Subobject lifetime is also ended, but their destructors are not called either. This can be dangerous, or as intended.

You cannot free memory allocated by new. Doing so is behavior undefined by the C++ standard.

In theory you can overload operator new on your class to call malloc then placement new the object. That would make calling free more well behaved.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    "_You cannot free memory allocated by new ... and maintain defined behavior_"? Just to point out that, sure, it may _look_ like you can `free` memory allocated by `new`, but the program will have UB. – Ted Lyngmo Oct 12 '21 at 16:58
  • 1
    @TedLyngmo - It's fine as is. There's no need to add "and be lawful" when one says "You cannot cross during a red traffic light". – StoryTeller - Unslander Monica Oct 12 '21 at 17:04
  • @StoryTeller-UnslanderMonica :-) True ... but there's often one who has _observed_ that it "worked" and makes assumptions. When the question is what it is, it wouldn't hurt to give it a little more UB warnings. I upped both answers without edits though. It _is_ good as it is. – Ted Lyngmo Oct 12 '21 at 17:08
  • No my answer, not my monkeys. I guess that's why I like the red light analogy. You tell people it's not safe and they too may respond its fine because nothing bad happened to them (yet). – StoryTeller - Unslander Monica Oct 12 '21 at 17:15
  • @StoryTeller-UnslanderMonica I know it's not your answer and I totally get the analogy. The only reason I thought it would be a good idea to tell the pedestrian about the cars is because it looked like this one was about to step out into full traffic. Perhaps a tightly packed mine field would be an even better analogy Full traffic isn't that hard to handle :-) – Ted Lyngmo Oct 12 '21 at 17:38
2

No.

It's not about the type void*, it's about the allocator design.

new and malloc may work completely differently, or might not even share the same memory. So it's only logical that you're not allowed to send one into the other.

Also, even if you call free, you will need to call ~Foo(). Otherwise you still leaked objects.

GCC even has the decency to warn you:

<source>: In function 'int main()':
<source>:10:9: warning: 'void free(void*)' called on pointer returned from a mismatched allocation function [-Wmismatched-new-delete]
   10 |     free(pf);
      |     ~~~~^~~~
<source>:9:23: note: returned from 'void* operator new(long unsigned int)'
    9 |     Foo* pf = new Foo();// ok
      |                       ^

Then you might try to run the program, but the address sanitizer will report an error and abort the program:

=================================================================
==1==ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new vs free) on 0x602000000010
    #0 0x7fa4139b04f7 in free (/opt/compiler-explorer/gcc-11.2.0/lib64/libasan.so.6+0xb14f7)
    #1 0x401177 in main /app/example.cpp:10
    #2 0x7fa4133b60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #3 0x40109d in _start (/app/output.s+0x40109d)

0x602000000010 is located 0 bytes inside of 1-byte region [0x602000000010,0x602000000011)
allocated by thread T0 here:
    #0 0x7fa4139b1f57 in operator new(unsigned long) (/opt/compiler-explorer/gcc-11.2.0/lib64/libasan.so.6+0xb2f57)
    #1 0x401167 in main /app/example.cpp:9
    #2 0x7fa4133b60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: alloc-dealloc-mismatch (/opt/compiler-explorer/gcc-11.2.0/lib64/libasan.so.6+0xb14f7) in free
==1==HINT: if you don't care about these errors you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0
==1==ABORTING

An object with a deleted destructor is not meant to be free, or instantiated in any conventional way.


You could however allocate a block of memory, placement new into it then destroy all subobjects (including base classes) when deallocating that memory block.

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141