Удалить элемент из vector и очистить память

при очистки памяти через итератор возникает исключение.

#include <stdint.h>
#include <vector> 
#include "Shared.h"

using namespace std;

const int RangeItems = 3;

struct Anchor
{
    uint16_t anchor_addr;
    float range[RangeItems];
    float dbm;

    Anchor(uint16_t addr) : anchor_addr(addr), dbm(0) {}
};


struct Link
{
   vector<Anchor> Anchors;

   Link()
   {
       Anchors.reserve(10); //зарезервировать сразу N элементов
   }
};

struct Link* init_link(uint16_t addr)
{
    Link* l = new Link();
    Anchor* a = new Anchor(addr);
    l->Anchors.push_back(*a);
    return l;
}


void add_To_End(struct Link* p, uint16_t addr)
{
    Anchor* a = new Anchor(addr);
    p->Anchors.push_back(*a);
}


Result delete_Anchor(Link* p, uint16_t addr)
{
    for (auto it = p->Anchors.begin(); it != p->Anchors.end(); ++it)
    {
        Anchor a = *it;
        if (a.anchor_addr == addr)
        {
            p->Anchors.erase(it);
            delete &a;               //ECXEPTION!!!!!
            return Succsess;
        }
    }
    return NotFound;
}

Тестирую работу с вектором

int main()
{   
    uwb_data= init_link(1);
    add_To_End(uwb_data, 2);
    add_To_End(uwb_data, 3);
    add_To_End(uwb_data, 4);
    auto res = delete_Anchor(uwb_data, 3);
}

При удалении элемента из вектора, хочу очистить занимаемую элементом Anchor память, но возникает Исключение.


Ответы (2 шт):

Автор решения: AR Hovsepyan

А получается вот что:

  1. Вызов uwb_data=init_link(1); создает Link и Anchor в свободной памяти с адресами соответственно XL1 иXA1, потом заносит копию Anchor в вектор объекта Link, который выделяет память YA1 для этого объекта.

То, что мы сохранили адрес XL1 в uwb_data, вселяет надежду, что мы потом можем освобождать эту память(хотя вы этого не пытаетесь делать).

  1. Дальше _ вызов add_To_End(uwb_data, 2); создает Anchor с адресом XA2 и заносит копию Anchor в вектор (объекта под uwb_data), который выделяет память YA2 для этого объекта.(в векторе уже два таких объекта)

  2. Вызов: delete_Anchor(uwb_data, 1) находит в uwb_data->Anchors два схожих объекта и удаляет их. Таким образом вектор освобождает память YA1 и YA2

delete &a; становится ошибкой, потому что a есть локальный объект. Но если вы пытаетесь каким то образом удалить объект вектора, то это тоже ошибка, потому что вектор уже его удалил.

А упала, В пропала. Что осталось на трубе? А остались XA1, XA2 и XL1. И, если, последний хранится в uwb_data, и после использования можно написать delete uwb_data, то информация о первых двух потеряна.

Есть много вариантов решения:

  • не создавать Anchor в свободной памяти, а просто переместить в вектор Anchors.emplace_back(Anchor(1))(наверное так лучше)
  • или после push_back(*a) освобождать память под а (ведь объект все равно уже не нужен, так как в векторе его копия)
  • Использовать умные указатели
  • или(плохой вариант) где то хранить указатели на объекты, созданных в динамической области нами(не распределителем вектора). Но нужно ли это?...
→ Ссылка
Автор решения: HolyBlackCat

Вектор, как и все стандартные контейнеры, хранит копии того, что вы в него добавляете.

Т.е. вот здесь:

Anchor* a = new Anchor(addr);
p->Anchors.push_back(*a);

в вектор попадает копия объекта, т.е. delete на ней вызывать не надо, это надо делать на оригинале, на a. Вы этого не делаете, поэтому здесь утечка памяти.

В этом же причина краша - вы зовете delete не на том объекте, который создали в куче, а на его копии, которую создал вектор.

Вообще, в современном C++ почти никогда не нужно использовать new и delete, если только вы не делаете свой аналог вектора или чего-то подобного.

Правильно добавлять элемент так: .emplace_back(addr); или .push_back(Anchor(addr)); (для вашего класса .push_back(addr); тоже сработает). При удалении delete не нужен, достаточно erase.

Link тоже нет смысла создавать через new. Просто Link l; ... return l;. И еще, раз вы пишете на С++ а не на С, то вместо struct Link достаточно писать просто Link (везде кроме самого определения структуры).

→ Ссылка