Правильный способ выделить и освободить память для массива объектов, которые я буду использовать в std::map

У меня есть коллекция полигонов, которые я извлекаю из базы данных и хочу сохранить в двоичном дереве для быстрого доступа. В качестве бинарного дерева я использую std::map.

Я создал это решение, которое описано ниже, но я думаю, что это неправильно, потому что я не вызываю free() для освобождения памяти, выделенной malloc().

Мои вопросы (проблемы):

  1. Правильно ли использовать std::map, если мне нужно только вставлять и получать доступ к элементам этой карты? Я просто хочу быстро найти геометрию по ее идентификатору.
  2. В std::map я храню указатели на геометрии вместо хранения самих геометрий. Это хорошая идея? Раньше я пытался хранить сами геометрии, но потом понял, что std::map делает копию объекта, что создавало проблемы.
  3. В методе ConvertSpatial2GPC(..) я создаю объекты gpc_geometry, которые создают ссылки, которые я выпускаю в gpc_free_polygon(..). Но я не могу выпустить сам объект gpc_geometry, потому что у меня нет ссылки на него на тот момент.

Я использую следующие структуры:

typedef struct                      /* Polygon vertex structure          */
{
  double              x;            /* Vertex x component                */
  double              y;            /* vertex y component                */
} gpc_vertex;

typedef struct                      /* Vertex list structure             */
{
  int                 num_vertices; /* Number of vertices in list        */
  gpc_vertex         *vertex;       /* Vertex array pointer              */
} gpc_vertex_list;

typedef struct                      /* Polygon set structure             */
{
  int                 num_contours; /* Number of contours in polygon     */
  int                *hole;         /* Hole / external contour flags     */
  gpc_vertex_list    *contour;      /* Contour array pointer             */
} gpc_polygon;

typedef std::map<long, gpc_polygon*> layer;

Мой рабочий процесс выглядит следующим образом:

  1. Загружать элементы из базы данных
  2. Вызовите метод initializeLayer(), который возвращает слой (см. предыдущее определение типа)
  3. ... Работа со слоем...
  4. Вызовите метод freeLayer(), чтобы освободить память, используемую слоем.

Код для инициализации объектов геометрии:

layer initializeLayer() {
    //... database connection code

    //find the count of objects in database
    int count = ...

    //helper object for loading from database
    spatial_obj* sp_obj = NULL;

    //initialize a array to hold the objects
    gpc_polygon* gpc_objects;
    gpc_objects = (gpc_polygon*)malloc(sizeof(gpc_polygon) * count);

    layer myLayer;

    int i = 0;

    //... query database
    while(db.Fetch()) {
        id = db.GetLongData(0);
        db.GetSDO_Object(&sp_obj); //load from database
        db.ConvertSpatial2GPC(sp_obj, &gpc_mullad[i]); //convert polygon to GPC format
        //insert a pair (ID->pointer to the geometry)
        myLayer.insert(layer::value_type(id, &gpc_objects[i]);
        i++;
    }

    return layer;
}

Код для освобождения слоя:

void freeLayer(layer myLayer) {
    for (layer::iterator it = myLayer.begin(); it != myLayer.end(); ++it) {
        gpc_free_polygon(it->second); //frees the memory from this geometry object
    }
}

Код для освобождения объекта геометрии:

void gpc_free_polygon(gpc_polygon *p)
{
    int c;

    for (c= 0; c < p->num_contours; c++) {
        FREE(p->contour[c].vertex);

    FREE(p->hole);
    FREE(p->contour);
    p->num_contours= 0;
}

person Rauni Lillemets    schedule 05.11.2014    source источник
comment
+1 за четкие объяснения и хорошее оформление. Если бы все вопросы были такими.   -  person Jabberwocky    schedule 05.11.2014
comment
Это не C. Это больше похоже на C++. Таким образом, нет причин, по которым вы должны создавать список вручную с помощью malloc. Найдите std::vector.   -  person user694733    schedule 05.11.2014
comment
@ user694733, может быть, вы могли бы добавить свой комментарий в качестве ответа, чтобы мы могли его обсудить. Я так понимаю, что если бы я использовал вектор в методе initializeLayer(), то в конце этого метода вектор был бы уничтожен и память освободилась. Но это сделало бы недействительными указатели в std::map myLayer, которые я хочу использовать вне этого метода. Или я должен создать векторный объект вне метода initializeLayer() и передать его как параметр в метод inititalizeLauer(), чтобы он освободился только в конце работы?   -  person Rauni Lillemets    schedule 06.11.2014
comment
@Rauni Ну, общая картина вашего дизайна немного размыта, но я бы сказал, да. Передача вектора в функцию инициализации, вероятно, то, что вы хотите. Кроме того, вы возвращаете всю карту myLayer из функции инициализации. Вы, вероятно, хотите передать это также извне и заполнить функцию. В общем, сначала отдавайте предпочтение коллекциям, затем ссылкам, затем указателям с new/delete и, наконец, указатели с malloc/free используйте только в случае необходимости.   -  person user694733    schedule 06.11.2014
comment
@user694733 user694733 Я думаю, что могу полностью отказаться от std::map, потому что он мне действительно не нужен (см. ответ, который я опубликовал). Я думаю, что я должен просто использовать вектор структур, где каждая структура состоит из идентификатора и gpc_polygon. Этот вектор будет отсортирован по идентификатору. В конце программы я перебирал вектор и освобождал все gpc_polygons. Но нужно ли мне также освобождать память, выделенную для структур, или std::vector позаботится об этом автоматически?   -  person Rauni Lillemets    schedule 06.11.2014
comment
@Rauni Если вы храните объекты (вместо указателей на объекты) в векторе, тогда вектор уничтожит объекты, когда сам вектор будет уничтожен. Вот почему я сказал, что следует предпочитать коллекции, а не необработанные распределения; это упрощает управление памятью.   -  person user694733    schedule 06.11.2014
comment
@ user694733 Хорошо. Спасибо за ваши Коментарии.   -  person Rauni Lillemets    schedule 06.11.2014
comment
@user694733 user694733 Я добавил код, который получил в своем ответе, если вам интересно.   -  person Rauni Lillemets    schedule 06.11.2014


Ответы (1)


Я думаю, что делаю вещи более сложными, чем они должны быть.

Мне действительно не нужен std::map для хранения указателей. Вместо этого я могу запросить полигоны из базы данных, чтобы они уже были упорядочены по их идентификаторам. И тогда я могу хранить полигоны в статической структуре (массиве или векторе). Когда мне нужно найти элемент по его идентификатору, я просто использую алгоритм бинарного поиска, чтобы найти его (в любом случае это логарифмическое время, подобное алгоритму поиска, используемому бинарным деревом).

Итак, мой метод initializeLayer() вместо этого вернет массив или вектор, который я освобожу в конце программы.

РЕДАКТИРОВАТЬ: я обнаружил, что мне не нужно самостоятельно реализовывать бинарный поиск. Для этого есть класс: std::binary_search. Ссылка: алгоритм двоичного поиска

EDIT2: Итак, вот что у меня получилось:

Структура объекта

typedef struct {
    long id;
    gpc_polygon gpc_obj;
} object;

Структура слоя

typedef std::vector<muld*> layer;

Код для инициализации объектов геометрии:

layer initializeLayer() {
    //... database connection code

    //find the count of objects in database
    int count = ...

    //helper object for loading from database
    spatial_obj* sp_obj = NULL;
    object* object_ptr = NULL;

    layer myLayer;
    myLayer.reserve(count);

    int i = 0;

    //... query database
    while(db.Fetch()) {
        id = db.GetLongData(0);
        db.GetSDO_Object(&sp_obj); //load from database

        object_ptr = new object;
        object_ptr->id = id;
        db.ConvertSpatial2GPC(sp_obj, &object_ptr->gpc_obj);
        myLayer.push_back(object_ptr);
        i++;
    }

    return layer;
}

Код для освобождения слоя:

void freeLayer(layer myLayer) {
    for(std::vector<int>::size_type i = 0; i != myLayer.size(); i++) {
        gpc_free_polygon(&myLayer[i]->gpc_obj);
        delete myLayer[i];
    }
}

Код для бинарного поиска:

Я обнаружил, что std::binary_search возвращает только то, нашел ли он объект или нет. std::lower_bound() спешит на помощь!

//Create empty object for searching
object* searched_obj = new obj;
object* found_obj = NULL;
searched_obj->id = id;
layer::iterator it;
it = std::lower_bound(myLayer.begin(), myLayer.end(), searched_obj, obj_comparer);
if(it != kiht.end()) {
    found_obj = *it;

    if(found_obj->id != id) {
        //Error!
    }
} else {
    //Error!
}
//Release memory
delete searched_obj;

Функция сравнения объектов

bool obj_comparer(object *a, object  *b) {
    return a->id < b->id;
}
person Rauni Lillemets    schedule 06.11.2014