Реализуйте простой список, предоставляющий базовую гарантию. Уточните, что список требует от пользователей, чтобы обеспечить указанную гарантию.
Для начала рассмотрим вообще какие виды гарантий существует. Стандартная библиотека формирует такой набор гарантий безопасности, который позволяет разделить ответственность за создание надежно работающих программ между разработчиками стандартной библиотеки и ее пользователями:
- «Базовая гарантия (basic guarantee) для всех операций» — поддерживаются базовые инварианты стандартной библиотеки и нет утечек ресурсов, например памяти.
- «Сильная гарантия (strong guarantee) для ключевых операций» — в дополнение к базовой гарантии операции либо выполняются успешно, либо не приводят ни к какому эффекту. Эта гарантия обеспечивается для таких ключевых операций, как push_back(), одноэлементных insert() для списка list и uninitialized_copy().
- «Гарантия отсутствия исключений (nothrow guarantee) для некоторых операций» — дополнительно к базовой гарантии устанавливается, что некоторые операции не генерируют исключений. Эта гарантия дается для не большого числа простых операций типа swap() или pop_back().
Ну что ж я разработал простой список от его код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
#include <iostream> using std::cout; using std::endl; #include <cstdlib> using std::exit; struct list_node { int val; list_node* next; list_node(int v):val(v),next(0){} }; class list { list_node* first; list_node* last; list_node* current; public: list():first(0),last(0),current(0){} ~list() { if(first==0) cout <<"list is empty"<<endl; else { list_node* temp=first->next; delete first; while(temp!=0) { list_node* temp1=temp->next; delete temp; temp=temp1; } } } void add(int v) { if(current==0) { cout <<"spicok pyct"<<endl; current=new list_node(v); first=current; } else { cout <<"est6 elementu"<<endl; list_node* temp=current; current=new list_node(v); temp->next=current; } } void print() { if(first==0) cout <<"list is empty"<<endl; else { cout <<first->val<<' '; list_node* temp=first->next; while(temp!=0) { cout <<temp->val<<' '; temp=temp->next; } } } }; int main() { list l; l.add(3); l.add(4); l.print(); return 0; } |
Подумаем где может произойти исключение и появится утечка памяти? Ну в данном коде видно что утечки нигде не происходит, в принципе любая операция поддерживает базовую гарантию.
Я только что поэкспериментировал и вижу что если мы генерируем исключение из конструктора, то деструктор не вызывается, зато если мы генерируем исключение из любого другого метода класса, то обязательно вызывается деструктор, это очень хорошо. Что ж значит щас попробуем просто написать простой код для проверки в методе, у нас тут опасный один метод это add(), потому что только он выделяет память.
Ну ладно от код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
#include <iostream> using std::cout; using std::endl; #include <cstdlib> using std::exit; class ex{}; struct list_node { int val; list_node* next; list_node(int& v):val(v),next(0){} }; class list { list_node* first; list_node* last; list_node* current; public: list():first(0),last(0),current(0){} ~list() { cout <<"vuzov destructora"<<endl; if(first==0) cout <<"list is empty"<<endl; else { list_node* temp=first->next; delete first; while(temp!=0) { list_node* temp1=temp->next; delete temp; temp=temp1; } } } void add(int v) { try { throw ex(); if(current==0) { cout <<"spicok pyct"<<endl; current=new list_node(v); first=current; } else { cout <<"est6 elementu"<<endl; list_node* temp=current; current=new list_node(v); temp->next=current; } } catch(...) { cout <<"mu tyt nyjno poydal9t6 recyrcu"<<endl; //тут нужно оставить объект в хорошом состоянии //ну мы просто щас попробуем поудалять память какую нить //тут может только сгенерировать исключение new оператор ну в принципе это никаких последствий за собой //не должно повести. } } void print() { throw ex(); if(first==0) cout <<"list is empty"<<endl; else { cout <<first->val<<' '; list_node* temp=first->next; while(temp!=0) { cout <<temp->val<<' '; temp=temp->next; } } } }; int main() { try { list l; l.add(3); l.add(4); l.print();cout <<endl; } catch(ex&) { cout <<"crabotalo ex"<<endl; } return 0; } |
Ну что сказать, я честно не вижу где здесь может быть случайно затерянный ресурс, разве что если для пользовательских типов сделать, ну в принципе я думаю тут в коде не должно быть никакой утечки, только если мы явно где то сгенерируем исключение.
[youtube]https://www.youtube.com/watch?v=5oSAbsM8G90[/youtube]