3

In c++ calling delete on an object allocated with new calls the destructor of the class, and release the memory. Would there be any difference if, instead of deleting the object, one calls explicitly its destructor and then free the memory?

Consider, e.g., the following example.

#include <iostream>

struct A {
  int i;
  ~A() { std::cout << "Destructor, i was " << i << std::endl; }
};

int main()
{
  A* p = new A{3};
  p->~A();
  free(p);

  return 0;
}

Compiling the code with g++ 7.3.0 and clang 6.0.1, and with -Wextra -Wall -pedantic does not raise any warning. The output of the program, as expected is

Destructor, i was 3

Running the program with valgrind/memcheck gives an error of mismatched free/delete:

==27743== Memcheck, a memory error detector
==27743== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==27743== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==27743== Command: ./destructor
==27743== 
Destructor, i was 3
==27743== Mismatched free() / delete / delete []
==27743==    at 0x4C3033B: free (vg_replace_malloc.c:530)
==27743==    by 0x4009FC: main (in /tmp/destructor)
==27743==  Address 0x5bbac80 is 0 bytes inside a block of size 4 alloc'd
==27743==    at 0x4C2F77F: operator new(unsigned long) (vg_replace_malloc.c:334)
==27743==    by 0x4009DA: main (in /tmp/destructor)
==27743== 
==27743== 
==27743== HEAP SUMMARY:
==27743==     in use at exit: 0 bytes in 0 blocks
==27743==   total heap usage: 3 allocs, 3 frees, 73,732 bytes allocated
==27743== 
==27743== All heap blocks were freed -- no leaks are possible
==27743== 
==27743== For counts of detected and suppressed errors, rerun with: -v
==27743== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

However, there is no memory leak.

I know of course that the standard way to dispose of the object p in the code above is to call delete. My question is more a formal one:

  • Is delete fully equivalent to a call to destructor and a free? Does the standard specify this, or I am running into UB with the code above?
francesco
  • 7,189
  • 7
  • 22
  • 49
  • Passing a non-null pointer to `free()` gives undefined behaviour if that pointer was not returned by `malloc()`, `calloc()`, or `realloc()`. Your call of `free()` therefore has undefined behaviour. – Peter Jan 15 '19 at 08:36
  • 1
    Also: [In brief, conceptually malloc and new allocate from different heaps](https://isocpp.org/wiki/faq/freestore-mgmt#mixing-malloc-and-delete). – Daniel Langr Jan 15 '19 at 08:38
  • @francesco But there are many answers to your question as well. See, e.g., my previous comment with copy-pasted link. There is simply no guarantee that `new` uses `malloc`/`calloc`/`realloc`. – Daniel Langr Jan 15 '19 at 08:40
  • 1
    @DanielLangr I don't think this is a duplicate. This question is aware of the dtor, but asks for dtor + `free` vs `delete`. Thats different... – m8mble Jan 15 '19 at 09:32
  • @m8mble If the question is answered elsewhere, despite that the other question is asked differently, then it's IMO a duplicate. Though we may argue about it :). But the answers are the same. BTW, both questions address the very same problem. Not invoking a destructor is not necessarily a problem; see, e.g., [Is it OK not to call the destructor on placement new allocated objects?](https://stackoverflow.com/q/41385355/580083). – Daniel Langr Jan 15 '19 at 09:37
  • @DanielLangr I'm not convinced. Let me put it differently: Suppose I wan't to answer *this* question with a quote from the standard. Should I post it to the other question to which the quoted text doesn't actually apply? – m8mble Jan 15 '19 at 09:42
  • @m8mble Which quote from the Standard applies to this question and not to the other one? – Daniel Langr Jan 15 '19 at 09:44
  • @DanielLangr I think a major difference is the possibility of `operator delete` overloads for which 18.6.1.12 specifies ordering requirements. – m8mble Jan 15 '19 at 09:51
  • @m8mble But that applies to the other question as well, right? The only difference between both questions I can see is that in this one, the destructor is explicitly invoked. Which doesn't change the answers. – Daniel Langr Jan 15 '19 at 10:10
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/186703/discussion-between-daniel-langr-and-m8mble). – Daniel Langr Jan 15 '19 at 10:13

1 Answers1

0

While it is legal to explicitly call the destructor the cases where you will want to do so are very rare (std::vector for example where objects are constructed and destructed within a larger buffer).

Note that you should always match allocators with the appropriate memory release, malloc/free, new/delete, etc. While it is typical for operator new() to rely on malloc under the hood there is no requirement that it do so and in that case your mismatch would have undefined results.

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23