07 Containers

522 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
522
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
4
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

07 Containers

  1. 1. Бублик Володимир Васильович Програмування - 2 Лекція 7 . Об'єктне програмування. Контейнерні класи Лекції для студентів 2 курсу
  2. 2. Повторення: агрегати <ul><li>Агрегати містять в собі інші об'єкти у вигляді атрибутів або відсилок чи указників на них </li></ul><ul><li>Звичайно атрибути закриті для доступу зовні </li></ul><ul><li>Доступ до атрибутів та заміна їх значень виконуються відповідно за допомогою селектора та модифікатора </li></ul><ul><li>Атрибути можуть бути зв'язані одне з одним, а тому модифікатор кожного з атрибутів повинен зберігати повноцінність інших </li></ul>
  3. 3. Контейнер <ul><li>Агрегат, здатний містити, як правило, значну кількість схожих (однотипних) об'єктів </li></ul><ul><li>Спосіб агрегування ― композиція </li></ul><ul><li>Види контейнерів: масив, послідовність, список, черга, стек, дек </li></ul><ul><li>Спосіб доступу: довільний або послідовний </li></ul>
  4. 4. Масив <ul><li>В стилі С </li></ul><ul><li>T ar [m] = {a 0 , a 1 , …, a m-1 }; </li></ul><ul><li>T * p = new T [n]; </li></ul><ul><li>Проблеми: </li></ul><ul><li>Неспроможність відрізнити указник одиночного даного від масиву </li></ul><ul><li>Нездатність контролювати вихід індексу за межі масиву </li></ul><ul><li>Перевага </li></ul><ul><li>Довільний доступ до елементу масиву за індексом </li></ul>
  5. 5. Приклад 1 <ul><li>double a[10] = {0,1,2,3,4,5,6,7,8,9}; </li></ul><ul><li>cout<< sizeof (a)/ sizeof ( double )<<endl; //10 </li></ul><ul><li>g(a); </li></ul><ul><li>// Масив забув свій розмір </li></ul><ul><li>void g( double a[]) </li></ul><ul><li>{ </li></ul><ul><li>cout<<&quot;Can't calculate size&quot;<<endl; </li></ul><ul><li>cout<< sizeof (a)/ sizeof ( double )<< endl; //0 </li></ul><ul><li>} </li></ul>
  6. 6. Приклад2 <ul><li>double a[10] = {0,1,2,3,4,5,6,7,8,9}; </li></ul><ul><li>cout<< sizeof (a)/ sizeof ( double )<<endl; //10 </li></ul><ul><li>g(a); </li></ul><ul><li>// Указник не знає, що він масив </li></ul><ul><li>void g( double * a) </li></ul><ul><li>{ </li></ul><ul><li>cout<<&quot;Can't calculate size&quot;<<endl; </li></ul><ul><li>cout<< sizeof (a)/ sizeof ( double )<< endl; //0 </li></ul><ul><li>} </li></ul>
  7. 7. Приклад3 <ul><li>void h( double * a, int n) </li></ul><ul><li>{ </li></ul><ul><li>for ( int i=0; i<n; ++i) </li></ul><ul><li>a[i]=i; </li></ul><ul><li>} </li></ul><ul><li>// Функція приймає адресу за масив </li></ul><ul><li>double a= 1; </li></ul><ul><li>h(&a, 1000); </li></ul>
  8. 8. Створення/видалення <ul><li>Як гарантувати парність new/delete new [] /delete [] ? </li></ul><ul><li>double * u = new double (12345); </li></ul><ul><li>double * v = new double [12345]; </li></ul><ul><li>delete [] u; </li></ul><ul><li>delete v; </li></ul>
  9. 9. Клас масивів <ul><li>class Array </li></ul><ul><li>{ </li></ul><ul><li>public : </li></ul><ul><li>explicit Array (size_t); </li></ul><ul><li>~Array(); </li></ul><ul><li>// Селектор-модифікатор </li></ul><ul><li>double & operator [] (size_t index); </li></ul><ul><li>// Модифікатор </li></ul><ul><li>const double & operator [] (size_t index) const ; </li></ul><ul><li>// Розмірність </li></ul><ul><li>size_t size() const; </li></ul>
  10. 10. Масив чисел <ul><li>// Клас для обробки помилкових ситуацій </li></ul><ul><li>class BadArray; </li></ul><ul><li>private : </li></ul><ul><li>// Атрибут розмірності </li></ul><ul><li>size_t _size; </li></ul><ul><li>// Власне масив </li></ul><ul><li>double * _pElem; </li></ul><ul><li>// Н е визначені для масивів : </li></ul><ul><li>Array ( const Array&); </li></ul><ul><li>Array& operator = ( const Array&); </li></ul><ul><li>}; </li></ul>
  11. 11. Конструктор масиву <ul><li>Array::Array (size_t sz): </li></ul><ul><li>_size(sz), _pElem ( new double [_size]) </li></ul><ul><li>{ </li></ul><ul><li>if (_pElem == 0) </li></ul><ul><li>throw BadArray (&quot;No more space for you&quot;); </li></ul><ul><li># ifndef NDEBUG </li></ul><ul><li>cout<<&quot;Array created [&quot;<<_size<<‘]'<<endl; </li></ul><ul><li># endif </li></ul><ul><li>return ; </li></ul><ul><li>} </li></ul>
  12. 12. Деструктор <ul><li>Розробник класу гарантує симетричність видалення створенню </li></ul><ul><li>Array::~Array() </li></ul><ul><li>{ </li></ul><ul><li># ifndef NDEBUG </li></ul><ul><li>cout<<&quot;Array of size (&quot;<<_size<<&quot;) deleted&quot;<<endl; </li></ul><ul><li># endif </li></ul><ul><li>delete [] _pElem; </li></ul><ul><li>} </li></ul>
  13. 13. Ініціалізація <ul><li>Чим наповнено масив дійсних чисел після його створення? ― Сміттям </li></ul><ul><li>Array ar(10); </li></ul><ul><li>for ( int i=0; i< 10 ; ++i) </li></ul><ul><li>cout<<ar[ i ]<<‘,’ </li></ul><ul><li>cout<<endl; </li></ul>
  14. 14. Масив об'єктів <ul><li>class ArrayOfPoints </li></ul><ul><li>{ </li></ul><ul><li>public : </li></ul><ul><li>explicit Array (size_t); </li></ul><ul><li>~Array(); </li></ul><ul><li>Point& operator [] (size_t index); </li></ul><ul><li>const Point & operator [] (size_t index) const ; </li></ul><ul><li>size_t size() const; </li></ul><ul><li>private : </li></ul><ul><li>size_t _size; </li></ul><ul><li>Point * _pElem; </li></ul><ul><li>} </li></ul>
  15. 15. Конструктор масиву <ul><li>ArrayOfPoints::ArrayOfPoints (size_t sz): </li></ul><ul><li>_size(sz), </li></ul><ul><li>// Виклик _ size конструкторів </li></ul><ul><li>_pElem ( new Point [_size]) </li></ul><ul><li>{ </li></ul><ul><li>if (_pElem == 0) </li></ul><ul><li>throw BadArray (&quot;No more space for you&quot;); </li></ul><ul><li># ifndef NDEBUG </li></ul><ul><li>cout<<&quot;Array created [&quot;<<_size<<‘]'<<endl; </li></ul><ul><li># endif </li></ul><ul><li>return ; </li></ul><ul><li>} </li></ul>
  16. 16. Конструктор масиву <ul><li>NB. Клас елемента масиву повинен мати конструктор без параметрів </li></ul>// Ініціалізація // без проблем class Foo { public : Foo(); }; // Ініціалізація // неможлива class Foox { public : Foox ( int x); };
  17. 17. Конструктор масиву <ul><li>NB. Клас елемента масиву повинен мати конструктор без параметрів </li></ul>// Ініціалізація // без проблем class Foo { public : Foo(); }; // Знову // без проблем class Foox { public : Foox ( int x=0); };
  18. 18. Індексування: селектор/модифікатор <ul><li>Небезпечна, взагалі кажучи, операція, оскільки фактично відкриває повний доступ до закритої частини класу </li></ul><ul><li>Для контейнерів прийнятна, оскільки контейнери не мають обмежень на призначені для зберігання дані </li></ul><ul><li>double & Array:: operator [] (size_t index) </li></ul><ul><li>{ </li></ul><ul><li>if (index<0 || index>=_size) </li></ul><ul><li>throw BadArray(&quot;Bad index: &quot;, index); </li></ul><ul><li>return _pElem [index]; </li></ul><ul><li>} </li></ul>
  19. 19. Чистий селектор <ul><li>Призначений для доступу передусім до контейнера, переданого параметром за допомогою сталої відсилки </li></ul><ul><li>double Array:: operator [] (size_t index) const </li></ul><ul><li>{ </li></ul><ul><li>if (index<0 || index>=_size) </li></ul><ul><li>throw BadArray(&quot;Bad index: &quot;, index); </li></ul><ul><li>return _pElem [index]; </li></ul><ul><li>} </li></ul>
  20. 20. Обробник помилок <ul><li>class Array::BadArray </li></ul><ul><li>{ </li></ul><ul><li>private : </li></ul><ul><li>string _reason; </li></ul><ul><li>size_t _index; </li></ul><ul><li>public : </li></ul><ul><li>BadArray(string reason=&quot;&quot;, size_t index=0) ; </li></ul><ul><li>~BadArray(); </li></ul><ul><li>}; </li></ul><ul><li>ostream& operator << </li></ul><ul><li>(ostream &os, const Array ::BadArray & seq); </li></ul>
  21. 21. Проблема наповненості <ul><li>Масив як контейнер не здатний зберігати менше даних, ніж його ємність: індексування контролює лише вихід індексу за межі масиву, але не перевіряє, чи був елемент попередньо визначений </li></ul>
  22. 22. Обмежена послідовність <ul><li>Розмістимо n членів послідовності s на перших n місцях масиву a розмірності n </li></ul>
  23. 23. Діаграма класів <ul><li>Масив агрегований як компонент послідовності </li></ul>
  24. 24. Послідовність. Версія 1 <ul><li># include <limits> </li></ul><ul><li>class Sequence </li></ul><ul><li>{ </li></ul><ul><li>public : </li></ul><ul><li>explicit Sequence(size_t capacity =_seqBlock); </li></ul><ul><li>~Sequence(); </li></ul><ul><li>Sequence& putAfter </li></ul><ul><li>( double elem, </li></ul><ul><li>size_t ind=std::numeric_limits<size_t>::max()); </li></ul><ul><li>Sequence& remove </li></ul><ul><li>(size_t ind=std::numeric_limits<size_t>::max()); </li></ul>
  25. 25. Послідовність. Версія 1 <ul><li>const double & operator [] (size_t index) const; </li></ul><ul><li>double & operator [] (size_t index); </li></ul><ul><li>// Поточна ємність </li></ul><ul><li>size_t capacity() const ; </li></ul><ul><li>// Поточний розмір </li></ul><ul><li>size_t size() const ; </li></ul><ul><li>// Чи порожня послідовність? </li></ul><ul><li>bool empty() const ; </li></ul><ul><li>// Очистити послідовність </li></ul><ul><li>void clear(); </li></ul>
  26. 26. Послідовність. Версія 1 <ul><li>class BadSeq ; </li></ul><ul><li>private : </li></ul><ul><li>size_t _size; </li></ul><ul><li>Array _seqArray; </li></ul><ul><li>// Стандартний розмір блока контейнера </li></ul><ul><li>static size_t _seqBlock; </li></ul><ul><li>Sequence ( const Sequence&); </li></ul><ul><li>Sequence& operator =(const Sequence&); </li></ul><ul><li>}; </li></ul>
  27. 27. Конструктор/деструктор <ul><li>// Створити порожню послідовність </li></ul><ul><li>Sequence::Sequence(size_t capacity): </li></ul><ul><li>_size(0),_seqArray(capacity) </li></ul><ul><li>{ </li></ul><ul><li>return ; </li></ul><ul><li>}; </li></ul><ul><li>// Деструктору поки що роботи немає. Чому? </li></ul><ul><li>Sequence ::~ Sequence () </li></ul><ul><li>{ </li></ul><ul><li>return ; </li></ul><ul><li>}; </li></ul>
  28. 28. Делегування індексувань <ul><li>double & Sequence:: operator [] (size_t index) const </li></ul><ul><li>{ </li></ul><ul><li>if (empty()) </li></ul><ul><li>throw BadSeq(“Empty sequence&quot;); </li></ul><ul><li>if (index>_size) </li></ul><ul><li>throw BadSeq (“Non existing element&quot;); </li></ul><ul><li>// Делегування доступу за індексом </li></ul><ul><li>return _seqArray[index]; </li></ul><ul><li>} </li></ul>
  29. 29. Долучення до послідовності <ul><li>Sequence & Sequence :: insert ( double elem , size_t index ) </li></ul><ul><li>{ </li></ul><ul><li>// Замовчуване значення індексу: дописати в кінець </li></ul><ul><li>if (_ size < index ) </li></ul><ul><li>index=_size; </li></ul><ul><li>// Контейнер переповнено </li></ul><ul><li>if ( _size + 1 > capacity()) </li></ul><ul><li>throw BadSeq(“No more space&quot;); </li></ul><ul><li>_size++; </li></ul>
  30. 30. Долучення до послідовності <ul><li>// Зсуваємо залишок послідовності праворуч </li></ul><ul><li>for (size_t i =_size-1; i > index; i--) </li></ul><ul><li>_seqArray[i]= _seqArray[i-1]; </li></ul><ul><li>// Заносимо новий елемент </li></ul><ul><li>_seqArray [index] = elem; </li></ul><ul><li>return * this ; </li></ul><ul><li>} </li></ul>
  31. 31. Необмежена послідовність <ul><li>Можливість замінити менший контейнер більшим за домогою закритого методу enlarge() досягається за рахунок агрегування масиву указником </li></ul>
  32. 32. Збільшення контейнера <ul><li>void Sequence :: enlarge ( int times) </li></ul><ul><li>{ </li></ul><ul><li>Array *newAr= new Array(times*_seqBlock+capacity()); </li></ul><ul><li>if (newAr == 0) throw (“No more space&quot;); </li></ul><ul><li>assert(newArray != 0); </li></ul><ul><li>for (size_t i=0; i<_size; i++) </li></ul><ul><li>(*newAr)[i] = (*_seqArray)[i]; </li></ul><ul><li>delete _seqArray; </li></ul><ul><li>_seqArray = newArray; </li></ul><ul><li>return ; </li></ul><ul><li>} </li></ul>
  33. 33. Вправа <ul><li>Спроектувати послідовність на базі масиву, в якій можна буде зберігати об'єкти без попереднього виклику конструкторів без параметрів для заповнення всього масиву </li></ul>
  34. 34. Повторення <ul><li>Два способи організації циклу над масивом </li></ul><ul><li>const int n =4; </li></ul><ul><li>int a[n]; </li></ul><ul><li>// цикл за індексом </li></ul><ul><li>for ( int i=0; i<n; ++i) </li></ul><ul><li>a[i]=i; </li></ul><ul><li>// цикл за ітератором </li></ul><ul><li>int *pcurrent = a; </li></ul><ul><li>int * const pend = a + n; </li></ul><ul><li>while (pcurrent!=pend) </li></ul><ul><li>cout<<*(pcurrent++)<<endl; </li></ul>
  35. 35. Ітератори <ul><li>Доповнимо послідовність трьома указниками </li></ul><ul><li>private : </li></ul><ul><li>size_t _size; </li></ul><ul><li>Array * _seqArray; </li></ul><ul><li>// Указник поточного елементу </li></ul><ul><li>mutable double * _current; </li></ul><ul><li>// Указник початку послідовності </li></ul><ul><li>double * _start; </li></ul><ul><li>// Указник кінця послідовності </li></ul><ul><li>double * _ end ; </li></ul>
  36. 36. Ітератори <ul><li>І чотирма методами </li></ul><ul><li>// Почати ітерацію </li></ul><ul><li>void start() const { _current = _start;} </li></ul><ul><li>// Зробити наступний крок </li></ul><ul><li>void next() const { _current++;} </li></ul><ul><li>// Перевірити на закінчення </li></ul><ul><li>bool stop() const { return (_current == _end);} </li></ul><ul><li>// Взяти поточний елемент </li></ul><ul><li>const double & getElem() const { return *_current;} </li></ul>
  37. 37. Застосування ітераторів <ul><li>ostream& operator <<(ostream &os, const Sequence& seq) </li></ul><ul><li>{ </li></ul><ul><li>seq.start(); </li></ul><ul><li>while (!seq.stop()) </li></ul><ul><li>{ </li></ul><ul><li>cout<<chr<<seq.getElem()<<‘ ‘; </li></ul><ul><li>seq.next(); </li></ul><ul><li>} </li></ul><ul><li>cout<<endl; </li></ul><ul><li>return os; </li></ul><ul><li>} </li></ul>
  38. 38. Вправа <ul><li>Побудувати контейнер послідовного доступу List у вигляді списку </li></ul><ul><li>Визначити ітератори так, щоб програми користувачів не залежали від типу контейнера </li></ul><ul><li>ostream& operator <<(ostream &os, const List & seq) </li></ul><ul><li>{ </li></ul><ul><li>seq.start(); </li></ul><ul><li>while (!seq.stop()) </li></ul><ul><li>{ </li></ul><ul><li>cout<<chr<<seq.getElem()<<‘ ‘; </li></ul><ul><li>seq.next(); </li></ul><ul><li>} </li></ul><ul><li>cout<<endl; </li></ul><ul><li>return os; </li></ul><ul><li>} </li></ul>
  39. 39. Обмежений стек на базі масиву <ul><li>class Stack </li></ul><ul><li>{ </li></ul><ul><li>public : </li></ul><ul><li>explicit Stack(size_t size = _stackBlock); </li></ul><ul><li>~Stack(); </li></ul><ul><li>bool empty() const ; </li></ul><ul><li>bool full() const ; </li></ul><ul><li>size_t capacity() const ; </li></ul><ul><li>const double top() const ; </li></ul><ul><li>void pop(); </li></ul><ul><li>void push( const double value); </li></ul>
  40. 40. Обмежений стек на базі масиву <ul><li>class BadStack; </li></ul><ul><li>private : </li></ul><ul><li>static const size_t _stackBlock; </li></ul><ul><li>// дно стеку </li></ul><ul><li>static const size_t _ bos; </li></ul><ul><li>// верхівка стеку </li></ul><ul><li>size_t _top; </li></ul><ul><li>Array stackArray; </li></ul><ul><li>Stack( const &Stack); </li></ul><ul><li>Stack& operator =( const Stack&); </li></ul><ul><li>}; </li></ul>
  41. 41. Обмежена черга на базі масиву <ul><li>class Queue </li></ul><ul><li>{ </li></ul><ul><li>public : </li></ul><ul><li>explicit Queue (size_t size = _queueBlock); </li></ul><ul><li>~Stack(); </li></ul><ul><li>bool empty() const ; </li></ul><ul><li>bool full() const ; </li></ul><ul><li>size_t capacity() const ; </li></ul><ul><li>const double front() const ; </li></ul><ul><li>void pop(); </li></ul><ul><li>void put( const double value); </li></ul>
  42. 42. Обмежена черга на базі масиву <ul><li>class BadQueue; </li></ul><ul><li>private: </li></ul><ul><li>static const size_t _ bos; </li></ul><ul><li>static const size_t _QueueBlock; </li></ul><ul><li>size_t _size; </li></ul><ul><li>size_t _front; </li></ul><ul><li>size_t _back; </li></ul><ul><li>Array _QueueArray; </li></ul><ul><li>size_t plus1(size_t); </li></ul><ul><li>}; </li></ul>
  43. 43. Вправа <ul><li>Визначте клас двосторонньої черги на базі масиву </li></ul>
  44. 44. Висновки <ul><li>Залежно від специфіки задачі використовуються контейнери з прямим (масив, послідовність), послідовним (список) або регламентованим (стек, черга, дек) доступом </li></ul><ul><li>Ітератори дозволяють користуватися контейнерами, не вдаючись в деталі їх визначення </li></ul>

×