13

I have an object with some pointers inside of it. The destructor calls delete on these pointers. But sometimes I want to delete them, and sometimes I don't. So I'd like to be able to delete the object without calling the destructor. Is this possible?

Edit: I realize this is an AWFUL idea that no one should ever do. Nonetheless, I want to do it because it will make some internal functions much easier to write.

Mike Izbicki
  • 6,286
  • 1
  • 23
  • 53
  • So the question is how to make the dynamically allocated object to be deallocated in a way you don't need to explicitly call `delete` / `free` ? – LihO Feb 14 '13 at 18:07
  • @LihO Yes. Alternatively, calling `delete`/`free` without calling the destructor. – Mike Izbicki Feb 14 '13 at 18:10
  • 8
    Well, just set those internal pointers to `nullptr`, then `delete`ing them will be a no-op. – Christian Rau Feb 14 '13 at 18:10
  • 1
    `delete` calls the destructor. – Robert Allan Hennigan Leahy Feb 14 '13 at 18:10
  • 1
    @ChristianRau Yep. Now I feel like an idiot for not thinking of that. – Mike Izbicki Feb 14 '13 at 19:01
  • @MikeIzbicki `delete` does two things; call the destructor then deallocate memory. If you set the pointer to `nullptr` you're not just avoiding the destructor, you're also not doing the deallocation. You have explicitly said you _do_ want deallocation, therefore setting the pointer to `nullptr` does not achieve what you've asked. – bames53 Feb 14 '13 at 19:10
  • @bames53 No, that was a misunderstand (but your elabortations are correct, of course). He doesn't want to call the destructor of the outer wrapping object (while still deallocating it), in order to not `delete` the internal pointers (since that's what this wrapping object's destructor does). So I suggested to set the interal pointers to `nullptr` to not `delete` them when still executing the outer object's destructor properly. – Christian Rau Feb 14 '13 at 20:18

8 Answers8

14

The delete operator does two things to the object you pass it:

  • calls the appropriate destructor.
  • calls the deallocation function, operator delete.

So deleting an object without calling a destructor means you want to simply call operator delete on the object:

Foo *f = new Foo;
operator delete(f);

operator delete is a normal function call and the usual name lookup and overload resolution is done. However the delete operator has its own rules for finding the correct operator delete. For example the delete operator will look for member operator delete functions that the usual name lookup and overload resolution does not find. That means that you need to make sure you're calling the right function when you manually use operator delete.

As you say, using operator delete directly is an awful idea. Failing to call the destructor breaks RAII, resulting in resource leaks. It can even lead to undefined behavior. Also, you'll have to take on the responsibility of writing exception safe code without RAII, which is exceptionally hard. You're almost guaranteed to get it wrong.

bames53
  • 86,085
  • 15
  • 179
  • 244
  • 2
    `delete f` is actually `f->~Foo(); operator delete(f);` (Just like `new Foo` is actually `new (operator new(sizeof(Foo))) Foo`). Those "*disjoint allocation/release from construct/destroy*" forms are rare, but happen when the history of the memory doesn't follow the history of the object it holds. This is typically found -for example- in `std::vector` implementations. – Emilio Garavaglia Feb 15 '13 at 08:42
  • @EmilioGaravaglia I believe you're just repeating part of what I said. I don't see what, if any, corrections you are proposing. – bames53 Feb 15 '13 at 15:16
  • It's not a correction: just a complement about the corresponding placement new / operator new, and about this construct happens regularly inside the standard library – Emilio Garavaglia Feb 15 '13 at 15:21
7

You can set the pointers to NULL, then the destructor will not delete them.

struct WithPointers
{
    int* ptr1;
    int* ptr2;

    WithPointers(): ptr1(NULL), ptr2(NULL) {}

    ~WithPointers()
    {
        delete ptr1;
        delete ptr2;
    }
}

...
WithPointers* object1 = new WithPointers;
WithPointers* object2 = new WithPointers;
object1->ptr1 = new int(11);
object1->ptr2 = new int(12);
object2->ptr1 = new int(999);
object2->ptr2 = new int(22);
...
int* pointer_to_999 = object2->ptr1;
object2->ptr1 = NULL;
delete object1;
delete object2; // the number 999 is not deleted now!
// Work with the number 999
delete pointer_to_999; // please remember to delete it at the end!
anatolyg
  • 26,506
  • 9
  • 60
  • 134
  • 1
    And it goes boom in the future when he forgets. Use the force and beware the darkside! – Michael Dorgan Feb 14 '13 at 18:14
  • 1
    @MichaelDorgan: If it goes boom if he forget is not a problem: he will discover it and correct it. The "dark side" are things that are incorrect but don't go boom! (you'll never know until a user one day will see something incorrect) – Emilio Garavaglia Feb 14 '13 at 20:08
  • 1
    @EmilioGaravaglia leaking is a classic "thing that is incorrect but doesn't go boom!" And deleting something you shouldn't is classic "things that are incorrect but doesn't go boom until a bit of code that is doing nothing wrong in a completely different spot goes boom with little evidence linking it back to the actual problem that caused the boom". – Yakk - Adam Nevraumont Feb 15 '13 at 03:19
  • 1
    In C++11 one should set them to `nullptr`, not to `NULL`. – Pharap Mar 05 '19 at 01:43
4

Eww! Yes, it's possible, no I wouldn't do it. Have the objects that need variable lifetime be controlled via a shared pointer or some other reference counted object instead. Cleaner to work with C++ than breaking some of its internal tenants...

Michael Dorgan
  • 12,453
  • 3
  • 31
  • 61
  • I know this is the proper way to do it, but I'd like to do it my way for various reasons. This is not functionality that would be exposed publicly, but something used internally to make a few functions much simpler. – Mike Izbicki Feb 14 '13 at 18:07
  • 2
    I would advice you not to go there. Who knows, when in the future, someone else walks into your code and tweeks something and boom. Or perhaps a new compiler version, etc. Breaking the rules makes it very hard to debug something as the solution won't be the first 20 things that come to mind when looking at the code. – Michael Dorgan Feb 14 '13 at 18:11
  • 3
    It might make some internal functions "easier to write" (debatable) but it *certainly* won't make them safer or easier to *maintain*. Anybody looking at this code after you is going to think you were insane. – user229044 Feb 14 '13 at 18:18
  • @MikeIzbicki: Does it make sense to say something is much simpler when it's broken? – GManNickG Feb 14 '13 at 18:19
  • It's a test harness for grading student code (I'm a TA). I don't really want to manually edit all 100 of their classes when this hack will work just fine and can be automated. – Mike Izbicki Feb 14 '13 at 18:59
  • Given this info, I retract some of my comments. Kinda wish this one in the original post :) – Michael Dorgan Feb 14 '13 at 21:39
2

Whatever you're actually trying to do, there is a problem with your code. If you don't want to delete the sub-objects, just use a boolean flag that you will set before deleting the object and that will be taken into account by the destructor.

But honestly, you should be using smart pointers instead of naked pointers (and in your very case, it looks like shared pointers are what you need).

syam
  • 14,701
  • 3
  • 41
  • 65
2

Write a method that you can call before calling the destructor. The method will flip a member boolean. When the destuctor is called, it will check that boolean member and destroy the pointer if it is true, and keep it if it is false.

I wouldn't recommend doing this. Better for the class to not take responsibility for deleting the pointer.

umps
  • 1,119
  • 4
  • 15
  • 25
2

Yes, this is possible. std::vector does this, in that it allocates buffers with space for objects, then conditionally constructs them (in-place) and destroys them, managing the memory independently of the object lifetime.

In C++11, I'd use a union of your type and a small type with trivial constructors/destructors to indicate a memory location that can fit your type, but doesn't have to have that type in it. External to that you have to track if the object is actually there. Creating the item consists of using placement new, and destroying it consists of manually calling the destructor.

The buffer of the union objects, be it N objects or 1, would be managed completely independently. The default constructor of the union would either construct nothing, or construct the trivial type (in which case you might want to destroy that trivial type).

However, odds are that the real answer to your question is "don't do that". And if you do that, you wrap the pointers in a class whose only job is handling the above mess. Classes of that type (whose job is to manage a pointer's lifetime and pointer-like properties) are called "smart pointers".

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

As others have suggested, the correct way to solve this problem isn't to NOT call the destructor [you only need to add something like std::string or std::vector to your object, and all of a sudden you have a memory leak]. The correct way is to either not let your object own those other objects at all (e.g. delete them separately before/after the object is deleted), or have a method with which the object knows whether to delete the pointers or not.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
-1

In c++20, there is a better way,std::destroying_delete_t tag type used to identify the destroying delete form of operator delete.

Usage is discussed in this question below: What is "destroying operator delete" in C++20?

wine qas
  • 1
  • 1