Oberon space
General Category => Общий раздел => Тема начата: Jordan от Сентябрь 16, 2018, 07:38:37 pm
-
Приветствую!
Написал прогу которая генерирует типизированные контейнеры для си. По ходу написания всплыл вопрос о деструкторах.
Код на основе которого генерируется контейнер
typedef struct NAME_vector
{
size_t total;
size_t pos;
TYPE * data;
} NAME_vector;
inline NAME_vector * NAME_vector_new(size_t count)
{
NAME_vector * p = (NAME_vector*)calloc(1, sizeof(NAME_vector));
p->total = count;
p->pos = 0;
p->data = (TYPE*)calloc(p->total, sizeof(TYPE));
return p;
}
inline void NAME_vector_clear(NAME_vector * src)
{
src->pos = 0;
}
inline size_t NAME_vector_size(NAME_vector * src)
{
return src->pos;
}
inline size_t NAME_vector_capacity(NAME_vector * src)
{
return src->total;
}
inline TYPE * NAME_vector_get(NAME_vector * src, size_t idx)
{
assert(idx <= NAME_vector_size(src));
return &src->data[idx];
}
inline void NAME_vector_free(NAME_vector * src)
{
for (size_t i = 0; i < NAME_vector_size(src); i++)
{
TYPE * j = NAME_vector_get(src, i);
NAME_destroy(j);
}
free(src->data);
free(src);
}
/*
inline void NAME_vector_free(NAME_vector * src)
{
free(src->data);
free(src);
}
*/
inline void NAME_vector_set(NAME_vector * src, size_t idx, TYPE val)
{
memcpy(&src->data[idx], &val, sizeof(TYPE));
}
inline void NAME_vector_resize(NAME_vector * src, size_t count)
{
if (count > src->total)
{
src->total = count;
src->data = (TYPE*)realloc(src->data, src->total * sizeof(TYPE));
}
}
inline void NAME_vector_push_back(NAME_vector * src, const TYPE val)
{
if (src->pos + 1 > src->total)
{
src->total = src->total * 2;
src->data = (TYPE*)realloc(src->data, src->total * sizeof(TYPE));
}
memcpy(&src->data[src->pos], &val, sizeof(TYPE));
src->pos++;
}
Код деструктора для вектора
inline void critter_ptr_vector_free(critter_ptr_vector * src)
{
for (size_t i = 0; i < critter_ptr_vector_size(src); i++)
{
critter * j = critter_ptr_vector_get(src, i);
critter_destroy(j);
}
free(src->data);
free(src);
}
Сам код данного объекта
#define critter_max (32)
typedef struct
{
char * name;
uint16_t base [critter_max];
uint16_t bonus[critter_max];
} critter;
void critter_init(critter * src, const char * name)
{
src->name = strdup(name);
for (size_t i = 0; i < critter_max; i++)
{
src->base = 5;
}
for (size_t j = 0; j < critter_max; j++)
{
src->bonus[j] = 7;
}
}
void critter_destroy(critter * src)
{
free(src->name);
}
Вроде все понятно. При уничтожении ветора все объекты будут корректно уничтожены, главное реализовать деструктор для хранимого объекта.
Но если хранить не объекты а указатели на объекты.
typedef struct NAME_ptr_vector
{
size_t total;
size_t pos;
TYPE ** data;
} NAME_ptr_vector;
inline NAME_ptr_vector * NAME_ptr_vector_new(size_t count)
{
NAME_ptr_vector * p = (NAME_ptr_vector*)calloc(1, sizeof(NAME_ptr_vector));
p->total = count;
p->pos = 0;
p->data = (TYPE**)calloc(p->total, sizeof(TYPE*));
return p;
}
inline void NAME_ptr_vector_clear(NAME_ptr_vector * src)
{
src->pos = 0;
}
inline size_t NAME_ptr_vector_size(NAME_ptr_vector * src)
{
return src->pos;
}
inline size_t NAME_ptr_vector_capacity(NAME_ptr_vector * src)
{
return src->total;
}
inline TYPE * NAME_ptr_vector_get(NAME_ptr_vector * src, size_t idx)
{
assert(idx <= NAME_ptr_vector_size(src));
return src->data[idx];
}
inline void NAME_ptr_vector_free(NAME_ptr_vector * src)
{
for (size_t i = 0; i < NAME_ptr_vector_size(src); i++)
{
TYPE * j = NAME_ptr_vector_get(src, i);
NAME_destroy(j);
}
free(src->data);
free(src);
}
/*
inline void NAME_ptr_vector_free(NAME_ptr_vector * src)
{
free(src->data);
free(src);
}
*/
inline void NAME_ptr_vector_set(NAME_ptr_vector * src, size_t idx, TYPE * val)
{
src->data[idx] = val;
}
inline void NAME_ptr_vector_resize(NAME_ptr_vector * src, size_t count)
{
if (count > src->total)
{
src->total = count;
src->data = (TYPE**)realloc(src->data, src->total * sizeof(TYPE*));
}
}
inline void NAME_ptr_vector_push_back(NAME_ptr_vector * src, TYPE * val)
{
if (src->pos + 1 > src->total)
{
src->total = src->total * 2;
src->data = (TYPE**)realloc(src->data, src->total * sizeof(TYPE*));
}
src->data[src->pos] = val;
src->pos++;
}
Генерируется вектор указателей. Код почти аналогичен.
Вопрос в том, что в С++ деструктор для указателей объектов которые находятся в векторе не вызывается автоматически. Для чего это сделано?
Стоит ли в моей реализации поступить так же?
-
Для этого сделаны умные указатели (то есть в деструкторе уже указателя вызывается деструктор объекта на который он указывает): https://en.cppreference.com/w/cpp/memory/shared_ptr
https://en.cppreference.com/w/cpp/memory/unique_ptr
Умный указатель это не что-то вшитое в язык, это обычный класс, такой в плюсах можно сделать самостоятельно.
-
Понял. Без умного указателя деструктор объектов в векторе не вызывается.
Сначала нужно удалить вручную память по указателям, после чего при выходе из области видимости вектора, он уничтожается.
Получается, что к примеру указатель на объект может быть и в другом векторе или контейнере и при уничтожении вектора, указатель в другой контейнере будет не валиден.
Тогда, нужно в моем случае сделать два контейнера, с возможностью вызова деструктора и без.
unique_ptr_vector деструкор вызовется автоматически при уничтожении вектора, вручную контролировать уникальность, так как в си такую проверку не сделать
ptr_vector массив указателей при уничтожении вектора, проходить в ручную по массиву и вызывать деструктор