6.4. Как определить последовательный контейнер?

6.4. Как определить последовательный контейнер?

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

#include vector

#inclnde list

#include deque

#include map

#include set

Определение контейнера начинается именем его типа, за которым в угловых скобках следует тип данных его элементов . Например:

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; ix4; ++ix )

ilist.push_back( ia[ ix ] );

создаст последовательность 0, 1, 2, 3, а push_front()

for ( int ix=0; ix4; ++ix )

ilist.push_front( ia[ ix ] );

создаст последовательность 3, 2, 1, 0.

Мы можем при создании явно указать размер массива – как константным, так и неконстантным выражением:

#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

Объясните, что делает данная программа:

#include string

#include vector

#include iostream

#int main()

{

vectorstring svec;

svec.reserve( 1024 );

string text_word;

while ( cin text_word )

svec.push_back( text_word );

svec.resize( svec.size()+svec.size()/2 );

// ...

}

Упражнение 6.6

Может ли емкость контейнера быть меньше его размера? Желательно ли, чтобы емкость была равна размеру: изначально или после вставки элемента? Почему?

Упражнение 6.7

Если программа из упражнения 6.5 прочитает 256 слов, то какова наиболее вероятная емкость контейнера после изменения размера? А если она считает 512 слов? 1000? 1048?

Упражнение 6.8

Какие из данных классов не могут храниться в векторе:

(a)

class cl1 {

public:

c11( int=0 );

bool operator==();

bool operator!=();

bool operator=();

bool operator();

// ...

};

(b)

class c12 {

public:

c12( int=0 );

bool operator!=();

bool operator=();

// ...

};

(с)

class c13 {

public:

int ival;

};

(d)

class c14 {

public:

c14( int, int=0 );

bool operator==();

bool operator!=();

// ...

}