Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Конструктори копіювання та перетворення

Клас string: конструктори і присвоєння рядків, операції, відкладене копіювання. Найпоширенішим типом, що вживається практично у шкірному проекті, є тип string. Його можна запрограмувати самому, але вживання власного типу string створить проблеми сумісності, утруднити вживання програми в інших умовах. Тому вживають стандартний клас string, що є компонентом стандартної бібліотеки С++. Але клас string сам по собі цікавий, щоб окремо вивчити його можливу реалізацію. Почнемо з вимог до класу string. Він повинний забезпечувати: Створення рядка символів. Ініціалізацію рядка символів символьним масивом або іншим рядком. Копіювання одного рядка до іншого (присвоєння). Читання та запис окремих символів рядка. Порівняння двох рядків на рівність. Конкатенацію двох рядків. Обрахунок довжини рядка. Перевірку рядка на порожнечу. Знищення рядка символів. Приклади використання string st1; // створення порожнього рядка ////створення і ініціалізація string st2("Приклад ініціалізації"); string st3 = st2; //теж ініціалізація string st4 (st2); //ще одна ініціалізація st1 = st2; //копіювання присвоєнням cout << st1[1]; //читання символу st1[1] = 'п'; //заміна символу if (st3 == st2) //порівняння рядків на рівність cout << "рівні"; st1 = st2 + st3; //конкатенація st2 = st3 + "рядка"; //теж конкатенація st2 += st3; //конкатенація з дописуванням в кінець рядка cout << st.size(); //обчислення довжини рядка if (st1.empty()) //перевірка на порожнечу cout << "рядок порожній"; ~string st1; //знищення рядка Перетворення char* до string: string s1; const char *pc = "a character array"; s1 = pc; // правильно Навпаки не працює: char *str = s1; // помилка компіляції ////Перетворення виконує класна функція c_str (): char *str = sl.c_str(); // майже вірно const char *str = sl.c_str(); // зовсім вірно Вісь фрагмент специфікації класу string. Це дещо спрощений у запису текст заголовного файлу із стандартної бібліотеки. Особливих пояснень цей текст не потребує, оскільки призначення класу звичайне. Мінімальні пояснення до тексту дані в коментарях. class string { public: typedef unsigned long size_type; typedef char* iterator; // // Максимально можлива довжину тексту визначимо як // // const string::size_type string::npos = - 1; // // оскільки максимальне можливе число - це // // npos = 4 294 967 295 (2**32-1) static const size_type npos; // // Набір конструкторів і деструкція string(); string(const string& _X); string(const char* _S); // // Буде використано перших _N символів з рядка _S string(const char* _S, size_type _N); ~string(); // // Оператор індексування string& operator[](size_type _P0); // // Варіанти присвоєння string& operator=(const string& _X); string& operator=(const char* _S); string& operator=(const char _C); // // Присвоєння з конкатенацією string& operator+=(const string& _X); string& operator+=(const char* _S); // // Конкатенація string operator+(const string& _X) const; // // Порівняння рядків bool operator==(const string& _X) const; bool operator!=(const string& _X) const; bool operator>(const string& _X) const; bool operator>=(const string& _X) const; bool operator< (const string& _X) const; bool operator<=(const string& _X) const; // // Ввід - вивід friend operator>>(istream &is, string& _X); friend operator>>(ostream &os, const string& _X) // // Перетворення string в const char* const char* c_str() const; // // Властивості рядка (розмір, порожнеча) size_type length() const; size_type size() const; size_type capacity() const; bool empty() const; // // Очищення рядка void clear(); // // Перестановка void swap(string& _X); friend void swap(string& _X, string& _Y); // // Пошук підрядка size_type find ((const string& _X, size_type _P = 0) const; // // Взяття підрядка string substr ((size_type _P = 0, size_type _M = npos) const; // // Ітератори iterator begin(); iterator end(); // // Дописування в кінець string& append(const string& _X); string& append(const char* _S); string& append(const char* _S, size_type _N); // // Функції присвоєння string& assign(const string& _X); string& assign ((const string& _X, size_type _P, size_type _M); string& assign(const char* _S); string& assign(const char* _S, size_type _N); string& assign(const char _C); // // Лексикографічне порівняння частин рядків bool compare(const string& _X) const; bool compare(size_type _P, size_type _M, const string& _X) const; // // Вставка підрядка string& insert(size_type _P0, const string& _X); string& insert(size_type _P0, const string& _X, size_type _P, size_type _M); string& insert(size_type _P0, const char* _S); string& insert(size_type _P0, const char* _S, size_type _M); // // Стирання підрядка string& erase(size_type _P0 = 0, size_type _M = npos); // // Заміна підрядка string& replace(size_type _P0, size_type _N0, const string& _X); string& replace(size_type _P0, size_type _N0, const char* _S); string& replace(iterator _F, iterator _L, const string& _X);   private:   char* _allocator; size_type _len; size_type _capacity; }; Дуже корисною вправою була б спроба самостійної власної реалізації операцій і функцій над рядками. А мі розглянемо ще одну специфікацію рядків, а саме рядків з відкладеним копіюванням DeferredString, запропоновану б.Страуструпом в Спеціальному виданні С++. class DeferredString { private: // // Спільне зберігання однакових рядків різних об' єктів struct Srep; //representation Srep* rep;   public: // // Розрізнення індексування при читанні і запису class Cref; //reference to char // // Генерування виключень при виході індексів за межі class Range(); //errors   DeferredString(); DeferredString(const DeferredString&); DeferredString(const char*); ~DeferredString(); DeferredString& operator=(const DeferredString&); DeferredString& operator=(const char*); // // Перевірка виходу індексу за межі рядка void check(int i) const; char read(int i) const; void write (int i, char c); Cref operator[](int i); char operator[](int i) const; int size() const; }; Нижче наведене реалізацію цього класу. Порожній рядок, як і будь-який новий, завжди викликає конструктор зображення: DeferredString::DeferredString() { rep = new Srep(0,""); } DeferredString::DeferredString(const char* s) { rep=new Srep(strlen(s), s); } Копіювальний конструктор створює ще одне посилання на існуюче зображення DeferredString::DeferredString(const DeferredString& x) { x.rep ->n++; rep=x.rep; //shared representation } Деструкція знищує об' єкт лише у тому випадку, коли на нього немає більше посилань. Інакше кількість посилань зменшується на одиницю DeferredString::DeferredString(const DeferredString& x) { x.rep ->n++; rep=x.rep; //shared representation } Як завжди, присвоєння відрізняється від коструктора необхідністю розпорядитися старимо значенням лівої частини DeferredString& DeferredString:: operator=(const DeferredString &x) { x.rep ->n++; // // якщо присвоєння виду st=st // // те одночасно із зменшенням rep ->n // // зменшиться також x.rep ->n // // Старе значення витирається лише тоді // // коли воно більш ніким не використовується if (--rep ->n==0) delete rep; rep=x.rep; return *this; } DeferredString& DeferredString::operator=(const char* s) { // // Перевіряємо, чи хтось користується старимо значенням if (rep ->n == 1) // // Якщо ні, виконуємо присвоєння на місці rep ->assign(strlen(s), s); else { // // Зменшуємо лічильник посилань rep ->n--; // // Будуємо нове зображення rep=new Srep(strlen(s), s);   } return *this; } Реалізації перевірки виходу індексу за межі і читання символу за його номером очевидні void DeferredString::check(int i) const { if (i<0||rep -<sz<=i) { throw Range(); } } char DeferredString::read(int i) const { return rep ->s[i]; } Запис нового символу приводити до створення нового зображення. Можна було б передбачити вдосконалення реалізації для випадку, коли новий символ співпадає зі старимо void DeferredString::write (int i, char c) { rep=rep ->getOwnCopy(); rep ->s[i]=c; } Оператор індексування тепер перевірить спочатку правильність індексу, а потім створить об' єкт Cref (про нього нижче) DeferredString::Cref DeferredString::operator[](int i) { check(i); return Cref(*this, i); } char DeferredString::operator[](int i) const { check(i); return rep ->s[i]; } int DeferredString::size() const { return rep ->sz; } Структура спільного зображення має досить зрозумілу специфікацію struct DeferredString::Srep { char* s; //pointer to characters int sz; //number of elements int n; //number of references   Srep (int nsz, const char* p); ~Srep(); Srep* getOwnCopy(); void assign (int nsz, const char* p); private: Srep (const Srep&); Srep& operator= (const Srep&); }; Зверніть увагу на закриту частину структури. До неї внесені оператори, замовчуваними версіями яких можна було б ненароком зіпсувати усю систему відкладеного копіювання. Конструктор створює нове зображення Зверніть увагу на закриту частину структури. До неї внесені оператори, замовчуваними версіями яких можна було б ненароком зіпсувати усю систему відкладеного копіювання. Конструктор створює нове зображення деструкція його знищує. DeferredString::Srep::~Srep() { delete [] s; } Тут відбувається явне копіювання рядка із створенням нового зображення DeferredString::Srep* DeferredString::Srep::getOwnCopy() { if (n==1) return this; n--; return new Srep(sz, s); } Найнижчий рівень реалізації присвоєння void DeferredString::Srep::assign(int nsz, const char* p) { if (sz!=nsz) { delete [] s; sz=nsz; s = new char[sz+1]; } strcpy(s, p); } Вісь клас для розпізнавання читання і запису class DeferredString::Cref { friend class DeferredString; DeferredString & s; int i; Cref(DeferredString& ss, int ii): s(ss), i(ii){} public: operator char() const {return s.read(i);} void operator=(char c){s.write(i, c);} }; Корисно провести детальне спостереження за поведінкою об' єктів з відкладеним копіюванням.  

 

<== предыдущая лекция | следующая лекция ==>
Перевантаження конструкторів | Закон Деметри
Поделиться с друзьями:


Дата добавления: 2013-12-14; Просмотров: 358; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.009 сек.