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

We use cookies. Read the Privacy and Cookie Policy

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!=();

// ...

}