0

I'm a bit confused w.r.t. the usage of delete and creation of new objects. Say I have a class which stores pointers to some other object in an array:

#include <array>
#include "someObject.hpp"
class someContainer() {
    std::array<someObject*, 10> objects;
    void set_object(int n);
}

What is the best way to create and store instances of someObject in the set_object function that does not involve the usage of smart pointers or other wrappers.

For example, I believe that...

void someContainer::set_object(n) {
    // Some check that 0 < n < 9
    ...
    //=====
    someObject object* = &someObject(constructor_param);
    objects[n] = object;
}

... will result in undefined behavior, because the destructor of object's reference is called upon the function's exit.

Would the correct way, then, be this?

void someContainer::set_object(n) {
    // Some check that 0 < n < 9
    ...
    //=====
    someObject object* = new someObject(constructor_param);
    objects[n] = object;
}

void someContainer::~someContainer() {
    int len = objects.size
    for (int i=0; i < len; i++) {
        delete objects[i];
    }

Or is there undefined behavior somewhere that I'm missing? (Also, I think that one shouldn't iterate through the array while also deleting objects from it, which is why I use the len index. Is this true?) I'm asking here because things compile and seem to work well, but I'm having a hard time really knowing.

1 Answer 1

1

This is an important issue in memory management: who owns an allocated value, i. e. who is responsible for freeing it. If you call

object[n] = new someObject(constructor_param);

and there is already an object stored there, the old object will remain somewhere on the heap, maybe without any pointers left to it, so it can't ever be freed again.

You would probably want your setter to look like this:

someObject *someContainer::set_object(n) {
    // Some check that 0 < n < 9
    ...
    //=====
    someObject *previous = objects[n];
    objects[n] = new someObject(constructor_param);
    return previous;
}

to signal to the user of the function that he needs to deal with the previous object, either delete it immediately or store it somewhere.

Also, you are completely free to delete the objects in a regular loop: remember that you are only freeing the objects that the array elements point to, and are not changing the array at all.

Sign up to request clarification or add additional context in comments.

4 Comments

return previous; - that is a good way to leak memory, unless you mark the function as [[nodiscard]] (C++17 and later only). Otherwise, you should return a std::unique_ptr<someObject> instead. Or, just don't return anything at all, just delete the previous object before exit. This is where making the array be std::array<std::unique_ptr<someObject>, 10> instead comes in handy.
@RemyLebeau Why unnecessarily restrict it to a unique_ptr? When using the GSL, one could annotate the return type as owner<someObject*> for clarity.
@Tau the point is, if you are going to return the previous object, it must be made clear that the caller is expected to take ownership of it, and that ownership should be automatic in case the caller chooses to ignore it. unique_ptr provides that guarantee. gsl::owner is just an anotation, it doesn't actually affect anything in code, and it certainly does not enforce automatic ownership.
Of course, unique_ptr is safest in the specific case, but as the question does not state whether the container is supposed to own its contents or not, this is the best one can do in the general case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.