Is it possible to invert the order of destruction?

I have a base class that implements many basic functions, and it needs some kind of "storage" (memory block), which should be provided by the class that inherits it (or the user).

class Base
{
public:

    Base(void* storage, size_t storageSize) :
        storage_{storage},
        storageSize_{storageSize}
    {
        // do something with the storage...
    }

    ~Base()
    {
        // do something with the storage...
    }

    // member functions

private:

    void* storage_;
    size_t storageSize_;
};

It is important to note that this block of memory used in constructor and destructor.

This works very well when the child class uses static storage:

template<size_t Size>
class StaticObject : public Base
{
public:

    StaticObject() :
        Base{&storage, Size}
    {

    }

private:

    typename std::aligned_storage<Size>::type staticStorage_;
};

I know that the storage is used before its creation (it is “built” after the base constructor is completed) and after its destruction (“destroyed” before the base destructor starts working), but for trivial std::aligned_storage<...>::type, it does not matter.

However, this idea does not fully work when I want to use it with dynamically distributed storage:

class DynamicObject : public Base
{
public:

    DynamicObject(size_t size) :
        DynamicObject{std::unique_ptr<uint8_t>{new uint8_t[size]}, size}
    {

    }

private:

    DynamicObject(std::unique_ptr<uint8_t>&& dynamicStorage, size_t size) :
        Base{dynamicStorage.get(), size},
        dynamicStorage_{std::move(dynamicStorage)}
    {

    }

    std::unique_ptr<uint8_t> dynamicStorage_;
};

, () , Base - "" . . , - . , Base ( )...

- - Base DynamicObject unique_ptr Base - - /, :

  • Base,
  • (const) Base,

, DynamicObject - , ( ) Base ( ) - , /, "" ...

, - . - , RTOS, (https://github.com/DISTORTEC/distortos) - . Dynamic*.hpp Static*.hpp - https://github.com/DISTORTEC/distortos/tree/master/include/distortos

- , , - ? - DynamicObject ? , ?

+4
3

, , shared_ptr ( unique_ptr, ).

class Base
{
public:
    typedef void (*StorageDeleter)(void*);

    Base(void* storage, size_t storageSize, StorageDeleter deleter = nullptr) :
        storage_{storage},
        storageSize_{storageSize},
        deleter_{deleter}
    {
        // do something with the storage...
    }

    virtual ~Base()
    {
        // do something with the storage...
        if (deleter_) deleter_(storage_);
    }

    // member functions

private:
    void* storage_;
    size_t storageSize_;
    StorageDeleter deleter_;
};

/* no changes to this one */
template<size_t Size>
class StaticObject;

class DynamicObject : public Base
{
    static void array_deleter(void* p) { uint8_t* pExact = (uint8_t*)p; delete [] pExact; }
public:
    DynamicObject(size_t size) :
        DynamicObject{std::unique_ptr<uint8_t[]>{new uint8_t[size]}, size}
    {

    }

private:
    DynamicObject(std::unique_ptr<uint8_t[]>&& dynamicStorage, size_t size) :
        Base{dynamicStorage.get(), size, &DynamicObject::array_deleter},
    {
        dynamicStorage.release();
    }
};

, - - - DynamicObject .

std::unique_ptr, , Base ( ).

, ( + deleter) , std::unique_ptr<T, Deleter>. , :

class Base
{
    typedef void (*StorageDeleter)(void*);
    typedef std::unique_ptr<void, StorageDeleter> AutofreePtr;

public:
    Base(AutofreePtr&& storage, size_t storageSize) :
        storage_{std::move(storage)},
        storageSize_{storageSize}
    {
        // do something with the storage...
    }

    virtual ~Base()
    {
        // do something with the storage...
    }

    // member functions

private:
    AutofreePtr storage_;
    size_t storageSize_;
};

template<size_t Size>
class StaticObject : public Base
{
    static void no_delete(void*) {}
public:

    StaticObject() :
        Base{{&storage, &StaticObject::no_delete}, Size}
    {

    }

private:

    typename std::aligned_storage<Size>::type staticStorage_;
};

class DynamicObject : public Base
{
    static void array_deleter(void* p) { uint8_t* pExact = (uint8_t*)p; delete [] pExact; }
public:

    DynamicObject(size_t size) :
        DynamicObject{{new uint8_t[size], &DynamicObject::array_deleter}, size}
    {

    }
};
+1

, .

class Base
{
public:

    Base(void* storage, size_t storageSize) :
        storage_{storage},
        storageSize_{storageSize}
    {
    }

    virtual ~Base()
    {
    }

private:
    void* storage_;
    size_t storageSize_;
};

class Worker
{
    Worker(Base* storage)
        : storage(storage)
    {
        // do something with the storage
    }

    ~Worker()
    {
        // do something with the storage
    }

    Base* storage;
};

Base* storage = new FancyStorage;
Worker w(storage);
delete storage;

, , , , .

+1

It seems like you really want to change the hierarchy; a Basemust have some storage, and not, say, a StaticObject, having Base. This can be, for example, implemented using generics.

template< typename Storage >
class Base
{
    Storage storage; // Storage could be a private base class too
public:
    // Fix: use perfect forwarding
    template< typename T... >
    Base(T ...args):Storage(args...) { /* More initialization */ }

    ~Base() {
        // Still safe to use storage!
    }

    void set_all_storate_to_zero()
    {
        memset(storage.ptr(), 0, storage.size());
    }
};
+1
source

Source: https://habr.com/ru/post/1612326/


All Articles