Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Как определить последовательный контейнер?
Для того чтобы определить объект контейнерного типа, необходимо сначала включить соответствующий заголовочный файл: #include < vector> #inclnde < list> #include < deque> #include < map> #include < set> Определение контейнера начинается именем его типа, за которым в угловых скобках следует тип данных его элементов[12]. Например: vector< string > svec; list< int > ilist; Переменная svec определяется как вектор, способный содержать элементы типа string, а ilist – как список с элементами типа int. Оба контейнера при таком определении пусты. Чтобы убедиться в этом, можно вызвать функцию-член empty(): if ( svec.empty()! = true ) ; // что-то не так Простейший метод вставки элементов – использование функции-члена push_back(), которая добавляет элементы в конец контейнера. Например: string text_word; while ( cin > > text_word ) svec.push_back( text_word ); Здесь строки из стандартного ввода считываются в переменную text_word, и затем копия каждой строки добавляется в контейнер svec с помощью push_back(). Список имеет функцию-член push_front(), которая добавляет элемент в его начало. Пусть есть следующий массив: int ia[ 4 ] = { 0, 1, 2, 3 }; Использование push_back() for ( int ix=0; ix< 4; ++ix ) ilist.push_back( ia[ ix ] ); создаст последовательность 0, 1, 2, 3, а push_front() for ( int ix=0; ix< 4; ++ix ) ilist.push_front( ia[ ix ] ); создаст последовательность 3, 2, 1, 0. [13] Мы можем при создании явно указать размер массива – как константным, так и неконстантным выражением: #include < list> #include < vector> #include < string>
extern int get_word_count( string file_name ); const int list_size = 64;
list< int > ilist( list_size ); vector< string > svec(get_word_count(string(" Chimera" ))); Каждый элемент контейнера инициализируется значением по умолчанию, соответствующим типу данных. Для int это 0. Для строкового типа вызывается конструктор по умолчанию класса string. Мы можем указать начальное значение всех элементов: list< int > ilist( list_size, -1 ); vector< string > svec( 24, " pooh" ); Разрешается не только задавать начальный размер контейнера, но и впоследствии изменять его с помощью функции-члена resize(). Например: svec.resize( 2 * svec.size() ); Размер svec в этом примере удваивается. Каждый новый элемент получает значение по умолчанию. Если мы хотим инициализировать его каким-то другим значением, то оно указывается вторым параметром функции-члена resize(): // каждый новый элемент получает значение " piglet" svec.resize( 2 * svec.size(), " piglet" ); Кстати, какова наиболее вероятная емкость svec при определении, если его начальный размер равен 24? Правильно, 24! В общем случае минимальная емкость вектора равна его текущему размеру. При удвоении размера емкость, как правило, тоже удваивается Мы можем инициализировать новый контейнер с помощью существующего. Например: vector< string > svec2( svec ); list< int > ilist2( ilist ); Каждый контейнер поддерживает полный набор операций сравнения: равенство, неравенство, меньше, больше, меньше или равно, больше или равно. Сопоставляются попарно все элементы контейнера. Если они равны и размеры контейнеров одинаковы, то эти контейнеры равны; в противном случае – не равны. Результат операций “больше” или “меньше” определяется сравнением первых двух неравных элементов. Вот что печатает программа, сравнивающая пять векторов:
ivecl: 1 3 5 7 9 12 ivec2: 0 1 1 2 3 5 8 13 ivec3: 1 3 9 ivec4: 1 3 5 7 ivec5: 2 4
// первый неравный элемент: 1, О // ivecl больше чем ivec2 ivecl < ivec2 //false ivec2 < ivecl //true
// первый неравный элемент: 5, 9 ivecl < ivec3 //true
// все элементы равны, но ivec4 содержит меньше элементов // следовательно, ivec4 меньше, чем ivecl ivecl < ivec4 //false
// первый неравный элемент: 1, 2 ivecl < ivec5 //true
ivecl == ivecl //true ivecl == ivec4 //false ivecl! = ivec4 //true
ivecl > ivec2 //true ivec3 > ivecl //true ivec5 > ivec2 //true
Существуют три ограничения на тип элементов контейнера (практически это касается только пользовательских классов). Для должны быть определены: · операция “равно”; · операция “меньше” (все операции сравнения контейнеров, о которых говорилось выше, используют только эти две операции сравнения); · значение по умолчанию (для класса это означает наличие конструктора по умолчанию). Все предопределенные типы данных, включая указатели и классы из стандартной библиотеки С++ удовлетворяют этим требованиям. Упражнение 6.5 Объясните, что делает данная программа:
Упражнение 6.6 Может ли емкость контейнера быть меньше его размера? Желательно ли, чтобы емкость была равна размеру: изначально или после вставки элемента? Почему? Упражнение 6.7 Если программа из упражнения 6.5 прочитает 256 слов, то какова наиболее вероятная емкость контейнера после изменения размера? А если она считает 512 слов? 1000? 1048? Упражнение 6.8 Какие из данных классов не могут храниться в векторе:
Итераторы Итератор предоставляет обобщенный способ перебора элементов любого контейнера – как последовательного, так и ассоциативного. Пусть iter является итератором для какого-либо контейнера. Тогда ++iter; перемещает итератор так, что он указывает на следующий элемент контейнера, а *iter; разыменовывает итератор, возвращая элемент, на который он указывает. Все контейнеры имеют функции-члены begin() и end(). · begin() возвращает итератор, указывающий на первый элемент контейнера. · end() возвращает итератор, указывающий на элемент, следующий за последним в контейнере. Чтобы перебрать все элементы контейнера, нужно написать: for ( iter = container. begin(); iter! = container.end(); ++iter ) do_something_with_element( *iter ); Объявление итератора выглядит слишком сложным. Вот определение пары итераторов вектора типа string: // vector< string> vec; vector< string>:: iterator iter = vec.begin(); vector< string>:: iterator iter_end = vec.end(); В классе vector для определения iterator используется typedef. Синтаксис vector< string>:: iterator ссылается на iterator, определенный с помощью typedef внутри класса vector, содержащего элементы типа string. Для того чтобы напечатать все элементы вектора, нужно написать: for(; iter! = iter_end; ++iter ) cout < < *iter < < '\n'; Здесь значением *iter выражения является, конечно, элемент вектора. В дополнение к типу iterator в каждом контейнере определен тип const_iterator, который необходим для навигации по контейнеру, объявленному как const. const_iterator позволяет только читать элементы контейнера: #include < vector> void even_odd( const vector< int> *pvec, vector< int> *pvec_even, vector< int> *pvec_odd ) { // const_iterator необходим для навигации по pvec vector< int>:: const_iterator c_iter = pvec-> begin(); vector< int>:: const_1terator c_iter_end = pvec-> end();
for (; c_iter! = c_iter_end; ++c_iter ) if ( *c_iter % 2 ) pvec_even-> push_back( *c_iter ); else pvec_odd-> push_back( *c_iter ); } Что делать, если мы хотим просмотреть некоторое подмножество элементов, например взять каждый второй или третий элемент, или хотим начать с середины? Итераторы поддерживают адресную арифметику, а значит, мы можем прибавить некоторое число к итератору: vector< int>:: iterator iter = vec-> begin()+vec.size()/2; iter получает значение адреса элемента из середины вектора, а выражение iter += 2; сдвигает iter на два элемента. Арифметические действия с итераторами возможны только для контейнеров vector и deque. list не поддерживает адресную арифметику, поскольку его элементы не располагаются в непрерывной области памяти. Следующее выражение к списку неприменимо: ilist.begin() + 2; так как для перемещения на два элемента необходимо два раза перейти по адресу, содержащемуся в закрытом члене next. У классов vector и deque перемещение на два элемента означает прибавление 2 к указателю на текущий элемент. (Адресная арифметика рассматривается в разделе 3.3.) Объект контейнерного типа может быть инициализирован парой итераторов, обозначающих начало и конец последовательности копируемых в новый объект элементов. (Второй итератор должен указывать на элемент, следующий за последним копируемым.) Допустим, есть вектор: #include < vector> #include < string> #include < iostream>
int main() { vector< string> svec; string intext; while ( cin > > intext ) svec.push_back( intext );
// обработать svec... } Вот как можно определить новые векторы, инициализируя их элементами первого вектора:
Использование специального типа istream_iterator (о нем рассказывается в разделе 12.4.3) упрощает чтение элементов из входного потока в svec: #include < vector> #include < string> #include < iterator>
int mainQ { // привязка istream_iterator к стандартному вводу istream_iterator< string> infile( cin );
// istream_iterator, отмечающий конец потока istream_iterator< string> eos;
// инициализация svec элементами, считываемыми из cin; vector< string> svec( infile, eos );
//... } Кроме итераторов, для задания диапазона значений, инициализирующих контейнер, можно использовать два указателя на массив встроенного типа. Пусть есть следующий массив строк: #include < string> string words[4] = { " stately", " plump", " buck", " mulligan" }; Мы можем инициализировать вектор с помощью указателей на первый элемент массива и на элемент, следующий за последним: vector< string > vwords( words, words+4 ); Второй указатель служит “стражем”: элемент, на который он указывает, не копируется. Аналогичным образом можно инициализировать список целых элементов: int ia[6] = { 0, 1, 2, 3, 4, 5 }; list< int > ilist( ia, ia+6 ); В разделе 12.4 мы снова обратимся к итераторам и опишем их более детально. Сейчас информации достаточно для того, чтобы использовать итераторы в нашей системе текстового поиска. Но прежде чем вернуться к ней, рассмотрим некоторые дополнительные операции, поддерживаемые контейнерами. Упражнение 6.9 Какие ошибки допущены при использовании итераторов: const vector< int > ivec; vector< string > svec; list< int > ilist;
(a) vector< int>:: iterator it = ivec.begin(); (b) list< int>:: iterator it = ilist.begin()+2; (c) vector< string>:: iterator it = & svec[0]; (d) for ( vector< string>:: iterator it = svec.begin(); it! = 0; ++it ) //... Упражнение 6.10 Найдите ошибки в использовании итераторов: int ia[7] = { 0, 1, 1, 2, 3, 5, 8 }; string sa[6] = { " Fort Sumter", " Manassas", " Perryville", " Vicksburg", " Meridian", " Chancellorsvine" };
(a) vector< string> svec( sa, & sa[6] ); (b) list< int> ilist( ia+4, ia+6 ); (c) list< int> ilist2( ilist.begin(), ilist.begin()+2 ); (d) vector< int> ivec( & ia[0], ia+8 ); (e) list< string> slist( sa+6, sa ); (f) vector< string> svec2( sa, sa+6 ); |
Последнее изменение этой страницы: 2019-04-09; Просмотров: 303; Нарушение авторского права страницы