Is it possible to delete an object in c++ without calling the destructor?
Asked Answered
G

8

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.

Gallicanism answered 14/2, 2013 at 18:2 Comment(7)
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 ?Electoral
@Electoral Yes. Alternatively, calling delete/free without calling the destructor.Gallicanism
Well, just set those internal pointers to nullptr, then deleteing them will be a no-op.Cesura
delete calls the destructor.Penney
@ChristianRau Yep. Now I feel like an idiot for not thinking of that.Gallicanism
@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.Pascale
@Pascale 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.Cesura
A
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!
Ambulant answered 14/2, 2013 at 18:12 Comment(4)
And it goes boom in the future when he forgets. Use the force and beware the darkside!Unlisted
@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)Braxy
@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".Quartan
In C++11 one should set them to nullptr, not to NULL.Lara
P
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.

Pascale answered 14/2, 2013 at 19:3 Comment(3)
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.Braxy
@EmilioGaravaglia I believe you're just repeating part of what I said. I don't see what, if any, corrections you are proposing.Pascale
It's not a correction: just a complement about the corresponding placement new / operator new, and about this construct happens regularly inside the standard libraryBraxy
A
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!
Ambulant answered 14/2, 2013 at 18:12 Comment(4)
And it goes boom in the future when he forgets. Use the force and beware the darkside!Unlisted
@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)Braxy
@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".Quartan
In C++11 one should set them to nullptr, not to NULL.Lara
U
3

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...

Unlisted answered 14/2, 2013 at 18:5 Comment(6)
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.Gallicanism
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.Unlisted
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.Lupine
@MikeIzbicki: Does it make sense to say something is much simpler when it's broken?Herrle
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.Gallicanism
Given this info, I retract some of my comments. Kinda wish this one in the original post :)Unlisted
D
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).

Denim answered 14/2, 2013 at 18:8 Comment(0)
U
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.

Urien answered 14/2, 2013 at 18:13 Comment(0)
Q
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".

Quartan answered 14/2, 2013 at 18:15 Comment(0)
M
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.

Mauromaurois answered 14/2, 2013 at 18:11 Comment(0)
M
0

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?

Medeiros answered 16/6, 2023 at 18:19 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.