Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
86
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

261

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 );

6.6.Операции с последовательными контейнерами

Функция-член push_back() позволяет добавить единственный элемент в конец контейнера. Но как вставить элемент в произвольную позицию? А целую последовательность элементов? Для этих случаев существуют более общие операции.

vector< string > svec; list< string > slist; string spouse( "Beth" );

slist.insert( slist.begin(), spouse );

Например, для вставки элемента в начало контейнера можно использовать: svec.insert( svec.begin(), spouse );

Первый параметр функции-члена insert() (итератор, адресующий некоторый элемент контейнера) задает позицию, а второй вставляемое перед этой позицией значение. В примере выше элемент добавляется в начало контейнера. А так можно реализовать вставку в произвольную позицию:

С++ для начинающих

262

string son( "Danny" );

list<string>::iterator iter;

iter = find( slist.begin(), slist.end(), son );

slist.insert( iter, spouse );

Здесь find() возвращает позицию элемента в контейнере, если элемент найден, либо итератор end(), если ничего не найдено. (Мы вернемся к функции find() в конце следующего раздела.) Как можно догадаться, push_back() эквивалентен следующей

// эквивалентный вызов: slist.push_back( value );

записи:

slist.insert( slist.end(), value );

Вторая форма функции-члена insert() позволяет вставить указанное количество одинаковых элементов, начиная с определенной позиции. Например, если мы хотим

vector<string> svec; string anna( "Anna" );

добавить десять элементов Anna в начало вектора, то должны написать: svec.insert( svec.begin(), 10, anna );

insert() имеет и третью форму, помогающую вставить в контейнер несколько элементов. Допустим, имеется следующий массив:

string sarray[4] = { "quasi", "simba", "frollo", "scar" };

Мы можем добавить все его элементы или только некоторый диапазон в наш вектор

svec.insert( svec.begin(), sarray, sarray+4 ); svec.insert( svec.begin() + svec.size()/2,

строк:

sarray+2, sarray+4 );

//вставляем элементы svec

//в середину svec_two

svec_two.insert( svec_two.begin() + svec_two.size()/2,

Такой диапазон отмечается и с помощью пары итераторов svec.begin(), svec.end() );

С++ для начинающих

263

list< string > slist;

//...

//вставляем элементы svec

//перед элементом, содержащим stringVal list< string >::iterator iter =

find( slist.begin(), slist.end(), stringVal );

или любого контейнера, содержащего строки:14 slist.insert( iter, svec.begin(), svec.end() );

6.6.1. Удаление

В общем случае удаление осуществляется двумя формами функции-члена erase(). Первая форма удаляет единственный элемент, вторая диапазон, отмеченный парой итераторов. Для последнего элемента можно воспользоваться функцией-членом pop_back().

При вызове erase() параметром является итератор, указывающий на нужный элемент.

В следующем фрагменте кода мы воспользуемся обобщенным алгоритмом find() для

string searchValue( "Quasimodo" ); list< string >::iterator iter =

find( slist.begin(), slist.end(), searchValue ); if ( iter != slist.end() )

нахождения элемента и, если он найден, передадим его адрес функции-члену erase(). slist.erase( iter );

Для удаления всех элементов контейнера или некоторого диапазона можно написать

//удаляем все элементы контейнера slist.erase( slist.begin(), slist.end() );

//удаляем элементы, помеченные итераторами list< string >::iterator first, last;

first = find( slist. begin(), slist.end(), vail ); last = find( slist.begin(), slist.end(), va12 );

// ... проверка first и last

следующее:

slist.erase( first, last );

14 Последняя форма insert() требует, чтобы компилятор работал с шаблонами функций-членов. Если ваш компилятор еще не поддерживает это свойство стандарта С++, то оба контейнера должны быть одного типа, например два списка или два вектора, содержащих элементы одного типа.

С++ для начинающих

264

Парной по отношению к push_back() является функция-член pop_back(), удаляющая

vector< string >::iterator iter = buffer.begin(); for ( ; iter != buffer.end(), iter++ )

{

slist.push_back( *iter );

if ( ! do_something( slist )) slist.pop_back();

из контейнера последний элемент, не возвращая его значения:

}

6.6.2. Присваивание и обмен

Что происходит, если мы присваиваем один контейнер другому? Оператор присваивания копирует элементы из контейнера, стоящего справа, в контейнер, стоящий слева от знака

//svecl содержит 10 элементов

//svec2 содержит 24 элемента

//после присваивания оба содержат по 24 элемента

равенства. А если эти контейнеры имеют разный размер? Например: svecl = svec2;

Контейнер-адресат (svec1) теперь содержит столько же элементов, сколько контейнер- источник (svec2). 10 элементов, изначально содержавшихся в svec1, удаляются (для каждого из них вызывается деструктор класса string).

Функция обмена swap() может рассматриваться как дополнение к операции присваивания. Когда мы пишем:

svecl.swap( svec2 );

svec1 после вызова функции содержит 24 элемента, которые он получил бы в результате присваивания:

svecl = svec2;

но зато теперь svec2 получает 10 элементов, ранее находившихся в svec1. Контейнеры обмениваютсясвоим содержимым.

6.6.3. Обобщенные алгоритмы

Операции, описанные в предыдущих разделах, составляют набор, поддерживаемый непосредственно контейнерами vector и deque. Согласитесь, что это весьма небогатый интерфейс и ему явно не хватает базовых операций find(), sort(), merge() и т.д.

Планировалось вынести общие для всех контейнеров операции в набор обобщенных алгоритмов, которые могут применяться ко всем контейнерным типам, а также к массивам встроенных типов. (Обобщенные алгоритмы описываются в главе 12 и в Приложении.) Эти алгоритмы связываются с определенным типом контейнера с

С++ для начинающих

265

помощью передачи им в качестве параметров пары соответствующих итераторов. Вот как

#include <list> #include <vector>

int ia[ 6 ] = { 0, 1, 2, 3, 4, 5 }; vector<string> svec;

list<double> dtist;

// соответствующий заголовочный файл

#include <algorithm> vector<string>::iterator viter; list<double>::iterator liter; #int *pia;

//find() возвращает итератор на найденный элемент

//для массива возвращается указатель ...

pia = find( &ia[0], &ia[6], some_int_value );

liter = find( dlist.begin(), dlist.end(), some_double_value );

выглядят вызовы алгоритма find() для списка, вектора и массива разных типов: viter = find( svec.begin(), svec.end(), some_string_value );

Контейнер list поддерживает дополнительные операции, такие, как sort() и merge(), поскольку в нем не реализован произвольный доступ к элементам. (Эти операции описаны в разделе 12.6.)

Теперь вернемся к нашей поисковой системе. Упражнение 6.11

int ia[] = { 1, 5, 34 }; int ia2[] = { 1, 2, 3 };

int ia3[] = { 6, 13, 21, 29, 38, 55, 67, 89 };

Напишите программу, в которой определены следующие объекты: vector<int> ivec;

Используя различные операции вставки и подходящие значения ia, ia2 и ia3, модифицируйте вектор ivec так, чтобы он содержал последовательность:

{ 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 }

Упражнение 6.12

int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };

Напишите программу, определяющую данные объекты: list<int> ilist( ia, ia+11 );

Используя функцию-член erase() с одним параметром, удалите из ilist все нечетные элементы.