Студопедия

КАТЕГОРИИ:


Архитектура-(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);}

};

 

Корисно провести детальне спостереження за поведінкою об'єктів з відкладеним копіюванням.

 

 

<== предыдущая лекция | следующая лекция ==>
Довизначення (overloading) операцій. Зверніть увагу на сигнатуру і реалізацію постфіксних операторів інкременту | Створення похідних об'єктів
Поделиться с друзьями:


Дата добавления: 2014-01-06; Просмотров: 440; Нарушение авторских прав?; Мы поможем в написании вашей работы!


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



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




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