Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП (С++) Лекции Бобин.doc
Скачиваний:
57
Добавлен:
08.02.2015
Размер:
625.66 Кб
Скачать

Глава 12. Обработка исключительных ситуаций

Если в программе возникает внештатная ситуация, не предусмотренная логикой работы программы, то говорят об исключительной ситуации: exception.

При использовании обработки исключительных ситуаций существенно увеличивается объем программного кода и ухудшается его читаемость, поскольку обработка исключений смешивается с общей логикой программы.

Пример:

#include <cstdio>

#include <cstdlib>

class MyError

{

int code;

const char * msg;

public:

MyError(const char *msg = “”, int code = 0): code(code), msg(msg)

{

}

const char *getMsg() const

{

return msg;

}

const int getCode () const

{

return code;

}

};

MyError ErrorInfo;

bool ReadData(const char *fileName, char *data, const int size)

{

FILE * fd;

fd = fopen(filename, “rb”);

if(fd == NULL)

{

ErrorInfo = MyError(“File open error”, 1);

return false;

}

fread(data, size, 1, fd);

fclose(fd);

ErrorInfo = MyError();

return true;

}

int main()

{

char data [200];

if(!ReadData(“test.bin”, data, 200))

{

fprintf(stderr, “Error! %s (Code %d)\n”,

ErrorIngo.getMsg(),

ErrorInfo.getCode());

return 1;

}

printf(“Все хорошо! \n”);

return 0;

}

Из этого примера видно, что обработка ошибок занимает много места и сливается с основной логикой программы.

п.12.2. Обработка исключений в языке С++

В языке Си++ используется специальный механизм обработки исключений, который объединяет три компонента: оператор запуска исключений, оператор определения блока перехвата исключений и определение блока обработки исключений.

В потенциально опасной функции используется оператор запуска исключений, который прерывает ее работу и активизирует механизм обработки исключений. Вызов потенциально опасной функции помещается внутрь блока перехвата исключений, который играет роль диспетчера и выбирает последовательность действий, необходимую для устранения возникшей ошибки. Соответственно, сама последовательность действий помещается внутрь соответствующего блока обработки исключений.

п.12.2.1. Запуск исключений

При возникновении ошибки необходимо создать объект, содержащий информацию об этой ошибке (в простейшем случае – целое число). Его называют объектом исключения. Далее, с помощью ключевого слова throw. Необходимо «запустить» данный объект за пределы блока, где он был создан.

{

...

throw объект;

...

}

Данный оператор прекращает работу блока, внутри которого он располагается, создает объект исключения и передает его «наружу». При запуске исключения уничтожаются все созданные к тому моменту времени переменные внутри данного блока. Для каждой уничтожаемой переменной обязательно вызывается деструктор. Если в блоке, в который попало запущенное исключение, не производится его обработка, то производится повторный запуск исключения за пределы этого блока. Этот процесс может длиться вплоть до функции main. Если в функцииmainтак же не происходит обработки, то программа аварийно завершается с помощью библиотечной функцииterminate().

п.12.2.2. Перехват и обработка исключений

Для организации перехвата и обработки исключений используются ключевые слова tryиcatch. С помощью словаtryсоздается блок перехвата исключений.

try

{

...

}

Сразу после блока перехвата с помощью слова catchсоздается один или несколько блоков обработки исключений. Блок обработки исключений внешне выглядит как функция с одним параметром. Значением этого параметра является объект исключений, полученный из блока перехвата исключений. Блоки обработки исключений располагаются друг за другом и должны отличаться типом объекта исключений.

try

{

опасный_код

}

catch(тип &имя)

{

обработка

}

catch(тип &имя)

{

обработка

}

...

Пример:

#include <cstdio>

#include <cstdlib>

class MyError

{

int code;

const char * msg;

public:

MyError(const char *msg = “”, int code = 0): code(code), msg(msg)

{

}

const char *getMsg() const

{

return msg;

}

const int getCode() const

{

return code;

}

};

bool ReadData(const char *fileName, char *data, const int size)

{

FILE * fd;

fd = fopen(filename, “rb”);

if(fd == NULL)

throw MyError(“File open error”, 1);

fread(data, size, 1, fd);

fclose(fd);

}

int main()

{

char data[200];

try

{

ReadData(“test.bin”, data, 200);

printf(“Ok!”);

}

catch(MyError &err)

{

fprintf(stderr, “Error! %s (Code %d)\n”, ErrorInfo.getMsg(),

ErrorInfo.getCode());

return 1;

}

return 0;

}

п.12.2.3.Поиск обработчика исключений

После запуска исключения с помощью оператора throw, механизм обработки исключений С++ начинает искать первый обработчик с аргументом, соответствующим типу этого исключения. Если такой обработчик находится, то управление передается ему и данное исключение считается обработанным. После выполнения найденного блока обработки поиск обработчиков прекращается, и другие блоки обработки не выполняются. Управление передается сразу за пределы всей конструкции перехвата.

п.12.2.4. Перезапуск исключений

Перезапуск исключения используется, когда между запуском исключения и основной обработкой нужно проделать какие-то вспомогательный действия на пути следования исключений.

Организовать перезапуск исключения можно, добавив оператор throwбез параметров в конце блока обработки.

try

{

...

}

catch(...)

{

...

throw;

}

п.12.3. Спецификация искючений

При создании любой функции программист может ограничить список типов исключений, которые разрешается запускать из данной функции. Для этого в объявлении функции необходимо указать ключевое слово throw, а затем в скобках перечислить через запятую типы исключений, которые разрешается запускать. Если в объявлении функции ничего не сказано о типах исключений, то в данной функции разрешено запускать исключения любых типов. Если список исключений присутствует в объявлении функции и является пустым, то ей запрещено запускать какие либо исключения.

Если функция запускает исключение, тип которой не указан в списке разрешенных, то запускается стандартное исключение bad_exception, если оно разрешено, либо вызывается библиотечная функцияunexpected(). С технической точки зрения реализация механизма спецификации исключений представляет собой нетривиальную задачу, поэтому не во всех компиляторах реализуется такая возможность.

п.12.4.Функцииterminate()иunexpected()

Функции terminate()иunexpected()входят в стандартную библиотеку языка С++ и вызываются в самых крайних случаях, как реакция на невозможность обработки исключений. Прототипы этих функций находятся в заголовочном файле<exception>.

п.12.4.1. Функция terminate()

Как правило, функция terminate()является последним средством обработки исключений, когда никакой другой обработчик не подходит. По умолчанию эта функция вызывает библиотечную функциюabort(), которая аварийно завершает работу программы. Программист может определить свой собственный обработчик, который будет вызываться вместо функцииterminate(). Таковым должна быть функция типаvoid, не принимающая никаких параметров. Указать новый обработчик можно с помощью библиотечной функцииset_terminate(). На вход эта функция принимает адрес нового обработчика и возвращает адрес старого. Прототип этой функции находится так же в заголовочном файле <exception>.

п.12.4.2. Функция unexpected()

Все аналогично функцииterminate(), но для указания нового обработчика используется функцияset_unexpected().

п.12.5. Исключения в конструкторах и деструкторах

Механизм обработки исключений С++ гарантирует, что при выходе из блока в результате запуска исключения для всех локальных объектов этого блока, конструкторы которых были успешно завершены, будут вызваны деструкторы. Если при вызове конструктора запускается исключение, то при выходе исключения за пределы данного конструктора деструктор для данного объекта вызван не будет.

Если внутри блока tryбыло создано пять объектов. Пусть при вызове конструктора для объекта с индексом 4, произошло исключение, которое вышло за пределы конструктора. Перед тем, как управление будет передано в блок обработки, будут вызваны деструкторы для объектов с индексами 0, 1, 2 и 3. Для объекта с индексом 4 деструктора вызвано не будет. Решения данной проблемы следующие: либо блок перехвата помещают внутрь конструктора, либо создают класс-обёртку, внутрь которого помещают блок перехвата.

п.12.6. Стандартные исключения в библиотеке С++

В стандартной библиотеке языка С++ определена иерархия стандартных классов исключений. В корне этой иерархии лежит базовый класс exception, определенный в одноименном заголовочном файле. Данный класс содержит виртуальный методwhat(), предназначенный для пeрекрытия в потомках и выдачи текстового сообщения о причине ошибки. В заголовочном файлеstdexceptопределены два потомка классаexception:logic_error, который служит базовым для классов, описывающих логические ошибки;runtime_error, описывающие ошибки времени выполнения. Все остальные классы исключений являются производными от этих трех.

п.12.7. Оператор new

При неудачно попытке выделения памяти оператор newзапускает стандартное исключениеbad_alloc. Что бы обработать такое исключение стоит поместить операторnewв блок перехвата, а после сделать блок обработки. Есть возможность указания отдельного обработчика ситуации «нехватка памяти». Для этого используется библиотечная функцияset_new_handler().

п.12.8. Операторы typeid и dynamic_cast

Оператор typeidзапускает стандартное исключениеbad_typeidпри попытке применить его к нулевому указателю. Аналогично, операторdynamic_castзапускает исключениеbad_castпри попытке некорректного преобразования типа ссылки.

Глава 13. Шаблоны в языке С++

Вспомненные нами технологии наследования и композиции предоставляют средства для многократного использования предварительно скомпилированных объектных модулей. Механизм шаблонов С++ позволяет многократно использовать исходный текст программы.

п.13.1. Обобщенные (шаблонные) функции

п.13.1.1. Понятие обобщенной функции

Обобщенная функция определяет универсальную последовательность операций, применимую к различным типам данных. Конкретный тип данных, с которым будет работать функция, передается ей в качестве параметра. С помощью обобщенной функции можно определить алгоритм обработки данных, не зависимый от типа данных. При вызове такой функции, применительно к конкретному набору данных, компилятор автоматически сгенерирует нужный вариант, подставив все типы.

В общем случае определение шаблона выглядит следующим образом:

template <class T> тип имя_функции(список_параметров)

{

тело_функции

}

В угловых скобках пишутся имена параметров, задающих типы данных. Т– параметр шаблона, его можно использовать внутри функции, как имя некоторого типа данных. При вызове обобщенной функции компилятор автоматически сгенерирует конкретный ее вариант, где вместоТбудет подставлено имя конкретного типа данных. Согласно современному стандарту параметр шаблона предваряется ключевым словомclass.

Примеробобщенной функции:

template <class T> void swap(T &a, T &b)

{

T temp;

temp = a;

a = b;

b = temp;

}

int main(void)

{

int a = 5, b = 6;

double x = 2.2, y = 3.3;

cout << “a = “ << a << “, b = “ << b << endl;

cout << “x = “ << x << “, y = “ << y << endl;

swap(a, b); \\ (*)

swap(x, y); \\ (**)

cout << “a = “ << a << “, b = “ << b << endl;

cout << “x = “ << x << “, y = “ << y << endl;

return 0;

}

Обнаружив вызов шаблонной функции, помеченной (*), компилятор проанализирует типы ее параметров и установит, то оба параметра относятся к типу int. Далее он сформирует на основе указанного шаблона конкретную версию этой функции, принимающую на вход параметры типаint. И осуществит в месте (*) вызов именно этой сгенерированной функции. Затем компилятор обнаружит вызов функции (**), проанализирует параметры и остановит, что оба параметра относятся к типуdouble. Соответственно, установив это, компилятор по указанном ранее шаблону сгенерирует еще один вариант функции, где вместоТ, подставленоdouble.

Обобщенная функция так же называется шаблонной. Конкретная, сгенерированная компилятором версия шаблонной функции, называется специализацией этой функции. Процесс генерации конкретной версии шаблонной функции называется инстанцированием.

В процессе компиляции программного кода, шаблоны не подвергаются синтаксическому анализу, следовательно, ошибки в них не контролируются. Проверке подвергаются только специализации. То есть для проверки шаблона, его нужно обязательно истанцировать.

Можно создавать шаблонные функции с несколькими параметрами.

Пример:

template <class T1, class T2> void sqr(T1 &a, T2 &b)

{

a *= a;

b *= b;

}

int main(void)

{

int a = 5, b = 6;

double x = 2.2, y = 3.3;

cout << “a = “ << a << “, b = “ << b << endl;

cout << “x = “ << x << “, y = “ << y << endl;

sqr(a,x); \\ (*)

sqr(y,b); \\ (**)

cout << “a = “ << a << “, b = “ << b << endl;

cout << “x = “ << x << “, y = “ << y << endl;

return 0;

}

п.13.1.2. Явная специализация обобщенной функции

Возникают такие ситуации, что для конкретных типов данных алгоритм обработки отличается от стандартного алгоритма. В этом случае используется явная специализация шаблона. Т.е. программист предъявляет вариант шаблонной функции для конкретного набора параметров.

Пример:

template <class T> void swap(T &a, T &b)

{

T temp;

temp = a;

a = b;

b = temp;

cout << “Общий алгоритм.” << endl;

}

template <> void swap(int &a, int &b)

{

int temp;

temp = a;

a = b;

b = temp;

cout << “Особый алгоритм для int” << endl;

}

int main(void)

{

int a = 5, b = 6;

double x = 2.2, y = 3.3;

cout << “a = “ << a << “, b = “ << b << endl;

cout << “x = “ << x << “, y = “ << y << endl;

swap(a,b); \\ (*)

swap(x,y); \\ (**)

cout << “a = “ << a << “, b = “ << b << endl;

cout << “x = “ << x << “, y = “ << y << endl;

return 0;

}

п.13.1.3. Перегрузка обобщенной функции

Для перегрузки шаблонной функции достаточно создать новый шаблон с другим числом параметров.

п.13.1.4. Явное указание параметров шаблона

В некоторых ситуациях не представляется возможности однозначно определить значения параметров шаблона.

Пример:

template <class T> void swap(T &a, T &b) { ... }

int main ()

{

int a;

double x;

swap(a,x); \\ ?!

return 0;

}

В такой ситуации не ясно, что подставлять: то ли int, то лиdouble. В такой ситуации необходимо явно указать значение параметров шаблона. В таких случаях необходимо явно указать параметр шаблона:

swap <double> (a,x)

Если для инстанцирования подходят несколько шаблонов, то компилятор инстанцирует самый специализированный шаблон, то есть тот, у которого указано максимальное число параметров.

п.13.2. Обобщенные (шаблонные) классы

п.13.2.1. Создание обобщенных классов

template <class T> ключ_класса имя_класса

{

тело_класса

};

Методы шаблонного класса с внутренним определением автоматически становятся шаблонными функциями. Методы шаблонного класса с внешним определением должны быть явно определены как шаблонные функции.

Пример:

template <class T> class Variable

{

T value;

public:

T getValue() const

{

return value;

}

void setValue(const T &newVal)

{

value = newVal;

}

};

Функции getValue и setValue автоматически становятся шаблонными. Альтернативное определение:

class Variable

{

T value;

public:

T getValue() const;

void setValue(const T &newval);

};

template <class T>

T variable <T>::getValue() const

{

return value;

}

template <class T>

void variable <T>::setValue() const

{

value = newValue;

}

В языке С++ есть такое понятие как перегрузка функций, но нет такого понятия как перегрузка классов. То есть можно сделать несколько функций с одинаковыми именами, поэтому в имя специализации шаблонной функции не входит информация о параметрах шаблона. Т.к. нет перегрузки классов, мы не можем сделать несколько классов с одинаковыми именами. Разные специализации шаблонного класса должны иметь разные имена. Таким образом, значение параметров шаблона вместе с угловыми скобками являются частью специализации имени шаблонного класса. При использовании инстанцированного класса необходимо явно указывать значения параметра шаблона в отличие от шаблонной функции.

п.13.2.2. Параметры шаблонных классов по умолчанию

В шаблонных классах разрешено указывать умалчиваемые значения для параметров шаблона. Механизм аналогичен умалчиваемым значениям параметров обычных функций.

Пример:

template <class T = int>

class Variable

{

...

};

При инстанцировании шаблона класса всегда необходимо указывать угловые скобки.

...

Variable<> x;

...

Так как параметр шаблона входит в имя класса.

п.13.2.3. Явная специализация шаблонного класса

Явная специализация шаблонного класса аналогична явной специализации шаблонной функции. Шаблон никогда не подвергнется синтаксическому анализу. Анализу подвергается только инстанцированные объекты.

п.13.3. Параметры шаблона

В С++ параметры шаблонов могут быть трех видов:

  1. Типизированные параметры.

  2. Не типизированные параметры.

  3. Другие шаблоны.

Все рассмотренные ранее параметры являются типизированными.

Не типизированный параметр– параметр, который параметризует целое число, а не тип данных. Значением этого параметра может быть только целочисленная константа времени компиляции.

Пример:

template <class T, int N>

class Stack

{

T data [N];

...

};

...

Stack<Car, 4> garage;

(может хранить типа «Car» не более 4 штук)

Stack<Car, 5> garage;

(уже другой тип данных!)

Пример(параметр шаблона – другой шаблон):

template<class X, template<class> class Y>

class Array

{

Y<x> data;

};

...

Array<int, Variable> p; // Массив, содержащий поле типа Variable, где вместо

// типа будет подставлен тип int

Тогда Y<x> data;развернется вVariable<int> data;

п.13.4. Ключевые слова typename и export

Пусть у нас есть следующий шаблон:

template<class T>

class Smth

{

...

};

Где-то далее в программе написали: T::X. Встретив такую конструкцию где-нибудь в теле какого-нибудь метода указанного шаблона класса, компилятор решит, чтоX– это метод, либо статическое поле классаT.

a = T::X(s);

T::X = s;

Предположим, что внутри класса, подставленного при инстанцировании вместо T, есть вложенный класс с именемX(T::X a;). И мы, записав такую конструкцию, хотим обратиться не к методу или полю, а к типу данных, вложенных в класс, который подставлен в качестве параметра шаблона:

class W

{

...

public:

class X

{

...

};

...

};

Smth<W> a; // T <=> W

Компилятор думает, что такая конструкция обращается к полю или методу, а мы – тип данных (T::X). В таком случае используется ключевое словоtypename:

typename T::X b = 5;

T::X = 5 // Обращение к статическому полю

Ключевое слово export позволяет разбить определение шаблона на несколько файлов. Техническая реализация поддержки ключевого словаexport в компиляторах является очень сложной. Поэтому во многих компиляторах данная возможность не реализована (напримерVisualStudio2010).

Глава 14. Стандартная библиотека шаблонов С++

(StandardTemplateLibrary,STL)

Стандартная библиотека С++ практически полностью включает в себя все средства стандартной библиотеки языка Си. А также содержит множество надстроек над ней.

Основные компоненты STL:

  1. библиотека общих утилит;

  2. библиотека поддержки строк в стиле С++;

  3. библиотека поддержки работы с числами;

  4. библиотека контейнеров;

  5. библиотека итераторов;

  6. библиотека алгоритмов;

  7. библиотека ввода-вывода;

  8. библиотека локализации.

В библиотеке общих утилит определены базовые объекты, на основе которых строятся остальные компоненты STL. Здесь вводится ряд простейших понятий и классов. В состав библиотеки утилит входят:

  1. вспомогательные компоненты в заголовочном файле utility;

  2. система функторов (functional);

  3. средства работы с памятью (memory);

  4. средства работы с датой и временем (ctime).

п.14.1. Заголовочный файл utility

Важным звеном в заголовочном файле является класс pair. Этот класс описывает пару значений произвольного типа. Класс является шаблонным и принимает на вход два параметра: тип первого значения в паре, тип второго значения в паре:

pair<T1, T2>

В классе определены 2 имени (стандартные псевдонимы):

first_type

second_type

Класс представляет собой структуру с двумя полями:

first

second

В классе есть конструктор умолчаний, конструктор общего вида с двумя параметрами, конструктор копирования, шаблонный конструктор приведения, позволяющий сконструировать пару одного типа на основе пары другого типа.

Для этого класса определен оператор сравнения на тождественное равенство (==).

Также определена операция строго меньше (<).

В качестве альтернативы конструктору общего вида определена шаблонная функция make_pair(). На вход она принимает 2 параметра, на выходе выдаетpair.

Пример:

pair<int, double> x = make_pair(6, 3.5);

pair<double, double> y(6.6, 3.5);

pair<int, double> z = y;

cout << (x == z) << endl;

Для практического применения класса pairс собственными классами в них следует определить конструкторы умолчаний и сравнения, а также операторы==и<.

п.14.2. Функторы

Функтор – объект, к которому применима операция (), простейший функтор – функция. Функтором может выступать объект класса, в котором перегружена операция().

Функторы второго типа представляют собой удобную альтернативу указателям на функции. Использование функтора внешне не отличается от вызова функции, зато предоставляет следующие преимущества:

  1. функторы позволяют сохранять внутреннее состояние между вызовами;

  2. функторы можно использовать в качестве параметра шаблонов.

Стандартные функторы строятся на базе двух понятий: унарная функция и бинарная функция. Для описания унарных функций используется следующий класс:

template<class Arg, class Result>

struct unary_function

{

typedef Arg argument_type;

typedef Result result_type;

};

Для описания бинарных функций используется следующий класс:

template <class Arg1, class Arg2, class Result>

struct binary_function

{

typedef Arg1 first_argument_type;

typedef Arg2 second_argument_type;

typedef Result result_type;

};

Шаблоны арифметических операций:

template <class T>

struct plus: binary_function<T, T, T>

{

T operator () (T &x, T &y);

{

return x + y;

}

}

Аналогичным образом определяются другие арифметические действия: разность (minus), умножение (multiplies), деление (divides), взятие по модулю (modulus), отрицание (negate). По аналогии с арифметическими операциями на базе бинарной функции описываются операции сравнения:

struct имя : binary_function<T, T, bool>

{

equal_to “x == y”

not_equal_to “x != y”

greater “x > y”

less “x < y”

greater_equal “x >= y”

less_equal “x <= y”

}

По этой же схеме реализуются логические операции:

struct имя : binary_function<T, T, bool>

{

logical_and “x && y”

logical_or “x || y”

}

Так же реализуется унарная операция отрицание:

struct logical_not: binary_function<T, T, bool>

п.14.3. Управление памятью

Все средства управления памятью находятся в заголовочном файле <memory>. В этом файле очень важен комплект шаблонных классов-распределителей (allocator). Назначение классов распределителей – описание алгоритма размещения объекта в памяти.

Еще одним важным компонентом файла <memory>является класс Умный Указатель. Этот класс имеет имя auto_ptr.

class auto_ptr

{

typedef T element_type;

explicit auto_ptr(T *p = NULL);

auto_ptr(auto_ptr<T> &);

template <class U>

auto_ptr(auto<U> &);

auto_ptr<T> & operator= (auto_ptr <T> &);

~auto_ptr();

T & operator * ();

T & operator -> ();

T * get() const;

T * release();

void reset(T *p = NULL);

template <class U> operator auto_ptr<U> ();

};

Объект этого класса – «Умный Указатель» на объект нужного класса. В классе есть конструктор, который создает Умный Указатель, ссылающийся на конкретный объект. Так же существует конструктор, создающий новый умный указатель на базе существующего и передает ему право управления объектом. Далее шаблонный конструктор, который создает новый Умный Указатель на основе Умного Указателя другого типа. Оператор присваивания, который принимает право владения от другого указателя. И наконец, деструктор, который уничтожает объект при условии, что данный указатель обладает правом владения этим объектом. Так же в классе имеется набор стандартных операторов: разыменования, косвенной адресации, метод get(), позволяющий получить обычный указатель на объект, методrelease(), лишающий текущий указатель права владения объектом и возвращает адрес объекта,reset(), перенастраивающий указатель на другой объект, и, наконец, оператор приведения типа.

Умный Указатель может обладать правом владенияобъектом, на который он ссылается. Это право можно передать другому умному указателю. Логика работы класса Умный Указатель реализует правильное уничтожение динамических объектов при условии работы с одним объектом через несколько умных указателей. Например, на один и тот же объект настроено два Умных Указателя. При уничтожении одного из них право владения объектом переходит к другому умному указателю. При уничтожении последнего владеющего объектом указателя уничтожается и сам объект.

Глава 15. Строки в стиле С++

п.15.1. Недостатки строк в стиле Си

Строки в стиле Си обладают следующими важными недостатками:

  1. Отсутствие средств автоматического управления памятью для размещения строк.

  2. Всегда надо помнить о завершающем символе.

  3. Отсутствуют механизмы автоматического контроля за пределы массива.

  4. Отсутствие автоматических средств уничтожения динамических строк.

  5. Сложный механизм копирования строк.

п.15.2. Работа со строками в стиле С++

В стандартной библиотеке языка С++ определен шаблонный класс basic_string, обобщающий понятие строки. В этом классе находятся методы, которые ликвидируют перечисленные ранее недостатки строк в стиле Си и существенно расширяют понятие строки. Данный шаблон определен в заголовочном файле<string>(это не одно и то же, что<cstring>). Первый параметр шаблона определяет тип данных, на базе которого строится строка. Фактически, первый параметр отвечает на вопрос «что такое один символ строки?».

В практике программирования обычно применяются строки двух видов: стандартные строки, состоящие из символов таблицы ASCIIи расширенные строки, состоящие из символов таблицыUNICODE. Обычно речь идет о двухбайтовых символахUTF-16.

Для представления одиночных символов формата ASCIIв языке С++ используется ключевое словоchar. Для представления расширенных символов (таблицыUNICODE) используется ключевое словоwchar_t.

Для удобства практического применения строк в стиле С++ в заголовочном файле <string>введены два псевдонима:

typedef basic_string <char> string;

typedef basic_string <wchar_t> wstring;

В дальнейшем мы будем рассматривать возможности класса basic_string, на примере частного случая этого класса – классаString. Рассматривая этот частный случай, мы понимаем, что он легко обобщается на весь классbasic_string.

Функции, в том числе и конструкторы для работы со строками делаются в нескольких стандартных вариантах, учитывающих разные формы представления понятия строка.

Понятие строки:

  • В качестве строки может выступать строка в стиле Си:

const char *str;

  • В качестве строки может выступать несколько первых символов строки в стиле Си:

const char *str, size_t count;

  • Как некий фрагмент строки в стиле Си:

const char *str, size_t offset, size_t count;

  • Как строка в стиле С++:

const string &str;

  • Для строки в стиле С++ возможны аналогичные варианты:

const string &str, size_t count;

const string &str, size_t offset, size_t count;

  • Последовательность одинаковых символов:

size_t count, char symbol;

Некоторые функции принимают информацию о фрагменте строки:

size_t offset, size_t count;

п.15.3. Создание строки

Осуществляется с помощью конструктора. Вызывается конструктор умолчаний, который создает пустую строку:

string str;

string Vlad(“НеждановВлад”);

string Name = Vlad;

string VladName(Vlad, 9, 4); // Влад

string aaaa(4, ‘a’);

п.15.4. Дописывание символов в строку

Дописывание символов в строку выполняет метод append().

string & string::append(строка);

Метод дописывает к текущей строке ту, которая указана в параметрах и возвращает получившуюся строку

Пример:

string VladFull = VladName;

VladFull.append(“ Нежданов”);

Аналогом метода append()является оператор+=.

VladFull += “ Нежданов”;

п.15.5. Конкатинация строк

Выполняет оператор +.

string operator +(строка, строка);

Хотя бы одним из параметров должна быть строка в стиле С++. Оператор получает на вход две строки и в качестве результата выдает полученную строку:

VladFull = VladName + “ Нежданов”;

п.15.6. Вставка символов в строку

Осуществляет метод insert():

insert & string::insert(size_t pos, строка);

Метод вставляет содержимое указанной строки перед позицией pos(гдеpos– индекс).

string hello(“Hellord”);

hello.insert(4, “o w”);

п.15.7. Замена символов в строке

Осуществляется методом replace():

string & string::replace(фрагмент,строка);

Метод заменяет фрагмент на указанную строку.

Фрагмент представляет пару: (куда, сколько).

Пример:

string HELLO(“HELLO ORLD”);

HELLO.replace(4, 2, “O W”);

п.15.8.Поиск символов в строке

  1. size_t string::find(строка) const;

size_t string::find(символ) const;

В качестве строки может выступать либо строка в стиле Си, либо С++.

Метод осуществляет поиск строки и возвращает индекс найденной позиции, либо значение константы string::npos, если совпадений не найдено.

  1. size_t string::find(строка, size_t offset) const;

Поиск, начиная с заданного смещения:

string hello(“Hello world”);

cout << hello.find(‘o’, 5) endl; // 7

  1. size_t string::rfind(строка) const

Поиск в обратном направлении:

size_t string::rfind(строка, size_t offset) const;

  1. size_t string::find_first_of(...) const;

Ищет первое вхождение какого-нибудь из указанных символов:

string hello(“Hello world”);

cout << hello.find_first_of(“abcdef”) << endl; // 1

  1. size_t string::find_first_not_of(...) const;

Ищет первый символ, не совпадающий с указанными.

  1. size_t string::find_last_of(...) const;

Ищет последнее вхождение символа из указанных.

  1. size_t string::find_last_not_of(...) const;

п.15.9.Удалениесимволовизстроки

size_t string::erase(size_t offset = 0, size_t count = npos);

Вызов этого метода без параметров – удалить всю строку.

string hello(“Hello my world”);

hello.erase(5, 4);

hello.erase(5);

п.15.10.Обращениекэлементустроки

char & string::at(size_t offset);

const char & string::at(size_t offset) const;

Второй вариант используется для строк, которые являются константами.

Вместо метода at()используется оператор[].

Оператор []не проверяет выход за пределы строки. Методat()сгенерирует стандартное исключениеout_of_range.

п.15.11. Определение размера строки

size_t string::size() const;

size_t string::length() const;

В строке в стиле С++ в конце нет нулевого символа. Он может быть добавлен туда как любой другой символ и будет учитываться при подсчете длины.

string hello(“Hello”);

hello += ‘\0’;

cout << hello.lenght(hello) << endl; // 6

п.15.12. Получение строки в стиле Си

const char * string::c_str() const;

Полученную строку запрещается как-либо менять.

п.15.13.Выделениеподстроки.

string string::substr(size_t offset = 0, size_t count = npos) const;

По умолчанию – копия исходной строки.

п.15.14.Обмендвухстрок

void string::swap(string &str);

Метод меняет содержимое текущей и какой-либо другой строк.

п.15.15. Присваивание и сравнение строк

“=”, “==”, “!=”, “>”, “<”, “>=”, “<=”.

Параметры такие же как у оператора “+”.

Сравнение происходит в лексикографическом порядке.