Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
// тип указан явно
list<string>::iterator it5 = a.begin();
list<string>::const_iterator it6 = a.begin();
// iterator или const_iterator в зависимости от типа а
auto it7 = a.begin(); // const_iterator только если a константа
auto it8 = a.cbegin(); // it8 - const_iterator
Когда с функциями begin() или end() используется ключевое слово auto, тип возвращаемого итератора зависит от типа контейнера. То, как предполагается использовать итератор, несущественно. Версии c позволяют получать итератор типа const_iterator независимо от типа контейнера.
Когда доступ на запись не нужен, используйте версии cbegin() и cend().
Упражнения раздела 9.2.3Упражнение 9.9. В чем разница между функциями begin() и cbegin()?
Упражнение 9.10. Каковы типы следующих четырех объектов?
vector<int> v1;
const vector<int> v2;
auto it1 = v1.begin(), it2 = v2.begin();
auto it3 = v1.cbegin(), it4 = v2.cbegin();
9.2.4. Определение и инициализация контейнера
Каждый контейнерный тип определяет стандартный конструктор (см. раздел 7.1.4). За исключением контейнера array стандартный конструктор создает пустой контейнер определенного типа. Также за исключением контейнера array другие конструкторы получают аргументы, которые определяют размер контейнера и исходные значения его элементов.
Инициализация контейнера как копии другого контейнераСуществуют два способа создать новый контейнер как копию другого: можно непосредственно скопировать контейнер или (за исключением контейнера array) скопировать только диапазон его элементов, обозначенный парой итераторов.
Таблица 9.3. Определение и инициализация контейнера
С c; Стандартный конструктор. Если C — массив, то элементы контейнера с инициализируются значением по умолчанию; в противном случае контейнер с пуст С c1(c2) С c1 = c2 Контейнер c1 — копия c2. Контейнеры c1 и c2 должны быть одинакового типа (т.е. должны иметь тот же тип контейнера и содержать элементы того же типа; у массивов должен также совпадать размер) С с{a, b, с...} С с = {a, b, с...} Контейнер c содержит копию элементов из списка инициализации. Тип элементов в списке должен быть совместимым с типом элементов C. В случае массива количество элементов списка не должно превышать размер массива, а все недостающие элементы инициализируются значением по умолчанию (см. раздел 3.3.1) С с(b, е) Контейнер c содержит копию элементов из диапазона, обозначенного итераторами b и е. Тип элементов должен быть совместимым с типом элементов С. (Недопустимо для массива.) Получающие размер конструкторы допустимы только для последовательных контейнеров (исключая массив) С seq(n) Контейнер seq содержит n элементов, инициализированных значением по умолчанию; этот конструктор является явным (см. раздел 7.5.4). (Недопустимо для строки.) С seq(n,t) Контейнер seq содержит n элементов со значением tЧтобы создать контейнер как копию другого, их типы контейнеров и элементов должны совпадать. При передаче итераторов идентичность типов контейнеров необязательна. Кроме того, могут отличаться типы элементов нового и исходного контейнеров, если возможно преобразование между ними (см. раздел 4.11):
// каждый контейнер имеет три элемента, инициализированных
// предоставленными инициализаторами
list<string> authors = {"Milton", "Shakespeare", "Austen"};
vector<const char*> articles = {"a", "an", "the"};
list<string> list2(authors); // ok: типы совпадают
deque<string> authList(authors); // ошибка: типы контейнеров
// не совпадают
vector<string> words(articles); // ошибка: типы элементов не совпадают
// ok: преобразует элементы const char* в string
forward_list<string> words(articles.begin(), articles.end());
При копировании содержимого одного контейнера в другой типы контейнеров и их элементов должны точно совпадать.
Конструктор, получающий два итератора, использует их для обозначения диапазона копируемых элементов. Как обычно, итераторы отмечают первый и следующий элемент после последнего копируемого. Размер нового контейнера будет совпадать с количеством элементов в диапазоне. Каждый элемент в новом контейнере инициализируется значением соответствующего элемента в диапазоне.
Поскольку итераторы обозначают диапазон, этот конструктор можно использовать для копирования части последовательности. С учетом того что it является итератором, обозначающим элемент в контейнере authors, можно написать следующее:
// копирует до, но не включая элемент, обозначенный итератором it
deque<string> authList(authors.begin(), it);
Списочная инициализацияПо новому стандарту контейнер допускает списочную инициализацию (см. раздел 3.3.1):
// каждый контейнер имеет три элемента, инициализированных
// предоставленными инициализаторами
list<string> authors = {"Milton", "Shakespeare", "Austen"};
vector<const char*> articles = {"a", "an", "the"};
Это определяет значение каждого элемента в контейнере явно. Кроме контейнеров таких типов, как array, список инициализации неявно определяет также размер контейнера: у контейнера будет столько элементов, сколько инициализаторов в списке.
Конструкторы последовательных контейнеров, связанные с размеромКроме конструкторов, общих для последовательных и ассоциативных контейнеров, последовательные контейнеры (кроме массива) можно также инициализировать, указав их размера и (необязательного) инициализирующий элемент. Если его не предоставить, библиотека создает инициализирующее значение сама (см. раздел 3.3.1):
vector<int> ivec(10, -1); // десять элементов типа int; значение -1
list<string> svec(10, "hi!"); // десять строк; значение "hi!"
forward_list<int> ivec(10); // десять элементов; значение 0
deque<string> svec(10); // десять элементов; все пустые строки
Конструктор, получающий аргумент размера, можно также использовать, если элемент имеет встроенный тип или тип класса, у которого есть стандартный конструктор (см. раздел 9.2). Если у типа элемента нет стандартного конструктора, то наряду с размером следует определить явный инициализатор элемента.
Конструкторы, получающие размер, допустимы только для последовательных контейнеров; ассоциативные контейнеры их не поддерживают.
Библиотечные массивы имеют фиксированный размерПодобно тому, как размер встроенного массива является частью его типа, размер библиотечного контейнера array тоже входит в состав его типа. Когда определяется массив, кроме типа элемента задается также размер контейнера:
array<int, 42> // тип: массив, содержащий 42 целых числа
array<string, 10> // тип: массив, содержащий 10 строк
Чтобы использовать контейнер array, следует указать тип элемента и его размер:
array<int, 10>::size_type i; // тип массива включает тип элемента
// и размер
array<int>::size_type j; // ошибка: array<int> - это не тип
Поскольку размер является частью типа массива, контейнер array не поддерживает обычные конструкторы контейнерных классов. Эти конструкторы, явно или неявно, определяют размер контейнера. В случае массива разрешение пользователям передавать аргумент размера было бы избыточно (в лучшем случае) и приводило бы к ошибкам.
Фиксированный характер размера массивов влияет также на поведение остальных конструкторов, действительно определяющих массив. В отличие от других контейнеров, созданный по умолчанию массив не пуст: количество его элементов соответствует размеру, а инициализированы они значением по умолчанию (см. раздел 2.2.1), как и элементы встроенного массива (см. раздел 3.5.1). При списочной инициализации массива количество инициализаторов не должно превышать размер массива. Если инициализаторов меньше, чем элементов массива, они используются для первых элементов, а все остальные инициализируются значением по умолчанию (см. раздел 3.3.1). В любом случае, если типом элемента является класс, то у него должен быть стандартный конструктор, обеспечивающий инициализацию значением по умолчанию: