C ++ destruction order: calling field destructor before class destructor

Is there a way to call the field destructor before the class destructor?

Suppose I have 2 classes Small

and Big

, and Big

contains an instance Small

as its field as such:

class Small
{
public:
    ~Small() {std::cout << "Small destructor" << std::endl;}
};

class Big
{
public:
    ~Big() {std::cout << "Big destructor" << std::endl;}

private:
    Small small;
};

int main()
{
    Big big;
}

      

This, of course, calls the large destructor before the small destructor:

Big destructor
Small destructor

      

I need a destructor Small

that is called before the destructor Big

, since it does some of the cleanup required for the destructor Big

.

I could:

  • call the destructor small.~Small()

    explicitly. -> This, however, calls the destructor Small

    twice: once explicitly and once after the destructor is executed Big

    .
  • have Small*

    as field and call delete small;

    in destructorBig

I know I may have a class function Small

that does cleanup and calls it in the destructor Big

, but I was wondering if there is a way to invert the order of the destructor.

Is there a better way to do this?

+3


source to share


3 answers


call small. ~ Small () is a destructor explicitly. -> This, however, calls the small destructor twice: once explicitly and once after the large destructor has been executed.

Well, I don't know why you want to continue with this flawed design, but you can solve the problem described in your first pool using placement new.
This follows a minimal working example:

#include <iostream>

struct Small {
    ~Small() {std::cout << "Small destructor" << std::endl;}
};

struct Big {
    Big() { ::new (storage) Small; }

    ~Big() {
        reinterpret_cast<Small *>(storage)->~Small();
        std::cout << "Big destructor" << std::endl;
    }

    Small & small() {
        return *reinterpret_cast<Small *>(storage);
    }

private:
    unsigned char storage[sizeof(Small)];
};

int main() {
    Big big;
}

      

You no longer have a type variable Small

, but with something like a member function Small

in this example, you can easily bypass it.

The idea is that you reserve enough space to create in place a Small

, and then you can explicitly call its destructor as you did. It won't be called twice, since all the class has to send Big

is an array unsigned char

s.
Moreover, you won't be storing Small

yours directly in dynamic storage, since you are actually using your item Big

to create it.




That being said, I suggest you allocate it on dynamic storage unless you have a compelling reason to do so. Use std::unique_ptr

and reset at the beginning of the destructor Big

. Yours Small

will disappear before the destructor's body is executed as expected, in which case the destructor will not be called twice.


EDIT

As pointed out in the comments, there std::optional

might be another viable solution instead std::unique_ptr

. Keep in mind that std::optional

is part of C ++ 17, so if you can use it, it mainly depends on which version of the standard you have to stick to.

+2


source


Without knowing why you want to do this, my only suggestion is to split Big

into parts to be destroyed after Small

from the rest, and then use composition to include inside Big

. Then you control the order of destruction:



class Small
{
public:
    ~Small() {std::cout << "Small destructor" << std::endl;}
};

class BigImpl
{
public:
     ~BigImpl() { std::cout << "Big destructor" << std::endl; }
};

class Big
{
private:
    BigImpl bigimpl;
    Small small;
};

      

+1


source


The order of calls to the destructor cannot be changed. The correct way to design it is to Small

do its own cleanup.

If you cannot change Small

, then you can create the SmallWrapper

containing class Small

and also do the necessary cleanup.

For this purpose, standard containers std::optional

or std::unique_ptr

or may be sufficient std::shared_ptr

.

0


source







All Articles