diff options
fix typos
1 files changed, 19 insertions, 14 deletions
| -rw-r--r-- | Seminar05/README.md | 33 |
diff --git a/Seminar05/README.md b/Seminar05/README.md index 7d2edaf..ce729b4 100644 --- a/Seminar05/README.md +++ b/Seminar05/README.md @@ -1,8 +1,8 @@ # Свързани списъци втора част ## Малко за представянията -Миналия семинар работихме с един класически начин да представим свързан списък, а именно просто чрез указател към първия му елемент. Днес ще разгледаме представянето на свързания списък като клас. -Разбира се, какво може да е толкова трудното на това да представим свързания списък като клас? Вместо функция *freeLinkedList* ще имаме деструктор, всички нужни функции ще ги направим като член-функции на новосъздадения ни клас и готово. +Миналия семинар работихме с един класически начин за представяне на свързан списък, а именно просто чрез указател към първия му елемент. Днес ще разгледаме представянето на свързания списък като клас. +Разбира се, какво може да е толкова трудното на това да представим свързания списък като клас? Правим подходящи конструктори, вместо функция *freeLinkedList* ще имаме деструктор, всички нужни функции ще ги направим като член-функции на новосъздадения ни клас и готово. Проблемът тук е, че доста често ни се налага да обхождаме списъка. Когато го представяхме чрез указател към първия му елемент нещата бяха лесни: ```cpp @@ -21,13 +21,13 @@ LinkedListNode* getHead(); ## Итератори Тук на помощ идват итераторите. Итераторите са обекти, които са отговорни за обхождане на контейнери (динамичен масив, свързан списък и тн). В *C++* голяма част от STL използва итератори за постигане на целите си. -Примерната реализация, с която ще работим, се намира във файла LinkedList.hpp. Представяме свързания списък с два указателя където +Примерната реализация, с която ще работим, се намира във файла LinkedList.hpp. Представяме свързания списък с два указателя, където * head сочи към първия (най-левия) елемент * tail сочи към последния (най-десния) елемент -* Също така пазим още една допълнителна променлива от тип size_t за да пазим дължината на списъка. +* Също така пазим още една допълнителна променлива от тип size_t за дължината на списъка. Разгледайте реализацията, особено алгоритмите за добавяне и премахване в началото и в края. Забележете, че всеки път разглеждаме три случая: * Дали листа е празен @@ -35,7 +35,7 @@ LinkedListNode* getHead(); * Дали листа има повече от един елемент Вече имаме някаква примерна реализация на свързан списък. Хайде да си направим итератор и да видим колко точно сложно ще е. -Ще направим итератора **вътрешен клас за класът LinkedList**, който ще е **публичен**. +Ще направим итератора **вътрешен и публичен клас за класа LinkedList**. ```cpp // ... public: @@ -74,7 +74,7 @@ Iterator& operator++() { return *this; } ``` -Супер, вече имаме ++it. Но ще е добре и да си направим it++, не знаем за какво ще ни потрябва. +Супер, вече имаме ++it. Но ще е добре и да си направим it++, не знаем за какво ще ни потрябва. ```cpp // Защо този път връщаме Iterator а не Iterator&? @@ -88,7 +88,6 @@ Iterator operator++(int) { ``` Напомняме, че разликата между префиксния и постфиксния operator++ е просто един "служебен" int като аргумент на постфиксния. - Супер, вече можем да се движим, но това движение става безполезно ако нямаме начин да видим каква информация имаме в currentNode. Тази задача по конвенция се изпълнява от operator*. Супер, нека го предефинираме: ```cpp T& operator*() { @@ -127,7 +126,7 @@ bool operator!=(const Iterator& other) const { friend class LinkedList; /// ... ``` -Вече сме готови да проверим дали нашия метод работи. +Вече сме готови. Нека да проверим дали нашия метод работи: ```cpp int main() { LinkedList<int> ll; @@ -139,8 +138,9 @@ int main() { std::cout << *it << " "; } ``` + ## Const и итераторите -нека разгледаме следния код +Нека разгледаме следния код: ```cpp int main() { const LinkedList<int> ll; @@ -170,9 +170,9 @@ int main() { std::cout << ll.back(); } ``` -В случая, въпреки че ll е const с отново променяме стойността му. +В случая, въпреки че ll е const, отново променяме стойността му. Но защо? -В случая beg е константен. Той е съставен от един пойнтър, който обаче не е константен. В някакъв смисъл това, че стойността на паметта към която сочи пойнтъра се променя не чупи константността на итератора. За това ще се наложи да дефинираме още един клас, ConstIterator! +В случая **beg е константен**. Той е съставен от един пойнтър, който обаче **не е константен**. В някакъв смисъл **това, че стойността на паметта към която сочи пойнтъра се променя не чупи константността на итератора**. За това ще се наложи да дефинираме още един клас, ConstIterator! ```cpp class ConstIterator { private: @@ -215,15 +215,18 @@ class ConstIterator { Реализираме следните: ```cpp +// Връща константен итератор към началото ConstIterator cbegin() const { return ConstIterator(head); } +// Връща константен итератор към края ConstIterator cend() const { return ConstIterator(nullptr); } ``` -Сега ако искам да имам итератор за константен списък просто ще създавам ConstIterator. Примерно, искаме да итерираме константен свързан списък: +Сега, ако искам да имам итератор за константен списък, просто ще създавам ConstIterator. Примерно, искаме да итерираме константен свързан списък: + ```cpp void iterate(const LinkedList<int>& ll){ for(LinkedList<int>::ConstIterator it = ll.cbegin(); it != ll.cend(); ++it) { @@ -233,14 +236,15 @@ void iterate(const LinkedList<int>& ll){ ``` ## auto -Най - вероятно вече сте чували за ключовата дума *auto*. Чрез нея карате компилатора сам да прецени какъв тип да използва за променливата. Тя е удобна когато имате дълги типове. Прави кодът по - четим и разбираем. Но като всяко хубаво нещо **с auto не е добре да се прекалява.** +Най - вероятно вече сте чували за ключовата дума *auto*. Чрез нея карате компилатора сам да прецени какъв тип да използва за променливата. Тя е удобна когато имате дълги типове. Прави кодът по-четим и разбираем. Като всяко хубаво нещо обаче **с auto не е добре да се прекалява.** ```cpp +// Автоматично разбирам типа на it for(auto it = ll.begin(); it != ll.end(); ++it) { //... } -auto x = 5; // х е променлива от тип int +auto x = 5; // х е променлива от тип int const auto y = 10u; // y e променлива от тип const unsigned auto f = 10.0f; // f е променлива от тип float const auto& r = f; // r е променлива от тип const float& @@ -251,6 +255,7 @@ auto it = ll.begin(); // it e променлива от тип std::list<int>::i const auto its = ll.end(); // it е променлива от тип ? auto itss = ll.cend(); // it е променлива от тип ? ``` + ## range based for loops От c++11 езикът ни предлага начин, чрез който да пишем подобни for цикли още по - бързо. ```cpp |
