Правилният начин за разпределяне и освобождаване на памет за масив от обекти, които ще използвам в 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(), който връща слой (вижте предишния typedef)
  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 от функцията init. Вероятно искате да прехвърлите и това отвън и да попълните функция. По принцип предпочитайте първо колекции, след това препратки, след това указатели с new/delete и накрая използвайте указатели с malloc/free само ако трябва.   -  person user694733    schedule 06.11.2014
comment
@user694733 Мисля, че мога напълно да премахна std::map, защото наистина нямам нужда от него (вижте отговора, който публикувах). Мисля, че просто трябва да използвам вектор от структури, където всяка структура се състои от ID и gpc_polygon. Този вектор ще бъде сортиран по ID. В края на програмата щях да повторя вектора и да освободя всички 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 Добавих кода, който получих в отговора си, ако се интересувате.   -  person Rauni Lillemets    schedule 06.11.2014


Отговори (1)


Мисля, че правя нещата по-сложни, отколкото трябва да бъдат.

Всъщност не се нуждая от std::map, за да съхранявам указателите. Вместо това мога да поискам полигоните от базата данни, така че те вече да са подредени по техните идентификатори. И тогава мога да съхранявам полигоните в статична структура (масив или вектор). Когато трябва да намеря елемент по неговия ID, просто ще използвам алгоритъм за двоично търсене, за да го намеря (което е логаритмично време като алгоритъма за търсене, използван от двоичното дърво, така или иначе).

И така, моят метод 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