Олег Логинов Стандартная библиотека С++ Лекция 5. Стандартная
Олег Логинов Стандартная библиотека С++ Лекция 5. Стандартная библиотека шаблонов STL – Standard Template Library. Алгоритмы.
Nortel Networks Confidential STL Алгоритмы - стандартные алгоритмы, предназначенные для обработки элементов коллекций. Под обработкой понимается выполнение таких стандартных операций, как поиск, сортировка, копирование, переупорядочение, модификация и численные расчеты. Алгоритмы не являются методами контейнерных классов - это глобальные функции, работающие с итераторами. Вместо того чтобы реализовывать каждый алгоритм для каждого типа контейнера, достаточно реализовать его один раз для обобщенного типа контейнера. Такой подход сокращает объем программного кода, делая библиотеку более мощной и гибкой. STL - Алгоритмы У такого подхода есть одно важное достоинство:
Nortel Networks Confidential Речь идет не о парадигме ООП (объектно-ориентированного программирования), а об общей парадигме функционального программирования. Вместо того чтобы объединять данные с операциями, как в ООП, мы разделяем их па части, взаимодействующие через некоторый интерфейс. У этого подхода есть свои негативные стороны: Такой подход недостаточно интуитивен. Некоторые комбинации структур данных и алгоритмов могут оказаться недопустимыми или, что еще хуже, допустимыми, но бесполезными (например, обладающими недостаточным быстродействием). STL - Алгоритмы
Nortel Networks Confidential STL – Алгоритмы. Пример. #include
Nortel Networks Confidential STL – Алгоритмы. Пример. Продолжение… ... // Сортировка всех элементов sort (coll.begin(), coll.end()); // Поиск первого элемента со значением, равным 3 pos = find (coll.begin(), coll.end(), // Интервал 3); // Значение // Перестановка найденного элемента со значением 3 // и всех последующих элементов в обратном порядке. reverse (pos, coll.end()); // Вывод всех элементов for (pos = coll.begin(); pos != coll.end(); ++pos) { cout << *pos << ' '; } cout << endl; } 1 2 6 5 4 3 Результат
Nortel Networks Confidential Любой алгоритм работает с одним или несколькими интервалами, заданными начальным и конечным итератором. itResult = min_element(itBeg, itEnd); itResult = min_element(coll.begin(), coll.end()); При вызове алгоритма начало и конец интервала передаются в двух разных аргументах (вместо того, чтобы передавать всю коллекцию в одном аргументе). Все алгоритмы работают с полуоткрытыми интервалами. Т.е., интервал включает заданную начальную позицию, но конечная позиция в него не включается: [начало, конец) Достоинства полуоткрытых интервалов обсуждались ранее… Интервал может (хотя и не обязан) содержать все элементы контейнера. Интервалы.
Nortel Networks Confidential Если это условие не выполняется, возможны непредсказуемые последствия, включая зацикливание или нарушение защиты памяти. В этом отношении итераторы так же ненадежны, как обычные указатели. Реализации STL могут выявлять подобные ошибки и обрабатывать их по своему усмотрению. itResult = min_element(itBeg, itEnd); Такой интерфейс чрезвычайно гибок, но потенциально опасен. Интервалы.
Nortel Networks Confidential Интервалы. Плохой пример. #include #include
Nortel Networks Confidential Интервалы. Плохой пример. #include #include
Nortel Networks Confidential Интервалы. Плохой пример. #include #include
Nortel Networks Confidential Интервалы. Плохой пример. #include #include
Nortel Networks Confidential Интервалы. Плохой пример. #include #include
Nortel Networks Confidential Если это условие не выполняется, возможны непредсказуемые последствия, включая зацикливание или нарушение защиты памяти. В этом отношении итераторы так же ненадежны, как обычные указатели. Реализации STL могут выявлять подобные ошибки и обрабатывать их по своему усмотрению. itResult = min_element(itBeg, itEnd); Такой интерфейс чрезвычайно гибок, но потенциально опасен. Интервалы. И еще раз…
Nortel Networks Confidential Как исправить предыдущий пример?! if (pos25 < pos35) { // Действителен интервал [pos25, pos35) ... } else if (pos35 < pos25) { // Действителен интервал [pos35, pos25) ... } else { // Итераторы равны; // следовательно, оба итератора находятся в позиции end() ... } При использовании итератора произвольного доступа проверка выполняется оператором <: Но такой подход применим только к коллекциям с итератором произвольного доступа: vector, deque, string (и обычный массив).
Nortel Networks Confidential Как исправить предыдущий пример?! pos25 = find (coll.begin(), coll.end(), 25); pos35 = find (coll.begin(), pos25, 35); if (pos35 != pos25) { // pos35 предшествует pos25; действителен только интервал [pos35, pos25) ... else { pos35 = find (pos25, coll.end(), 35); if (pos35 != pos25) { // pos25 предшествует pos35; действителен только интервал [pos25, pos35) ... else { // Итераторы равны; оба итератора находятся в позиции end() ... } } Единственное разумное решение - провести поиск одного итератора в интервале от начала до другого итератора или от другого итератора до конца. В этом случае алгоритм слегка изменяется: вместо того чтобы искать оба значения во всем исходном интервале, мы пытаемся определить, какое значение встречается первым: Эффективность подобной реализации оставляет желать лучшего.
Nortel Networks Confidential Некоторые алгоритмы работают сразу с несколькими интервалами. Обычно в таких случаях задаются начало и конец только одного интервала для остальных интервалов задается только начало Т.о. конечная позиция других интервалов определяется по количеству элементов в первом интервале. Например, следующий вызов equal() поэлементно сравнивает все содержимое коллекции colli с элементами со112, начиная с первого: Использование нескольких интервалов. // Вызов equal() поэлементно сравнивает все содержимое // коллекции coll1 с элементами соll2, начиная с первого элемента: if ( equal (coll1.begin(), coll1.end(), coll2.begin()) ) { ... }
Nortel Networks Confidential Использование нескольких интервалов. equal (coll1.begin(), coll1.end(), coll2.begin());
Nortel Networks Confidential #include #include
Nortel Networks Confidential ... // Вставка элементов со значениями от 1 до 9 for (int i = 1; i <= 9; ++i) { coll1.push_back(i); } // Изменение размера приемного интервала, чтобы он // были достаточен для работы алгоритма с перезаписью. coll2.resize (coll1.size()); // Копирование элементов из первой коллекции во вторую // - перезапись существующих элементов в приемном интервале copy (coll1.begin(), coll1.end(), // Источник coll2.begin()); // Приемник // Создание третьей коллекции с необходимым количеством элементов // - исходный размер передается в параметре deque
Nortel Networks Confidential Итераторные адаптеры - несколько специализированных итераторов, содержащихся в стандартной библиотеке С++. Итераторы, в данном контексте, являются чистыми абстракциями. Иначе говоря, любой объект, который ведет себя как итератор, является итератором. Итераторные адаптеры - классы, которые обладают интерфейсом итератора, но делают нечто совершенно иное. (По сравнению с обычными итераторами, которые предназначены лишь для перебора элементов коллекций.) Итераторные адаптеры наделяют саму концепцию итераторов рядом новых возможностей. Три стандартные разновидности итераторных адаптеров: итераторы вставки потоковые итераторы обратные итераторы Итераторные адаптеры.
Nortel Networks Confidential Итераторы вставки - позволяют использовать алгоритмы в режиме вставки (вместо режима перезаписи). Итераторы вставки решают проблему с нехваткой места в приемном интервале при записи: приемный интервал просто увеличивается до нужных размеров. Поведение итераторов вставки слегка переопределено: Если присвоить значение элементу, ассоциированному с итератором, это значение вставляется в коллекцию, с которой связан данный итератор: Операция перехода вперед (operator ++) не производит никаких действий. Три разновидности итераторов вставки позволяют вставлять элементы в разных местах - в начале, в конце или в заданной позиции. Итераторы вставки. *( inserter(coll4, coll4.begin()) ) = 5;
Nortel Networks Confidential ... list
Nortel Networks Confidential back_inserter (контейнер) Элементы присоединяются с конца в прежнем порядке с использованием функции push_back(). front_inserter (контейнер) Элементы вставляются в начало в обратном порядке с использованием функции push_front(). inserter (контейнер, позиция) Элементы вставляются в заданной позиции в прежнем порядке с использованием функции insert(). Итераторы вставки. Если переданная позиция окажется неверной, на выполнение операции может понадобиться больше времени, чем вообще без рекомендации.
Nortel Networks Confidential Потоки (данных) - объекты, представляющие каналы ввода-вывода. Потоковые итераторы - итераторы, обеспечивающие чтение из потока данных и запись в поток данных. Потоковые итераторы позволяют интерпретировать ввод с клавиатуры как коллекцию, из которой можно читать данные. Аналогично, результаты работы алгоритма можно перенаправить в файл или на экран. Потоковые итераторы. Итераторы, в данном контексте, являются чистыми абстракциями. Иначе говоря, любой объект, который ведет себя как итератор, является итератором. И еще раз…
Nortel Networks Confidential #include
Nortel Networks Confidential Обратные итераторы – итераторы, которые работают в противоположном направлении: вызов оператора ++ на внутреннем уровне преобразуется в вызов оператора --, и наоборот. Все контейнеры поддерживают создание обратных итераторов функциями rbegin() и rend(). Благодаря обратным итераторам все алгоритмы получают возможность работать в обратном направлении без модификации кода. «Нормальные» итераторы можно переключать в режим обратного перебора и наоборот. (Но при этом изменяется текущий элемент, связанный с итератором.) Обратные итераторы. Итераторы, в данном контексте, являются чистыми абстракциями. Иначе говоря, любой объект, который ведет себя как итератор, является итератором. И еще раз…
Nortel Networks Confidential #include
Nortel Networks Confidential ... list
Nortel Networks Confidential После вызова remove() функция end() возвращает прежнее значение, а функция size() - прежнее количество элементов. «Удаление» элементов. Алгоритм remove().
Nortel Networks Confidential ... // Удаление всех элементов со значением 3 // - сохранение нового логического конца коллекции list
Nortel Networks Confidential ... // Удаление и стирание всех элементов со значением 3 coll.erase (remove(coll.begin(), coll.end(), 3), coll.end()); // Вывод всех элементов модифицированной коллекции copy (coll.begin(), coll.end(), ostream_iterator
Nortel Networks Confidential Модифицирующие алгоритмы – алгоритмы, изменяющие содержимое коллекции. (Например remove().) Это алгоритмы, которые удаляют элементы, изменяют порядок их следования или их значения. Т.о. ассоциативные контейнеры не могут быть приемниками. Причина проста: работая с ассоциативным контейнером, модифицирующий алгоритм мог бы изменить значения или позиции элементов, что привело бы к нарушению порядка их сортировки. Тем самым будет нарушено основное правило, согласно которому элементы ассоциативных контейнеров всегда сортируются автоматически по заданному критерию. Следовательно, попытки модификации элементов ассоциативного контейнера через итератор вызывают ошибки на стадии компиляции. А значит попытка вызова любого модифицируещего алгоритма (например remove()) приведет к ошибке компилляции. Модифицирующие алгоритмы и ассоциативные контейнеры.
Nortel Networks Confidential Как же удалить элементы из ассоциативных контейнеров? Для удаления элементов из ассоциативных контейнеров используются собственные методы контейнера. В каждом ассоциативном контейнере предусмотрены методы удаления элементов. Например, элементы можно удалить функцией erase(). Контейнеры поддерживают несколько версий функции erase(). Только та версия, единственным аргументом которой является значение удаляемого элемента (или элементов), возвращает количество удаленных элементов. Очевидно, если дубликаты в коллекции запрещены (как в множествах и отображениях), возвращаемое значение функции может быть равно только 0 или 1. Удаление элементов из ассоциативных контейнеров.
Nortel Networks Confidential ... set
Nortel Networks Confidential Хорошим примером служит вызов алгоритма remove() для элементов списка. Алгоритм remove() не знает, что он работает со списком. Поэтому он , как обычно, переупорядочивает элементы, изменяя их значения. Например, при удалении первого элемента каждому элементу присваивается значение элемента, следующего после него. Такой подход игнорирует основное достоинство списков - возможность выполнения вставки, перемещения и удаления элементов посредством модификации ссылок, а не элементов. Для повышения эффективности операций в списках определены специальные методы для всех модифицирующих алгоритмов. Более того, эти методы действительно исключают удаляемые элементы. Алгоритмы и методы контейнеров.
Nortel Networks Confidential ... list
Nortel Networks Confidential Вы должны заранее знать, что некоторый контейнер содержит метод, обладающий более высоким быстродействием. Увы, при вызове неэффективного алгоритма (например, remove()) не последует ни предупреждения, ни сообщения об ошибке. С другой стороны, если выбрать метод, то при переходе на другой тип контейнера вам придется вносить изменения в программу. Если не уверены, что эффективнее по быстродействию – алгоритм или метод – воспользуйтесь справочником алгоритмов. Алгоритмы и методы контейнеров. И еще раз…
Nortel Networks Confidential Библиотека STL имеет расширяемую архитектуру. Программист может создавать собственные функции и алгоритмы для обработки коллекций. Такие операции тоже должны быть унифицированными. Чтобы упростить написание унифицированных функций, в каждом контейнерном классе присутствуют внутренние определения вспомогательных типов. Это очень удобно, например, при объявлении итератора. Ведь при указании типа итератора необходимо указывать тип контейнера. Ключевое слово typename указывает, что iterator – тип, а не значение. Помимо типов iterator и const_iterator контейнеры предоставляют и другие типы, упрощающие написание унифицированных функций. (Например, тип элементов для выполнения операций с временными копиями элементов.) Унифицированные пользовательские функции. typename T::iterator pos; Повторение…
Nortel Networks Confidential #include
Nortel Networks Confidential Некоторым алгоритмам через их аргументы могут передаваться пользовательские функции, которые вызываются в процессе внутренней работы алгоритма. Такая возможность делает алгоритмы еще более гибкими и мощными. Функциональный аргумент – аргумент (алгоритма, функции или метода класса), который «ведет себя как функция». В простейшем случае, это просто указатель на функцию. В некоторых случаях передача функции алгоритму обязательна, в других – нет (например, когда задано значение по умолчанию для функционального аргумента, или есть перегрузка с меньшим количеством аргументов). Функциональный аргумент может определять критерий поиска, критерий сортировки или преобразование, применяемое при копировании элементов из одной коллекции в другую. Передача функций алгоритмам. Повторение…
Nortel Networks Confidential #include
Nortel Networks Confidential ... // Вывод всех элементов for_each (coll.begin(), coll.end(), // Интервал print); // Операция Функциональные аргументы. Пример 1. namespace std { template
Nortel Networks Confidential ... #include "print.h" int square (int value) { return value * value; } int main() { set
Nortel Networks Confidential ... #include "print.h" int square (int value) { return value * value; } int main() { set
Nortel Networks Confidential Предикаты Предикат – вспомогательная функция, которая возвращает логическое значение (в С++ тип bool – true/false). В алгоритмах предикаты часто опеределяют критерии сортировки и поиска. Унарный предикат – предикат с одним аргументом. bool f(int); template
Nortel Networks Confidential #include #include
Nortel Networks Confidential int main() { list
Nortel Networks Confidential Бинарный предикат. Пример. ... class Person { public: string firstname() const; string lastname() const; ... }; // Бинарный предикат: // - сравнивает два объекта Person bool personSortCriterion (const Person& p1, const Person& p2) { // Первый объект Person меньше второго, // - если фамилия в первом объекте меньше фамилии во втором объекте; // - или если фамилии равны, а имя в первом объекте меньше. return p1.lastname() < p2.lastname() || ( ! (p2.lastname() < p1.lastname() ) && p1.firstname() < p2.firstname() ); } int main() { deque
Nortel Networks Confidential Функторы. Функтор – объект, который ведет себя как функция. (объект-функция) Класс в С++ с перегруженным оператором () является функтором: Функциональный аргумент – аргумент, который «ведет себя как функция». В качестве функциональных аргументов часто используются предикаты и функторы. namespace std { template
Nortel Networks Confidential Функтор. Пример 1. #include
Nortel Networks Confidential ... // Вывод всех элементов for_each (coll.begin(), coll.end(), // Интервал print); // Операция - функция Функция и функтор. ... // Вывод всех элементов for_each (coll.begin(), coll.end(), // Интервал PrintInt()); // Операция - функтор Сравните… Создание экземпляра функтора. Вызов конструктора. Либо print (*act) Либо op.operator () (*act)
Nortel Networks Confidential Функтор – «умная функция». Функтор - это «умная функция», поскольку их возможности не ограничиваются вызовом оператора (). Функторы могут представлять другие функции и иметь другие атрибуты, т.е. функторы обладают состоянием. У обычных функций такая возможность отсутствует. Одно из главных достоинств функторов - возможность их инициализации на стадии выполнения перед вызовом/использованием. Каждому функтору соответствует свой тип. Функциональное поведение может передаваться при конструировании экземпляра функтора, или даже как параметр шаблона (если функтор – шаблон). Например, это позволяет контейнерам разных типов использовать единственный объект функции в качестве критерия сортировки. Тем самым предотвращается возможное присваивание, слияние и сравнение коллекций, использующих разные критерии сортировки. Функторы обычно работают быстрее функций, т.к. шаблоны обычно лучше оптимизируются.
Nortel Networks Confidential Пример с обычной функцией. void add10 (int& elem) { elem += 10; } void f1() { vector
Nortel Networks Confidential Пример с шаблонной функцией. template
Nortel Networks Confidential Пример с функтором. // Функтор прибавляет к значению элемента приращение, // заданное при его инициализации class AddValue { int theValue; // Приращение public: // Конструктор инициализирует приращение AddValue(int v) : theValue(v) { } // Суммирование выполняется "вызовом функции" для элемента void operator() (int& elem) const { elem += theValue; } }; int main() { list
Nortel Networks Confidential Пример с несколькими экземплярами функторов. AddValue addx(x); // Объект функции, прибавляющий значение x AddValue addy(y); // Объект функции, прибавляющий значение y for_each (coll.begin(), coll.end(), // Прибавление значения x addx); // к каждому элементу ... for_each (coll.begin(), coll.end(), // Прибавление значения у addy); // к каждому элементу ... for_each (coll.begin(), coll.end(), // Прибавление значения x addx); // к каждому элементу Наконец, можно иметь несколько независимых экземпляров функторов в разных состояниях. Иногда это удобно:
Nortel Networks Confidential Стандартные функторы. Стандартная библиотека С++ содержит набор функторов для выполнения базовых операций. Примеры: set
Nortel Networks Confidential Функциональные адаптеры. Специальные функциональные адаптеры позволяют объединять стандартные функторы с другими значениями или использовать их в особых ситуациях. (Они же функторные адаптеры – ”functor adapters”). Пример функционального адаптера: // TEMPLATE CLASS bind2nd - functor adapter _Func(left, stored) template
Nortel Networks Confidential Функциональные адаптеры. Пример. set
Nortel Networks Confidential Функциональные адаптеры. Функциональные адаптеры позволяют объединять стандартные функторы с другими значениями или использовать их в особых ситуациях. Подобный подход к программированию приводит к функциональной композиции. Все методы функторов обычно объявляются встраиваемыми (inline). Поэтому, несмотря на абстрактную функциональную запись, мы получаем хорошую производительность. И еще раз…
Nortel Networks Confidential Специальные функциональные адаптеры. Сушествуют и другие специальные разновидности функциональных адаптеров. Например, некоторые адаптеры позволяют вызвать определенный метод класса для каждого элемента коллекции: Адаптер mem_fun_ref вызывает заданный метод класса для того элемента, для которого этот объект вызывается. В приведенном примере для каждого элемента коллекции coll вызывается метод save() класса Person. Разумеется, эта конструкция работает только в том случае, если элемент относится к тину Person или производному от него. for_each (coll.begin(), coll.end(), // Интервал mem_fun_ref (&Person::save)); // Операция
Nortel Networks Confidential Основные требования к элементам контейнеров. Требуется возможность копирования элемента копирующим конструктором. Созданная копия должна быть эквивалентна оригиналу. Т.е., любая проверка на равенство должна считать копию и оригинал равными, а поведение копии не должно отличаться от поведения оригинала. Копирующий конструктор вызывается очень часто, поэтому он должен обладать хорошим быстродействием (рекомендация). Требуется возможность присваивания элемента оператором присваивания. Контейнеры и алгоритмы используют оператор присваивания для замены старых элементов новыми. Требуется возможность уничтожения элемента деструктором. Контейнеры уничтожают свои внутренние копии элементов при удалении этих элементов из контейнера. Следовательно, деструктор не должен быть закрытым (private). Эти три операции - копирование, присваивание, уничтожение - автоматически генерируются для любого класса. Следовательно, любой класс автоматически удовлетворяет требованиям.
Nortel Networks Confidential Дополнительные требования к элементам контейнеров. Для некоторых методов последовательных контейнеров требуется конструктор по умолчанию. Например, можно создать непустой контейнер или увеличить количество элементов без указания значений новых элементов. Такие элементы создаются вызовом конструктора по умолчанию для соответствующего типа. Для некоторых операций требуется определить проверку на равенство оператором ==. Такая необходимость особенно часто возникает при поиске. Для ассоциативных контейнеров требуется, чтобы элементы поддерживали критерий сортировки. По умолчанию используется оператор <, вызываемый функтором less().
Nortel Networks Confidential Семантика значений и ссылочная семантика. Семантика значений - контейнеры создают внутренние копии своих элементов и возвращают эти копии. Следовательно, элементы контейнера равны, но не идентичны объектам, заносимым в контейнер. При модификации объектов, являющихся элементами контейнера, вы модифицируете копию, а не исходный объект. Ссылочная семантика - контейнеры содержат ссылки (указатели) на объекты, являющиеся их элементами. На практике нужны оба подхода: копии, независимые от исходных данных (семантика значений), и копии, ссылающиеся на исходные данные и изменяющиеся вместе с ними (ссылочная семантика).
Nortel Networks Confidential Семантика значений и ссылочная семантика. Все контейнеры STL поддерживают семантику значений. Они содержат значения вставляемых объектов, а не сами объекты. Достоинства: простота копирования элементов при использовании ссылок часто возникают ошибки (программист должен следить за тем, чтобы ссылки не относились к несуществующим объектам, кроме того, необходимо предусмотреть обработку возможных циклических ссылок). Недостатки: при отказе от ссылочной семантики копирование элементов может выполняться неэффективно или становится невозможным невозможность одновременного присутствия объектов в нескольких контейнерах.
Nortel Networks Confidential Ссылочная семантика в STL. Очевидная реализация ссылочной семантики основана на использовании указателей как элементов контейнеров. Обычным указателям присущи хорошо известные недостатки. Например, объект, на который ссылается указатель, оказывается несуществующим, или операция сравнения работает не так, как предполагалось, потому что вместо объектов сравниваются указатели на них. Разумное решение основано на применении умных указателей с подсчетом ссылок. Но такое иснользование (умного указателя, который автоматически уничтожает связанный объект при уничтожении последней ссылки на него) может вызвать немало проблем. Например, при прямом доступе к элементам возможна модификация их значений, пока они находятся в контейнере. В ассоциативном контейнере это приведет к нарушению порядка следования элементов, а это недопустимо. auto_ptr не подходит, поскольку не удовлетворяет одному из основных требований к элементам контейнеров: после копирования или присваивания объектов класса auto_ptr оригинал и копия не эквивалентны.
Nortel Networks Confidential Обработка ошибок. Обработка ошибок снижает быстродействие, а высокая скорость работы по прежнему остается основной целью большинства программ. Тот, кто ставит на первое место надежность, может добиться своего при помощи интерфейсных оболочек или специальных версий STL. В результате проверка ошибок в STL возможна, но не обязательна. Обычно ошибки приводят к нарушению защиты памяти с неприятными побочными эффектами и даже сбоем программы. Применение стандартной библиотеки STL так же чревато ошибками, как применение указателей в языке С. Найти такие ошибки иногда бывает очень трудно, особенно если нет безопасной версии STL. В спецификации стандартной библиотеки С++ указано, что любое использование STL, нарушающее предварительные условия, приводит к непредсказуемому поведению.
Nortel Networks Confidential Для нормальной работы STL должны выполняться условия: Действительность итераторов. Например, перед использованием итераторы должны инициализироваться. Следует помнить, что итераторы могут стать недействительными вследствие побочных эффектов других операций. В частности, в векторах и деках это может произойти при вставке, удалении или перемещении элементов. Конечный итератор не связан с элементом контейнера, поэтому вызов операторов * и -> для него недопустим. В частности, это относится к возвращаемым значениям функций end() и rend() контейнерных классов. Действительность интервалов: итераторы, определяющие интервал, должны относиться к одному контейнеру; второй итератор должен быть достижим в результате перебора элементов, начиная от первого. Корректность источников: при использовании нескольких интервалов-источников второй и последующие интервалы должны содержать не меньше элементов, чем первый интервал. Корректность приемника: количество элементов в приемном интервале должно быть достаточным для перезаписи. В противном случае необходимо использовать итераторы вставки.
Nortel Networks Confidential Пример с грубыми ошибками при использовании STL. #include
Nortel Networks Confidential Генерация исключений в STL. Проверка логических ошибок в STL практически отсутствует, поэтому сама библиотека STL почти не генерирует исключения, связанные с логикой. Существует только один метод, для которой в стандарте прямо указано на возможность возникновения исключения: метод at() векторов и деков (проверяемая версия оператора индексирования []). Во всех остальных случаях стандарт требует лишь стандартных исключений типа bad_alloc при нехватке памяти или исключений, возникающих при пользовательских операциях. Стандартная библиотека С++ предоставляет базовую гарантию безопасности исключений: возникновение исключений не приводит к утечке ресурсов или нарушению контейнерных инвариантов. Часто требуется транзакционная безопасность: при возникновении исключения произойдет возврат к состоянию перед началом операции. (Другими словами: полное восстановление при выбрасывании исключения, т.е. операция либо завершается успешно либо не вносит изменений.) Лишь часть методов контейнеров гарантируют транзакционную безопасность исключений. (См. справочник.)
Nortel Networks Confidential Генерация исключений в STL. Проверка логических ошибок в STL практически отсутствует, поэтому сама библиотека STL почти не генерирует исключения, связанные с логикой. Существует только один метод, для которой в стандарте прямо указано на возможность возникновения исключения: метод at() векторов и деков (проверяемая версия оператора индексирования []). Во всех остальных случаях стандарт требует лишь стандартных исключений типа bad_alloc при нехватке памяти или исключений, возникающих при пользовательских операциях. Стандартная библиотека С++ предоставляет базовую гарантию безопасности исключений: возникновение исключений не приводит к утечке ресурсов или нарушению контейнерных инвариантов. Часто требуется транзакционная безопасность: при возникновении исключения произойдет возврат к состоянию перед началом операции. (Другими словами: полное восстановление при выбрасывании исключения, т.е. операция либо завершается успешно либо не вносит изменений.) Лишь часть методов контейнеров гарантируют транзакционную безопасность исключений. (См. справочник.) Все гарантии основаны на запрете исключений в деструкторах (который в С++ должен выполняться всегда). Стандартная библиотека С++ соблюдает это требование; его должны соблюдать и прикладные программисты.
Nortel Networks Confidential Расширение STL. Библиотека STL проектировалась с расчетом на возможность расширения практически в любом направлении. Программист может создавать и использовать собственные контейнеры, итераторы, алгоритмы и функторы, удовлетворяющие определенным требованиям. Кроме того, в стандартной библиотеке С++ (1998/2003) не поддерживаются некоторые полезные возможности. Более поздние версии стандарта (TR1 – 2005 г.) включили в себя новые контейнеры, основанные на хэш-таблицах. (unordered_map, unordered_set) новые контейнеры, основанные на статических массивах. (array) новый класс tuple (расширение идеи класса pair, «аналог» struct) Существуют другие полезных расширения – функторы, итераторы, контейнеры и алгоритмы.
Nortel Networks Confidential Конец
7346-std_05_stl_algorithms.ppt
- Количество слайдов: 73