M. Alexandru
M. Alexandru

Reputation: 13

C++ custom delete

Let's say we have the following code:

class MyType
{
    public: int x;
}

MyType* object = new MyType();
delete object;

Is there a possible to check if object has specific value of x before deletion? For example, if object.x is odd number, the object should not be free from memory after delete is called. In a few words to make a custom delete for this class where we can choose when the object can be free at operator delete call.

Upvotes: 0

Views: 619

Answers (2)

dfrib
dfrib

Reputation: 73186

What is the real issue you are trying to solve?

To begin with, this Q&A is a good lesson to the common scenario in C++ of:

  • The language allowing you to do something, but just because you can doesn't mean you should.
  • These scenarios are commonly run into when trying to solve an XY problem.

In this particular case, instead of trying to overload operator delete (solving the Y problem, to much confusion of the clients of MyType) you are likely looking for something entirely different (the X problem), say a resource manager that take responsibility of MyType resources w.r.t. if and when they should be deleted (or not), based on e.g. object traits (such as oddness of a data member). See e.g. @Ayxan Haqverdili's answer for a minimal example.


A look at theoretically solving the misidentified Y problem

Don't do this, ever, for this kind of use case.

You can overload operator delete (as well as operator delete[]), by defining it as a static member function of the class for which you want to overload it (lookup precedence by lexical scope). As pointed out in the comments, whilst overloading the usual deallocation functions is legal, trying to conditionally allow for it to be called more than once is not:

struct MyType final {
  int x;
  static void operator delete(void *p) {
    if (static_cast<MyType *>(p)->x % 2 == 1) {
      ::operator delete(p);
    }
  }
};

int main() {
  MyType *object = new MyType{42};
  delete object; // Not DEALLOCATED.
                 // Underlying object DESTRUCTED, however.
                 // Extremely confusing.
  ++(object->x);
  delete object; // UB: destructor for underlying object called twice.
}

as per [expr.delete]/6 [emphasis mine]:

If the value of the operand of the delete-expression is not a null pointer value and the selected deallocation function (see below) is not a destroying operator delete, the delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted.

As a second attempt (emphasis: don't ever go down this road), we could avoid the destructor being called (only calling it conditionally) by overloading and abusing the (new as of C++20) class-specific destroying deallocation functions:

#include <iostream>
#include <new>

struct MyType final {
  int x;
  static void operator delete(MyType *p, std::destroying_delete_t) {
    if (p->x % 2 == 1) {
      p->~MyType();
      ::operator delete(p);
    }
  }
  ~MyType() {
      std::cout << "\ndtor";
  }
};

int main() {
  MyType *object = new MyType{42};
  delete object; // Neither deallocated nor underlying object destructed.
  ++(object->x);
  delete object; // Destructed and deallocated.
}

Afaict this program is well-formed, but it does abuse the new destroying delete overloads. Clang does emit a -Wmismatched-new-delete warning, which is typically a hint for UB, but it may be inaccurate here.

Anyway, this seems like a good place to stop the fruitless journey of addressing this particular Y problem.

Upvotes: 5

Aykhan Hagverdili
Aykhan Hagverdili

Reputation: 29965

Yes:

if (object->x % 2 == 1)
    delete object;

Note that raw pointers should be used rarely and with a lot of care.

Upvotes: 2

Related Questions