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


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


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

#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

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

    #include <string>

    #include <vector>

    #include <iostream>

    #int main()

    {

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

        // ...

    }


    Содержание раздела