КАТЕГОРИИ: Архитектура-(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. Він повинен забезпечувати:
· Створення рядка символів. · · · Ініціалізацію рядка символів символьним масивом або іншим рядком. · · Копіювання одного рядка до іншого (присвоєння). · · Читання та запис окремих символів рядка. · · Порівняння двох рядків на рівність. · · Конкатенацію двох рядків. · · Обрахунок довжини рядка. · · Перевірку рядка на пустоту. · · Знищення рядка символів.
Приклади використання
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);} };
Корисно провести детальне спостереження за поведінкою об'єктів з відкладеним копіюванням.
Дата добавления: 2014-01-06; Просмотров: 440; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |