570 likes | 852 Views
Стандартные библиотеки С++. История возникновения. В программах на С++ часто приходится выполнять однообразные действия, для которых в языке нет стандартных средств, или такие средства не достаточно удобны.
E N D
История возникновения • В программах на С++ часто приходится выполнять однообразные действия, для которых в языке нет стандартных средств, или такие средства не достаточно удобны. • Производители компиляторов и сред разработки предлагают средства для облегчения написания программ: • Библиотеки функций • Библиотеки классов • Часть средств была стандартизована и обычно поставляется с любым компилятором С++. • Некоторые средства приобрели такую популярность, что реализованы в современных языках как часть языка программирования.
Основные вопросы • Строки • Потоковые классы • Стандартная библиотека шаблонов (STL)
Строки • C++ не содержит стандартного типа данных «строка». Вместо этого он поддерживает массивы символов, завершаемые нуль-символом. • Они позволяют достичь высокой эффективности, но весьма неудобны и небезопасны в использовании, поскольку выход за границы строки не проверяется. • Тип данных string стандартной библиотеки лишен этих недостатков, но может проигрывать массивам символов в эффективности. • Основные действия со строками выполняются в нем с помощью операций и методов, а длина строки изменяется динамически в соответствии с потребностями. Строки типа string защищены от выхода информации за их границы и с ними можно работать так же, как с любым встроенным типом данных. • Для использования класса необходимо подключить к программе заголовочный файл <string>.
Пример использования string #include <cstring> #include <string> #include <iostream> using namespace std; int main (){ char c1[80], c2[80], сЗ[80];// Строки с завершающим нулем string s1, s2, s3; // Присваивание строк strcpy(c1, "old string one"); strcpy(c2, cl); s1 = "new string one"; s2 = s1; // Конкатенация строк strcpy(c3, c1); strcat(c3, c2); s3 = s1 + s2; // Сравнение строк if (strcmp(c2, c3) < 0 ) …; else …; if (s2 < s3) …; else …; }
Конструкторы и присваивание строк • Конструкторы: • string(); • string(const char *); • string(const char *,int n); • string(string&); • Операторы присваивания • string& operator=(const string& str); • string& operator=(const char* s); • string& operator=(char c); • Примеры: • string s1; • string s2("Вася"); • string s3(s2); • s1 = 'X'; • s1 = "Вася"; • s2 = s3;
Операции Операция Действие Операция Действие = присваивание > больше + конкатенация >= больше или равно == равенство [ ] индексация != неравенство « вывод < меньше » ввод <= меньше или равно += добавление
Методы • Вставка • insert(size_type pos, const string& str): • insert(size_type pos, const string& str, size_type pos2, size_type n); • insert(size_type pos, const char* s, size_type n); • Удаление • erase(size_type pos = 0, size_type n = npos); • clear(); • Выделение подстроки • string substr(size_type pos = 0, size_type n = npos); • Преобразование в массив символов • const char* c_str(); • Поиск подстрок • size_type find(const string &s, size_type pos = 0); • size_type find(char с, size_type pos = 0); • size_type rfind(const string& str, size_type pos = npos); • size_type rfind(char с, size_type pos = npos); • size_type find_first_of(char с, size_type pos = 0); • size_type find_last_of(char с, size_type pos = npos); • size_type find_first_of(const string& str, size_type pos = 0); • size_type find_last_of(const string& str, size_type pos = npos); • …
Пример применения методов поиска #include <string> #include <iostream> using namespace std; int main (){ string s1("лесная королева"), s2("ле"); cout « "s1= " « s1 « endl; cout « "s2= " « s2 « endl; int i = s1.find(s2); int j = s1.rfind(s2); cout « "первое s2 в s1 " « i « endl; cout « "последнее s2 в s1 " « j « endl; cout « "первое 'о' в s1 " « s1.find('o') « endl; cout « "последнее 'о' в s1 " « s1.rfind('o') « endl; cout « "первое в s1 " « s1.find_first_of(«абвгде») « endl; cout « "последнее в s1 " « s1.find_last_of(«абвгде») « endl; } Результат работы программы: sl= лесная королева s2= ле первое s2 в s1 0 последнее s2 в s1 11 первое 'о' в s1 8 последнее 'о' в s1 10 первое в s1 1 последнее в s1 14
Получение характеристик строк • size_type size() const; // Количество элементов строки • size_type length() const; // Количество элементов строки • size_type max_size() const; // Максимальная длина строки • size_type capacity() const; // Объем памяти, занимаемый строкой • bool empty()const; // Истина, если строка пустая
Потоковые классы • Поток — это абстрактное понятие, относящееся к любому переносу данных от источника к приемнику. • Потоки C++ обеспечивают • надежную работу как со стандартными, так и с определенными пользователем типами данных, • единообразный и понятный синтаксис. • Чтение данных из потока называется извлечением, вывод в поток — помещением, или включением. • Поток определяется как последовательность байтов и не зависит от конкретного устройства, с которым производится обмен (оперативная память, файл на диске, клавиатура или принтер). • По направлению обмена потоки можно разделить на • входные (данные вводятся в память), • выходные (данные выводятся из памяти) • двунаправленные (допускающие как извлечение, так и включение). • По виду устройств, с которыми работает поток: • Стандартные (для передачи данных от клавиатуры и на экран дисплея), • Файловые (для обмена информацией с файлами на внешних носителях данных ), • Строковые (для работы с массивами символов в оперативной памяти).
Иерархия классов для поддержки потоков • Базовые классы: • ios – содержит общие для ввода и вывода поля и методы, • streambuf обеспечивает буферизацию потоков и их взаимодействие с физическими устройствами. • От этих классов наследуется класс istream для входных потоков и ostream — для выходных. • Два последних класса являются базовыми для класса iostream, реализующего двунаправленные потоки. • Ниже в иерархии располагаются файловые и строковые потоки: • istringstream – класс входных строковых потоков; • ostringstream – класс выходных строковых потоков; • iostringstream – класс двунаправленных строковых потоков; • ifstream — класс входных файловых потоков; • ofstream — класс выходных файловых потоков; • fstream — класс двунаправленных файловых потоков.
Основные заголовочные файлы с описаниями классов потоков • #include <iostream> – стандартные объекты и операции с потоками ввода/вывода; • #include <fstream> – потоки ввода/вывода в файлы; • #include <sstream> – потоки ввода/вывода в строки; • Необходимо использовать пространство имен std • using namespace std; • Недостаток потоков – снижение быстродействия, для некоторых компиляторов может быть весьма значительным.
Стандартные потоки • Заголовочный файл <iostream> содержит, кроме описания классов для ввода/вывода, четыре предопределенных объекта: В классах istream и ostream операции извлечения из потока “»” и помещения в поток “«” определены путем перегрузки операций сдвига.
Пример использования стандартных потоков #include <iostream.h> int main(){ inti; cin » i; cout « "Вы ввели " « i; return 0; } • Операции извлечения и чтения в качестве результата своего выполнения формируют ссылку на объект типа istream для извлечения и ссылку на ostream — для чтения.
Особенности ввода • Помещение в буфер ввода происходит после нажатия клавиши перевода строки, после чего из буфера выполняется операция извлечения из потока. • Это дает возможность исправлять введенные символы до того, как нажата клавиша Enter. • Привводе строк извлечение происходит до ближайшего пробела (вместо него в строку заносится нуль-символ): char str1[100], str2[100]; cin » str1 » str2; • Если с клавиатуры вводится строка "раз два три четыре пять", переменные str1 и str2 примут значения "раз" и "два" соответственно, а остаток строки воспринят не будет . • При необходимости ввести из входного потока строку целиком (до символа '\n') пользуются методами get или getline.
Форматирование вывода • В потоковых классах форматирование выполняется тремя способами: • c помощью флагов; • c помощью манипуляторов; • c помощью форматирующих методов.
Флаги форматирования • Для управления флагами в классе ios есть методы flags, setf и unsetf.
Форматирующие методы • int ios::width() - возвращает значение ширины поля вывода; • int ios::width(int) - устанавливает ширину поля вывода в соответствии созначением параметра; • int ios::precision()—возвращает значение точности представления при выводе вещественных чисел; • int ios:: precision(int) — устанавливает значение точности представления при выводе вещественных чисел, возвращает старое значение точности; • char ios::fill() — возвращает текущий символ заполнения; • char ios::fill(char) — устанавливает значение текущего символа заполнения, возвращает старое значение символа.
Пример использования флагов и методов форматирования #include <iostream.h> int main(){ long a = 1000, b = 077; cout.width(7); cout.setf(ios::hex | ios::showbase | ios::uppercase); cout «a; cout.width(7); cout« b « endl; double d = 0.12, с = 1.3е-4; cout.setf(ios::left); cout« d « endl; cout« c; return 0; } • В результате работы программы в первой строке будут прописными буквами выведены переменные а и b в шестнадцатеричном представлении, под каждую из них отводится по 7 позиций (функция width действует только на одно выводимое значение, поэтому ее вызов требуется повторить дважды). Значения переменных d и сприжаты к левому краю поля: 0ХЗЕ8 0X3F 0.12 0.00013
Манипуляторы • Манипуляторами называются функции, которые можно включать в цепочку операций помещения и извлечения для форматирования данных. • Манипуляторы делятся на простые, не требующие указания аргументов, и параметризованные. • Пользоваться манипуляторами более удобно, чем методами установки флагов форматирования.
Простые манипуляторы • dec— устанавливает при вводе и выводе флаг десятичной системы счисления; • oct— устанавливает при вводе и выводе флаг восьмеричной системы счисления; • hex — устанавливает при вводе и выводе флаг шестнадцатеричной системы счисления; • ws— устанавливает при вводе извлечение пробельных символов; • endl— при выводе включает в поток символ новой строки и выгружает буфер; • ends — при выводе включает в поток нулевой символ; • fIush— при выводе выгружает буфер. • Например: : cout« 13 « hex « ' ' « 13 « oct« ' ' « 13 «endl; Если другие значения флагов установлены но умолчанию, будет выведено: 13 d 15
Параметризованные манипуляторы • Для использования требуется подключить к программе заголовочный файл <iomanip>. • setbase(int n)— задает основание системы счисления (n = 8, 16, 10 или 0).0 является основанием по умолчанию (десятичное, кроме случаев, когда вводятся 8- или 16-ричные числа); • resetiosflags(long)— сбрасывает флаги состояния потока, биты которых установлены в параметре; • setiosflags(long) — устанавливает флаги состояния потока, биты которых в параметре равны 1; • setfill(int) — устанавливает символ-заполнитель с кодом, равным значению параметра; • precision(int)— устанавливает максимальное количество цифр в дробной части для вещественных чисел в форме с фиксированной точкой (флаг fixed) или общее количество значащих цифр для чисел в форме с мантиссой и порядком (флаг scientific); • setw(int)— устанавливает минимальную ширину поля вывода,
Пример использования параметризированных манипуляторов #include <iostream.h> #include <iomanip.h> intmain(){ double d[] = {1.234, -12.34567, 123.456789,-1.234., 0.00001}; cout« setfill('.') « setprecision(4) « setiosflags(ios: :showpoint | ios::fixed); for (inti= 0: i< 5; i++) cout« setw(12) « d[i] « endl; return 0; } Результат работы программы: ......1.2340 ....-12.3457 ....123.4568 .....-1.2340 ......0.0000
Методы обмена с потоками (1) • В потоковых классах наряду с операциями извлечения » и включения « определены методы для неформатированного чтения и записи в поток (при этом преобразования данных не выполняются). • gcount()— возвращает количество символов, считанных с помощью последней функции неформатированного ввода; • get()– возвращает код извлеченного из потока символаили EOF; • get(c)– возвращает ссылку на поток, из которого выполнялось чтение, и записывает извлеченный символ в с; • get(buf,num,lim = ‘\n’)– считывает num-1 символов (или пока не встретитсясимвол lim) и копирует их в символьную строкуbuf. Вместо символа limв строку записывается признак конца строки ('\0'). Символ limостается в потоке. Возвращает ссылку на текущий поток; • getline(buf,num,lim = ‘\n’)– аналогична функции get, но копирует в bufи символ lim; • ignore(num = 1, lim = EOF)– считывает и пропускает символы до тех пор, покане будет прочитано numсимволов или не встретитсяразделитель, заданный параметром lim. Возвращаетссылку на текущий поток; • peek()– возвращает следующий символ без удаления его изпотока или EOF, если достигнут конец файла; • putback(c)– помещает в поток символ с, который становится текущим при извлечении из потока; • read(buf,num)– считывает numсимволов (или все символы до конца файла, если их меньше num) в символьный массив bufи возвращает ссылку на текущий поток; • readsome(buf,num)– считывает numсимволов (или все символы до конца файла, если их меньше num) в символьный массив bufи возвращает количество считанных символов.
Методы обмена с потоками (2) • flush()– записывает содержимое потока вывода на физическое устройство; • put(c)– выводит в поток символ с и возвращает ссылку на поток; • seekg (pos)- устанавливает текущую позицию записи в значение pos; • seekg (offs, org)— перемещает текущую позицию записи на offs байтов, считая от одной из трех позиций, определяемых параметром org: ios:: beg (от начала файла), ios::cur (от текущей позиции) или ios: :end (от конца файла); • tellg()– возвращает текущую позицию записи потока; • write(buf,num)– записывает в поток numсимволов из массива bufи возвращает ссылку на поток.
Пример чтения строки #include "iostream.h" int main(){ const int N = 20,Len = 100; char str [N] [Len]; int i = 0; while (cin.getline(str[i], Len, '\n') && i<N){ // … i++; } return 0; }
Файловые потоки • По способу доступа файлы можно разделить на • последовательные (чтение и запись производятся с начала байт за байтом) • с произвольным доступом(допускающие чтение и запись в указанную позицию) • Стандартная библиотека содержит три класса для работы с файлами: • ifstream— класс входных файловых потоков; • ofstream—класс выходных файловых потоков; • fstream— класс двунаправленных файловых потоков. • Эти классы являются производными от классов istream, ostreamи iostreamсоответственно, поэтому они наследуют перегруженные операции « и », флаги форматирования, манипуляторы, методы, состояние потоков и т. д. • Использование файлов в программе предполагает следующие операции: • создание потока; • открытие потока и связывание его с файлом; • обмен (ввод/вывод); . • уничтожение потока; • закрытие файла.
Конструкторы файловых потоков • Каждый класс файловых потоков содержит конструкторы, с помощью которых можно создавать объекты этих классов различными способами. • Конструкторы без параметров создают объект соответствующего класса, не связывая его с файлом: • ifstream(); • ofstream(); • fstream(); • Конструкторы с параметрами создают объект соответствующего класса, открывают файл с указанным именем и связывают файл с объектом: • ifstream(const char *name, int mode = ios::in); • ofstream(const char *name, int mode = ios::out | ios::trunc); • fstream(const char *name, int mode = ios::out | ios::in); • Другой способ открыть файл – использовать метод open, с параметрами как у конструктора.
Режимы открытия файлов • Вторым параметром конструктора является режим открытия файла. Если установленное по умолчанию значение не устраивает программиста, можно указать другое, составив его из битовых масок, определенных в классе ios: enum open_mode{ in= 0x01, // Открыть для чтения out= 0x02, // Открыть для записи ate= 0x04, // Установить указатель на конец файла app= 0x08, // Открыть для добавления в конец trunc= 0x10, // Если файл существует, удалить nocreate= 0x20, // Если файл не существует, выдать ошибку noreplace= 0x40, // Если файл существует, выдать ошибку binary= 0x80 // Открыть в двоичном режиме };
Строковые потоки • Строковые потоки позволяют считывать и записывать информацию из областей оперативной памяти так же, как из файла, с консоли или на дисплей. • В стандартной библиотеке определено три класса строковых потоков: • istringstream — входные строковые потоки; • ostringstream — выходные строковые потоки; • stringstream — двунаправленные строковые потоки. • Эти классы определяются в заголовочном файле <sstream> и являются производными от классов istream, ostream и iostream соответственно, поэтому они наследуют перегруженные операции « и », флаги форматирования, манипуляторы, методы, состояние потоков и т. д.
Создание строковых потоков • Участки памяти, с которыми выполняются операции извлечения и включения, по стандарту определяются как строки C++ (класс string). • Строковые потоки создаются и связываются с этими участками памяти с помощью конструкторов: • explicitistringstream(int mode = ios::in); • explicit istringstream(const string& name, int mode = ios::in); • explicit ostringstream(int mode = ios::out); • explicit ostringstream(const string& name, int mode = ios::out); • explicit stringstream(int mode = ios::in | ios::out); • explicit stringstream(const string& name, int mode = ios::in | ios::out); • explicit – означает, что конструктор может быть вызван только явным образом.
Пример использования строкового потока #include <sstream> #include <string> #include <iostream> #include <ctime> using namespace std; string message( int i){ ostringstream os; time_t t; time(&t); os « " time: " « ctime(&t) « " number: " « i « endl; return os.str(); } int main(){ cout << message(22); return 0; }
Потоки и типы, определенные пользователем • Для ввода и вывода в потоках используются перегруженные для всех стандартных типов операции чтения и извлечения « и ». При этом выбор конкретной операции определяется типом фактических параметров. • Для того чтобы вводить и выводить величины типов, определенных пользователем, требуется перегрузить эти операции. • Это бинарные операции, левым операндом которых является объект-поток, а правым — объект, который требуется извлечь или поместить в поток. • Возвращаемое значение должно быть ссылкой на поток, чтобы можно было организовывать цепочки операций, как и в случае стандартных типов
Пример использования потоков с типом пользователя сlass MyClass{ intx; float y; public: MyClass(int nx = 1, float ny = 0.01){x = nx; у = ny;} friend ostream& operator « (ostream& out, MyClass& C) { return out « "x = " « C.x « " у = " « С.y; } friend istreams operator » (istream& in,MyClass& C) { cout « "Введите x: “; in » C.x; cout « "Введите у: “; in » С.у; return in; } }; int main(){ MyClass C; cout « С « endl; MyClass C1(100, 100); cout « C1 « endl; MyClass C2; cin » C2; cout « C2 « endl; return 0; }
Стандартная библиотека шаблонов (STL) • Контейнеры • Итераторы • Алгоритмы
Контейнерные классы • Контейнеры – структуры данных, предназначенные для хранения набора однотипных объектов • Примерами контейнеров могут служить массивы, линейные списки или стеки. • Для каждого типа контейнера определены методы для работы с его элементами, не зависящие от конкретного типа данных, которые хранятся в контейнере, поэтому один и тот же вид контейнера можно использовать для хранения данных различных типов. • Использование контейнеров позволяет значительно повысить надежность программ, их переносимость и универсальность, а также уменьшить сроки их разработки.
Виды контейнеров • Последовательные • обеспечивают хранение конечного количества однотипных величин в виде непрерывной последовательности. • К ним относятся • векторы (vector), • двусторонние очереди (deque), • списки (list), • стеки (stack), • очереди (queue), • очереди с приоритетами (priority_queue). • Ассоциативные • обеспечивают быстрый доступ к данным по ключу. • построены на основе сбалансированных деревьев. • Существует пять типов ассоциативных контейнеров: • словари (mар), • словари с дубликатами (multimap), • множества (set), • множества с дубликатами (multiset); • битовые множества (bitset).
Последовательные контейнеры Вектор — это структура, эффективно реализующая произвольный доступ к элементам, добавление в конец и удаление из конца. Двусторонняя очередь эффективно реализует произвольный доступ к элементам, добавление в оба конца и удаление из обоих концов. Список эффективно реализует вставку и удаление элементов в произвольное место, но не имеет произвольного доступа к своим элементам.
Пример работы с вектором #include <fstream> #include <vector> using namespace std; int main(){ ifstream in ("inpnum.txt"); vector<int> v; int x; while ( in » x && !in.eof()) v.push_back(x); for (vector<int>::iterator i = v.begin(); i != v.end(); ++i) cout « *i « " "; }
Деки • Различие дека и вектора в том, что динамический массив дека открыт с двух концов.
Списки • Элементы контейнера list библиотеки STL объединены в двусвязный список. • Для использования списков необходимо подключить заголовочный файл <list> • Список не поддерживает произвольный доступ к элементам • В списках не определён оператор индексирования ([]) и функция at() • Вставка и удаление элементов в любой позиции выполняются быстро. • После вставки и удаления элементов указатели на др. элементы остаются действительными • Список не может оказаться в промежуточном состоянии (операция неделима) • Списки не перераспределяют память
#include "stdafx.h" #include <stdlib.h> #include <iostream> #include <list> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { list<int> v; for(int i=0;i<10;i++) v.push_back(rand()); cout<< “В списке ” << v.size() << “элементов” <<endl; cout << endl <<endl; return 0; }
Словари и словари с дубликатами • Элементами являются пары «ключ/значение». • Сортировка элементов проводится по ключу. #include <map> template <class Key, class T, class Compare = less<Key>, class Allocator=allocator<pair<const Key,T> > > • Первый аргумент шаблона определяет тип ключа, второй- тип значения. • Необязательный третий аргумент – критерий сортировки. • Необязательный четвёртый параметр – модель памяти.
Критерии сортировки Критерий сортировки определяется тремя свойствами: • Ассиметричность: • Для оператора <: если выражение x<y истинно, то выражение y<x ложно. • Для предиката op(): если выражение op(x,y) истинно, то выражение op(y,x) ложно. 2. Транзитивность: • Для оператора <: если выражения x<y и y<z истинны, то выражение x<z истинно. • Для предиката op(x,y) и op(y,z) истинны, то выражение op(x,z) истинно. 3. Иррефлексивность: • Для оператора <: выражение x<x всегда ложно. • Для предиката op(): выражение op(x,x) всегда ложно. Для проверки равества используестся следующее правило: «Два элемента равны, если ни один из них не меньше другого (или если ложны оба выражения op(x,y) и op(y,x).
#include <map> #include <string> #include <iostream> using namespace std; int main() { map<int,string> m1; m1[1] ="Иванов"; map<string,int> m2; m2["Петров"] = 5; int x = m2[“Сидоров”]; cout << "m1[1]=" << m1[1] << endl; cout << "m2[\"Петров\"]=" << m2["Петров"] << endl; return 0; }
Множества и множества с дубликатами