Тест на знання C++
Квітень 28th, 2009
Життя підкинуло цікаву ідею для перевірки практичних навичок C++ програміста. Таке собі тестове завдання.
Нижче наведено клас — зв’язаний список, призначений для зберігання всяких різних об’єктів. Клас поганий, нормальний С++ програміст такого собі дозволяти не повинен. Як ви гадаєте, що саме недобре у цьому класі?
-
class Linked_Lists
-
{
-
public:
-
Linked_Lists();
-
~Linked_Lists();
-
bool add(void *data);
-
bool remove_head();
-
bool first();
-
bool last();
-
bool next();
-
bool prev();
-
void* get_data();
-
bool is_empty();
-
int get_count();
-
-
protected:
-
struct Node
-
{
-
void *data;
-
Node *prev_node;
-
Node *next_node;
-
};
-
Node *head;
-
Node *tail;
-
Node *current;
-
int count;
-
};
Слід сказати, тут дещо специфічний дизайн, так уже сталося. Елементи додаються та видаляються зі списку за принципом FIFO. Водночас, доступний вказівник, який можна туди-сюди пересувати по списку, щоб отримати доступ до будь-яких даних. Втім, це не суттєво. Припустимо, так було потрібно для чогось.
Справжній недолік цього класу в іншому. У якості підказки варто глянемо на реалізацію деструктора:
-
Linked_Lists::~Linked_Lists()
-
{
-
Node *node;
-
-
if (head == 0) return;
-
-
first();
-
do
-
{
-
node = current;
-
next();
-
delete node->data;
-
delete node;
-
} while (current != 0);
-
}
Що ж саме у ньому не так?
Я не полінувався, зробив повну реалізацію цього класу і тестову програму. Все тут.
При компіляції можна отримати наступне попередження:
src/list.cpp:26: warning: deleting ‘void*’ is undefined
В тому то і полягає проблема. Операція delete відрізняється від C-шної функції free тим, що для при видаленні об’єктів додатково викликається деструктор. А випадку void* не відомо, який деструктор треба викликати (якщо взагалі треба). Тому програма, скомпільована g++ (за інші компілятори ручатися не буду) просто звільняє пам’ять, виділену під об’єкт, інші дії при цьому не виконуються.
У випадку використання стандартного типу (int який-небудь, або double) все буде нормально. І при використанні структур теж. А от якщо спробувати покласти до списку якийсь більш складний об’єкт, особливо зі зберіганням даних типу copy-on-write (QString, наприклад, або std::string), наслідки можуть бути абсолютно непередбачливі.
Виправити тут нічого не можна. Лише заново перепроектувати. Власне, спеціально для цього в C++ і було введено механізм шаблонів. Також можна виділити базовий клас і засовувати у список об’єкти його нащадків.
Написано за мотивами теми на одному форумі.
Категорії: C/C++ | Теґи:c/c++, програмування




