Free memory created by instantiating base class with parent type that has protected destructor

142 Views Asked by At

I think this is probably a really simple question, but I am as much a C++ developer as the guys at the walmart meat counter are butchers.

Say I have:

class Parent{
    protected:
        ~Parent(){};
};

class ChildA : public Parent{

};

struct Container{
    Parent *child;

    //Tried this, causes: munmap_chunk(): invalid pointer
    ~Container(){
        delete &child;
    }
};

Container MakeStruct(){
    ChildA child = *new ChildA();
    return Container { .child = &child };
}

int main()
{
    Container cont = MakeStruct();

    //Tried this, causes: "Parent::~Parent()" is inaccessible
    delete cont.child;
}

As you can see, I am using new to create a ChildA because I need it to outlive the MakeStruct function. So I know this means that child (in MakeStruct) will be placed on the heap, and that I am responsible for deleting it. But I can't seem to delete it. I can't change the type of child in Container because I need it to accept both a ChildA and a ChildB. It sort of makes sense, considering the destructor of Parent is protected. I have no control over Parent or either Child. They are part of an external library.

I case it helps, the actual code I'm working with is a library called ArduinoJson.

I am trying to return either a DynamicJsonDocument or a StaticJsonDocument<T> from a function, wrapped in a struct taking a JsonDocument:

Here is the struct that contains the JsonDocument:

struct rpc_handler_result_t {
    esp_err_t result;
    JsonDocument *response;
};

which is returned from:

{
    const int len = JSON_OBJECT_SIZE(1);
    StaticJsonDocument<len> reply = *new StaticJsonDocument<len>;

    reply["MaxOutput"] = Configuration::GetMaxOutputAmps();

    rpc_handler_result_t res = {
        .result = ESP_OK,
        .response = reply
    };

    return res;
}
1

There are 1 best solutions below

5
On BEST ANSWER

When you eventually call delete, you must pass it precisely the value you got from new. So you must store the value that new returns somewhere. But look at your call to new -- it dereferences that value and never stores it anywhere. So how can you call delete?!

 Container MakeStruct(){
    ChildA child = *new ChildA(); // The value returned by new is lost here
    return Container { .child = &child }; // child is a local object here
}

This is all wrong. You create a new object by calling new. But you do not store the value new returned anywhere. Now, child is a temporary object whose value was copy-constructed from the value of the object you allocated with new and leaked.

Then, you save the address of the temporary child object you created on the stack. But that object won't exist after you return.

What you wanted to do was save the value that new returned. But you got rid of it immediately by dereferencing it and never saving it.

So:

  1. You must store the value that new returns so you can delete it later.
  2. Don't try to pass addresses of local objects out of functions.

You wanted .child = new ChildA(), setting the child member to be a pointer to the object created by new, not a pointer to some temporary, local object. You can save the value new returns in a temporary if you want, just make sure .child gets the value new returned and not any other value.

Also:

Parent *child;

//Tried this, causes: munmap_chunk(): invalid pointer
~Container(){
    delete &child;
}

This is wrong too. What type is &child? Is &child something you got from new? This should be delete child;.