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