简体   繁体   中英

How to add and remove object from vector<unique_ptr>...?

I have two functions like this. When I add a new object, everything is fine, but if I try to add an object that was already in this vector, the application just closes. I am running the unique_ptr destructor after removing from a vector. How to fix it?

void AddObject(GameObject* obj) {
    objects().push_back(std::move(unique_ptr<GameObject>(obj)));
}
        
void DeleteObject(GameObject* obj) {
    for (auto itr = objects().begin(); itr != objects().end(); ++itr) {
        if ((*itr)->ID == obj->ID) {
            objects().erase(std::move(itr));
            itr->~unique_ptr();
        }
    }
}

Everything about the code you have shown is wrong.

AddObject() should take a unique_ptr<GameObject> as input, not a raw GameObject* pointer, eg:

void AddObject(unique_ptr<GameObject> obj) {
    objects().push_back(std::move(obj));
}

Otherwise, you risk the caller being able to add the same object multiple times, and thus it would be managed by multiple unique_ptr s.

A better option would be to not let the caller create its own objects to begin with. Have the caller specify the object type and constructor arguments, but let AddObject() handle the actual creation, eg:

template<typename T, typename... Args>
void AddObject(Args&&... args) {
    objects().push_back(std::make_unique<T>(std::forward<Args>(args)...));
}

As for DeleteObject() , erase() ing an element from a vector invalidates iterators that refer to elements at/after the erasure point, including the iterator that is being erased . Your loop is not accounting for that. erase() returns an iterator to the element following the one that is being erased, so if you are not going to break your loop (can you have multiple objects with the same ID value?) then you need to use that iterator to continue your loop correctly.

Also, directly calling a destructor on an object that was not created with placement-new is undefined behavior . In this case, there is no reason to destroy the unique_ptr s manually at all. They will get destroyed automatically when they go out of scope when removed from the vector.

If you intend for DeleteObject() to search for a specific object, then it should look for that specific object pointer, not an ID:

void DeleteObject(GameObject* obj) {
    auto &objs = objects();
    for (auto itr = objs.begin(); itr != objs.end(); ++itr) {
        if (itr->get() == obj) {
            objs.erase(itr);
            return;
        }
    }
}

Alternatively:

void DeleteObject(GameObject* obj) {
    auto &objs = objects();
    auto itr = std::find_if(objs.begin(), objs.end(),
        [=](auto &ptr){ return ptr.get() == obj; }
    );
    if (itr != objs.end()) {
        objs.erase(itr);
    }
}

If you intend for DeleteObject() to search for an ID, then it should take an ID as input, not an object pointer:

void DeleteObject(int ID) {
    auto &objs = objects();
    for (auto itr = objs.begin(); itr != objs.end(); ++itr) {
        if ((*itr)->ID == ID) {
            itr = objs.erase(itr);
            return;
        }
    }
}

Alternatively:

void DeleteObject(int ID) {
    auto &objs = objects();
    auto itr = std::find_if(objs.begin(), objs.end(),
        [=](auto &ptr){ return ptr->ID == ID; }
    );
    if (itr != objs.end()) {
        objs.erase(itr);
    }
}

Or, if multiple objects can have the same ID (?):

void DeleteObject(int ID) {
    auto &objs = objects();
    for (auto itr = objs.begin(); itr != objs.end(); ) {
        if ((*itr)->ID == ID) {
            itr = objs.erase(itr);
        }
        else {
            ++itr;
        }
    }
}

Alternatively:

void DeleteObject(int ID) {
    auto &objs = objects();
    objs.erase(
        std::remove_if(objs.begin(), objs.end(),
            [=](auto &ptr){ return ptr->ID == ID; }
        ),
        objs.end()
    );

    // or, in C++20 and later:

    std::erase_if(objs,
        [=](auto &ptr){ return ptr->ID == ID; }
    );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM