0

I have 2 classes A and B, which looks like this

class A{
  public:
    ~A(){/* do something */};
};

class B : public A{
  public:
    ~B(){/* do something */};
};

I want to avoid calling ~A() when ~B() is called. I found this post said that std::shared_ptr can customize deleter to control destructor. But I'm working on a Qt project which uses QScopedPointer to declare B. Now my question is:
How to customize the QScopedPointerDeleter to avoid calling destructor of parent A?

nnzzll
  • 328
  • 1
  • 9
  • 4
    Your question is an [XY problem](https://xyproblem.info/). *Why* do you want to stop the `A` destructor from being called? What is the actual and underlying problem that it's supposed to solve? Please ask about the actual problem directly instead. – Some programmer dude Sep 09 '22 at 07:08
  • @Someprogrammerdude thanks for the comment but my actual problem just can't be solved so I need another solution to avoid it. I inherit a class which is written by third party library and it's destructor is trying to access memory which is released by Qt during the event loop, so I'm wondering is there any solution to avoid it. – nnzzll Sep 09 '22 at 07:13
  • 4
    tell us about the bigger picture, why is some third party library trying to access invalid memory in its destructor? That sounds like a design issue. Your current solution sounds like a hack that avoids to actually solve the root cause – 463035818_is_not_an_ai Sep 09 '22 at 07:22

1 Answers1

1

The good guy

Don't do it. Solve the actual problem in a clean way.

The bad guy - I really want to do it

WARNING Keep in mind that you may get in trouble using this approach, f.ex. by creating memory leaks or undefined behavior.

Note that calling the child destructor ~B, without executing the parent/base destructor ~A isn't possible:

Even when the destructor is called directly (e.g. obj.~Foo();), the return statement in ~Foo() does not return control to the caller immediately: it calls all those member and base destructors first. [1]

However, you can move the logic of ~B to another member function and call that one instead:

class B : public A{
  public:
    ~B(){/* do something */};

    void DestructWithoutParent () {
      /* do something */
      /* call destructor of members*/
    }
};

Note that you should now call the destructor of your members (if any) explicitly.

Now you can release the memory of your B object manually and call your custom destructor function (see this answer for more information and why you shouldn't use this approach):

B *b = new B();
b->DestructWithoutParent (); // calls the 'destructor' of B without calling ~A
operator delete(b); // free memory of b

Note that the heap memory owned by A (f.ex. by smart pointer members of A), isn't released in this way. It may also violate the assumptions made by the programmer of class A, possibly resulting in undefined behavior.

Integrating this with QScopedPointer[2]:

struct BDeleter {
    static inline void cleanup(B *b)
    {
        b->DestructWithoutParent (); // calls the 'destructor' of B without calling ~A
        operator delete(b); // free memory of b
    } 
};

QScopedPointer<B, BDeleter> b(new B);
m7913d
  • 10,244
  • 7
  • 28
  • 56