Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
Фиксированный характер размера массивов влияет также на поведение остальных конструкторов, действительно определяющих массив. В отличие от других контейнеров, созданный по умолчанию массив не пуст: количество его элементов соответствует размеру, а инициализированы они значением по умолчанию (см. раздел 2.2.1), как и элементы встроенного массива (см. раздел 3.5.1). При списочной инициализации массива количество инициализаторов не должно превышать размер массива. Если инициализаторов меньше, чем элементов массива, они используются для первых элементов, а все остальные инициализируются значением по умолчанию (см. раздел 3.3.1). В любом случае, если типом элемента является класс, то у него должен быть стандартный конструктор, обеспечивающий инициализацию значением по умолчанию:
array<int, 10> ia1; // десять целых чисел, инициализированных
// значением по умолчанию
array<int, 10> ia2 = {0,1,2,3,4,5,6,7,8,9}; // списочная инициализация
array<int, 10> ia3 = {42}; // ia3[0] содержит значение 42, остальные
// элементы - значение 0
Следует заметить, что хоть и нельзя копировать или присваивать объекты массивов встроенных типов (см. раздел 3.5.1), для контейнеров array такого ограничения нет:
int digs[10] = {0,1,2,3,4,5,6,7,8,9};
int cpy[10] = digs; // ошибка: встроенные массивы не допускают
// копирования и присвоения
array<int, 10> digits = {0,1,2,3,4,5,6,7,8,9};
array<int, 10> copy = digits; // ok: если типы массивов совпадают
Как обычно, инициализирующий контейнер должен иметь тот же тип, что и создаваемый. Для контейнера array совпадать должны тип элемента и размер, поскольку размер массива — часть его типа.
Упражнения раздела 9.2.4Упражнение 9.11. Приведите пример каждого из шести способов создания и инициализации контейнеров vector. Объясните, какие значения будет содержать каждый вектор.
Упражнение 9.12. Объясните различие между конструктором, получающим контейнер для копирования, и конструктором получающим два итератора.
Упражнение 9.13. Как инициализировать контейнер vector<double> из контейнера list<int> и контейнера vector<int>? Напишите код для проверки ответов.
9.2.5. Присвоение и функция swap()
Связанные с присвоением операторы, перечисленные в табл. 9.4, воздействуют на весь контейнер. Оператор присвоения заменяет весь диапазон элементов в левом контейнере копиями элементов из правого:
c1 = c2; // заменяет содержимое контейнера c1 копией
// элементов контейнера c2
c1 = {a,b,c}; // после присвоения контейнер c1 имеет размер 3
После первого присвоения контейнеры слева и справа равны. Если контейнеры имели неравный размер, после присвоения у обоих будет размер контейнера из правого операнда. После второго присвоения размер контейнера c1 составит 3, что соответствует количеству значений, представленных в списке.
Таблица 9.4. Операторы присвоения контейнеров
c1 = c2 Заменяет элементы контейнера c1 копиями элементов контейнера c2. Контейнеры c1 и c2 должны иметь тот же тип с = {a, b, с...} Заменяет элементы контейнера c1 копиями элементов списка инициализации. (Недопустимо для массива.) swap(c1, c2) c1.swap(c2) Обменивает элементы контейнеров c1 и c2. Контейнеры c1 и c2 должны иметь тот же тип. Обычно функция swap() выполняется намного быстрее, чем процесс копирования элементов из контейнера c2 в c1 Операторы присвоения недопустимы для ассоциативных контейнеров и массива seq.assign(b,е) Заменяет элементы в контейнере seq таковыми из диапазона, обозначенного итераторами b и е. Итераторы b и е не должны ссылаться на элементы в контейнере seq seq.assign(il) Заменяет элементы в контейнере seq таковыми из списка инициализации il seq.assign(n,t) Заменяет элементы в контейнере seq набором из n элементов со значением tВ отличие от встроенных массивов, библиотечный тип array поддерживает присвоение. У левых и правых операндов должен быть одинаковый тип:
array<int, 10> a1 = {0,1,2,3,4,5,6,7,8,9};
array<int, 10> а2 = {0}; // все элементы со значением 0
a1 = а2; // замена элементов в a1
а2 = {0}; // ошибка: нельзя присвоить массиву значения из списка
Поскольку размер правого операнда может отличаться от размера левого операнда, тип array не поддерживает функцию assign() и это не позволяет присваивать значения из списка.
Связанные с присвоением операторы делают недопустимыми итераторы, ссылки и указатели на контейнер слева.
Применение функции assign() (только последовательные контейнеры)Оператор присвоения требует совпадения типов левых и правых операндов. Он копирует все элементы правого операнда в левый. Последовательные контейнеры (кроме array) определяют также функцию-член assign(), обеспечивающую присвоение разных, но совместимых типов, или присвоение контейнерам последовательностей. Функция assign() заменяет все элементы в левом контейнере копиями элементов, указанных в ее аргументе. Например, функцию assign() можно использовать для присвоения диапазона значений char* из вектора в список строк:
list<string> names;
vector<const char*> oldstyle;
names = oldstyle; // ошибка: типы контейнеров не совпадают
// ok: преобразование из const char* в string возможно
names.assign(oldstyle.cbegin(), oldstyle.cend());
Вызов функции assign() заменяет элементы в контейнере names копиями элементов из диапазона, обозначенного итераторами. Аргументы функции assign() определяют количество элементов контейнера и их значения.
Поскольку существующие элементы заменяются, итераторы, переданные функции assign(), не должны относиться к контейнеру, для которого вызвана функция assign().
Вторая версия функции assign() получает целочисленное значение и значение элемента. Она заменяет указанное количество элементов контейнера заданным значением:
// эквивалент slist1.clear();
// сопровождается slist1.insert(slist1.begin(), 10, "Hiya!");
list<string> slist1(1); // один элемент; пустая строка
slist1.assign(10, "Hiya!"); // десять элементов; со значением "Hiya!"
Применение функции swap()Функция swap() меняет местами значения двух контейнеров того же типа. Типы контейнеров и их элементов должны совпадать. После обращения к функции swap() элементы правого операнда оказываются в левом, и наоборот:
vector<string> svec1(10); // вектор из 10 элементов
vector<string> svec2(24); // вектор из 24 элементов
svec1.swap(svec2);
После выполнения функции swap() вектор svec1 содержит 24 строки, а вектор svec2 — 10. За исключением массивов смена двух контейнеров осуществляется очень быстро — сами элементы не меняются; меняются лишь внутренние структуры данных.
За исключением массивов функция swap() не копирует, не удаляет и не вставляет элементы, поэтому она гарантированно выполняется за постоянное время.
Благодаря тому факту, что элементы не перемещаются, итераторы, ссылки и указатели в контейнере, за исключением контейнера string, остаются допустимыми. Они продолжают указывать на те же элементы, что и перед перестановкой. Однако после вызова функции swap() эти элементы находятся в другом контейнере. Предположим, например, что итератор iter обозначал в векторе строк позицию svec1[3]. После вызова функции swap() он обозначит элемент в позиции svec2[3]. В отличие от других контейнеров, вызов функции swap() для строки делает некорректными итераторы, ссылки и указатели.