Как правильно очистить элементы от векторов указателей объектов - PullRequest
0 голосов
/ 04 мая 2018

Я изучал динамическое распределение и наткнулся на этот вопрос в StackOverflow:

Выделение объектов, хранящихся в векторе?

В одном из ответов с голосованием объясняется, как вручную управлять памятью при использовании «Векторов указателей на объекты»: итерация по вектору, вызывающая удаление.

Мой вопрос о том, как удалить определенные элементы вектора, а не весь вектор. В приведенном ниже примере программы у меня есть вектор указателей на объекты. Представьте, что переменная x этих объектов уменьшается со временем ... Когда значение x объекта достигает числа (скажем, 3), я хочу удалить объект; ОДНАКО, я бы хотел, чтобы вектор всегда сортировался по значениям х объектов.

Проблема в том, что когда я вызываю delete для объектов, значения x которых достигают 3, объект удаляется, но там все еще есть указатель, указывающий на случайные ячейки памяти, и размер вектора остается неизменным.

Когда я перебираю вектор, печатающий значения x, элементы, для которых я вызвал delete, все еще там, но указывают на значения, такие как -53408995. Как избавиться от элемента указателя на вектор, а также от объекта?

Вызов стереть не вариант, потому что в моей реальной программе (не минимальный пример ниже) вектор постоянно сортируется по другим факторам, которые изменяют эквиваленты значения х. Я не могу отслеживать их индекс. Я хотел бы удалить как объект, так и элемент указателя при выполнении итерации по вектору для проверки значения x.

Пример:

#include <iostream>
#include <vector>

class A
{
public:
    A(int i) { x = i; }
    int x;
};

int main()
{
    std::vector<A*> Vec;

    Vec.push_back(new A{ 5 });
    Vec.push_back(new A{ 4 });
    Vec.push_back(new A{ 3 });

    std::cout << "Size before = " << Vec.size() << std::endl; // 3

    for (auto& a : Vec)
    {
        std::cout << a->x << std::endl;

        if (a->x == 3) { delete a; }
    }

    std::cout << "Size after = " << Vec.size() << std::endl; // Still 3!

    for (auto& a : Vec)
    {
        std::cout << a->x << std::endl; // Prints 5, 4 and a random memory location like -34528374
    }

    return 0;
}

Ответы [ 4 ]

0 голосов
/ 05 мая 2018

Немного изучив итераторы, я также придумал ответ, использующий необработанные указатели:

for (std::vector<A*>::iterator it = Vec1.begin(); it != Vec1.end(); )
{
    if ((*it)->x == 3)
    {
        delete * it;
        it = Vec1.erase(it);
    }
    else 
    {
        ++it;
    }
}

Я оставлю пост phön как ответ, так как умные указатели всегда должны быть предпочтительнее необработанных указателей, если они доступны.

0 голосов
/ 04 мая 2018
std::vector<std::unique_ptr<A>> vec;

Это будет обрабатывать нормальное удаление и выход через исключение.

0 голосов
/ 04 мая 2018

Вы упомянули в комментариях, что объекты будут иметь другие указатели на них. это звучит как std::shared_ptr<A> для меня. таким образом, вы можете иметь другие указатели на ваши A объекты без проблем с утечкой памяти. (std::shared_ptr поставляется с небольшой (!) Производительностью, но пока вам не стоит об этом беспокоиться). Кроме того, я изменил ваш отрывок, чтобы удалить / стереть ваш элемент из вектора. помните, что A объекты все еще живы, ЕСЛИ есть другие экземпляры, сохраняющие копию std::shared_ptr<A> (но это хорошо).

вот код:

#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>

class A
{
public:
    A(int i) { x = i; }
    int x;
};

int main()
{
    std::vector<std::shared_ptr<A>> Vec;
    Vec.emplace_back(std::make_shared<A>(5));
    Vec.emplace_back(std::make_shared<A>(4));
    Vec.emplace_back(std::make_shared<A>(3));

    std::cout << "Size before = " << Vec.size() << std::endl;

    Vec.erase(
        std::remove_if(std::begin(Vec),std::end(Vec), [](auto&& ptr){ return ptr->x == 3;}),
        std::end(Vec));

    std::cout << "Size after = " << Vec.size() << std::endl;
    for (auto&& a : Vec)
    {
        std::cout << a->x << std::endl;
    }

    return 0;
}
0 голосов
/ 04 мая 2018

в этом случае вы должны использовать std :: list контейнер

#include <iostream>
#include <list>

class A
{
public:
    A(int i) { x = i; }
    int x;
};

int main()
{
    std::list<A*> Vec;

    Vec.push_back(new A{ 5 });
    Vec.push_back(new A{ 4 });
    Vec.push_back(new A{ 3 });

    std::cout << "Size before = " << Vec.size() << std::endl;

    for (auto& a : Vec)
    {
        std::cout << a->x << std::endl;
    }

    Vec.remove_if([](A* a){
        bool status = (a->x == 3);
        if(status){
            delete a;
            return true;
        }else{
            return false;
        }
    });

    std::cout << "Size after = " << Vec.size() << std::endl;

    for (auto& a : Vec)
    {
        std::cout << a->x << std::endl;
    }

    return 0;
}

вывод:

Size before = 3
5
4
3
Size after = 2
5
4

Я переписываю твой код, добавляя некоторые улучшения

#include <iostream>
#include <list>

class A
{
public:
    A(const int& i):x(i) {} // so X is assigned to i in construction ( more efficient )
    int get_x() const {return x;}
private:
    int x; // x have to be private ( good practice )
};

int main()
{
    std::list<A> List; // A instead of A* so the process of construction / destruction is handled automatically

    List.emplace_back(5); // append element and constructed at the same time
    List.emplace_back(4); // see std::list for more details
    List.emplace_back(3);

    std::cout << "Size before = " << List.size() << std::endl;

    for (auto& a : List)
    {
        std::cout << a.get_x() << std::endl;
    }

    List.remove_if([](A a){ return a.get_x() == 3;});

    std::cout << "Size after = " << List.size() << std::endl;

    for (auto& a : List)
    {
        std::cout << a.get_x() << std::endl;
    }
    return 0;
}
...