С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 22
Текст из файла (страница 22)
Так выглядитint main(){const int rowSize = 4;const int colSize = 3;int ia[ rowSize ][ colSize ];for ( int = 0; i < rowSize; ++i )for ( int j = 0; j < colSize; ++j )ia[ i ][ j ] = i + j j;инициализация двумерного массива с помощью вложенных циклов:}115С++ для начинающихКонструкцияia[ 1, 2 ]является допустимой с точки зрения синтаксиса С++, однако означает совсем не то, чегождет неопытный программист. Это отнюдь не объявление двумерного массива 1 на 2.Агрегат в квадратных скобках – это список выражений через запятую, результатомкоторого будет последнее значение 2 (см. оператор “запятая” в разделе 4.2).
Поэтомуобъявление ia[1,2] эквивалентно ia[2]. Это еще одна возможность допустить ошибку.3.9.2. Взаимосвязь массивов и указателейЕсли мы имеем определение массива:int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21 };то что означает простое указание его имени в программе?ia;Использование идентификатора массива в программе эквивалентно указанию адреса егоia;первого элемента:&ia[0]// оба выражения возвращают первый элемент*ia;Аналогично обратиться к значению первого элемента массива можно двумя способами:ia[0];Чтобы взять адрес второго элемента массива, мы должны написать:&ia[1];Как мы уже упоминали раньше, выражениеia+1;также дает адрес второго элемента массива. Соответственно, его значение дают нам*(ia+1);следующие два способа:ia[1];116С++ для начинающихОтметим разницу в выражениях:*ia+1и*(ia+1);Операция разыменования имеет более высокий приоритет, чем операция сложения (оприоритетах операций говорится в разделе 4.13).
Поэтому первое выражение сначаларазыменовывает переменную ia и получает первый элемент массива, а затем прибавляетк нему 1. Второе же выражение доставляет значение второго элемента.Проход по массиву можно осуществлять с помощью индекса, как мы делали это в#include <iostream>int main(){int ia[9] = { 0, 1, 1, 2, 3, 5, 8, 13, 21 };int *pbegin = ia;предыдущем разделе, или с помощью указателей. Например:while ( pbegin != pend ) {cout << *pbegin <<;++pbegin;int *pend = ia + 9;}Указатель pbegin инициализируется адресом первого элемента массива. Каждый проходпо циклу увеличивает этот указатель на 1, что означает смещение его на следующийэлемент.
Как понять, где остановиться? В нашем примере мы определили второйуказатель pend и инициализировали его адресом, следующим за последним элементоммассива ia. Как только значение pbegin станет равным pend, мы узнаем, что массивкончился.Перепишем эту программу так, чтобы начало и конец массива передавались параметрами#inc1ude <iostream>void ia_print( int *pbegin, int *pend ){while ( pbegin != pend ) {cout << *pbegin << ' ';++pbegin;}}int main(){int ia[9] = { 0, 1, 1, 2, 3, 5, 8, 13, 21 };ia_print( ia, ia + 9 );в некую обобщенную функцию, которая умеет печатать массив любого размера:}117С++ для начинающихНаша функция стала более универсальной, однако, она умеет работать только смассивами типа int.
Есть способ снять и это ограничение: преобразовать данную#inc1ude <iostream>template <c1ass e1emType>void print( elemType *pbegin, elemType *pend ){while ( pbegin != pend ) {cout << *pbegin << ' ';++pbegin;}функцию в шаблон (шаблоны были вкратце представлены в разделе 2.5):}int main(){int ia[9] = { 0, 1, 1, 2, 3, 5, 8, 13, 21 };double da[4] = { 3.14,6.28, 12.56, 25.12 };string sa[3] = { "piglet", "eeyore", "pooh" };print( ia, ia+9 );print( da, da+4 );print( sa, sa+3 );Теперь мы можем вызывать нашу функцию print() для печати массивов любого типа:}Мы написали обобщенную функцию. Стандартная библиотека предоставляет наборобобщенных алгоритмов (мы уже упоминали об этом в разделе 3.4), реализованныхподобным образом.
Параметрами таких функций являются указатели на начало и конецмассива, с которым они производят определенные действия. Вот, например, как выглядят#include <a1gorithm>int main(){int ia[6] = { 107, 28, 3, 47, 104, 76 };string sa[3] = { "piglet", "eeyore", "pooh" };sort( ia, ia+6 );sort( sa, sa+3 );вызовы обобщенного алгоритма сортировки:};(Мы подробно остановимся на обобщенных алгоритмах в главе 12; в Приложении будутприведены примеры их использования.)В стандартной библиотеке С++ содержится набор классов, которые инкапсулируютиспользование контейнеров и указателей.
(Об этом говорилось в разделе 2.8.) Вследующем разделе мы займемся стандартным контейнерным типом vector,являющимся объектно-ориентированной реализацией массива.118С++ для начинающих3.10. Класс vectorИспользование класса vector (см. раздел 2.8) является альтернативой применениювстроенных массивов. Этот класс предоставляет гораздо больше возможностей, поэтомуего использование предпочтительней. Однако встречаются ситуации, когда не обойтисьбез массивов встроенного типа.
Одна из таких ситуаций – обработка передаваемыхпрограмме параметров командной строки, о чем мы будем говорить в разделе 7.8. Классvector, как и класс string, является частью стандартной библиотеки С++.Для использования вектора необходимо включить заголовочный файл:#include <vector>Существуют два абсолютно разных подхода к использованию вектора, назовем ихидиомой массива и идиомой STL. В первом случае объект класса vector используетсяточно так же, как массив встроенного типа. Определяется вектор заданной размерности:vector< int > ivec( 10 );что аналогично определению массива встроенного типа:int ia[ 10 ];void simp1e_examp1e(){const int e1em_size = 10;vector< int > ivec( e1em_size );int ia[ e1em_size ];for ( int ix = 0; ix < e1em_size; ++ix )ia[ ix ] = ivec[ ix ];// ...Для доступа к отдельным элементам вектора применяется операция взятия индекса:}Мы можем узнать размерность вектора, используя функцию size(), и проверить, пуст лиvoid print_vector( vector<int> ivec ){if ( ivec.empty() )return;for ( int ix=0; ix< ivec.size(); ++ix )cout << ivec[ ix ] << ' ';вектор, с помощью функции empty().
Например:}Элементы вектора инициализируются значениями по умолчанию. Для числовых типов иуказателей таким значением является 0. Если в качестве элементов выступают объекты119С++ для начинающихкласса, то инициатор для них задается конструктором по умолчанию (см. раздел 2.3).Однако инициатор можно задать и явно, используя форму:vector< int > ivec( 10, -1 );Все десять элементов вектора будут равны -1.Массив встроенного типа можно явно инициализировать списком:int ia[ 6 ] = { -2, -1, О, 1, 2, 1024 };Для объекта класса vector аналогичное действие невозможно.
Однако такой объект// 6 элементов ia копируются в ivecможет быть инициализирован с помощью массива встроенного типа:vector< int > ivec( ia, ia+6 );Конструктору вектора ivec передаются два указателя – указатель на начало массива ia ина элемент, следующий за последним. В качестве списка начальных значений допустимо// копируются 3 элемента: ia[2], ia[3], ia[4]указать не весь массив, а некоторый его диапазон:vector< int > ivec( &ia[ 2 ], &ia[ 5 ] );Еще одним отличием вектора от массива встроенного типа является возможностьинициализации одного объекта типа vector другим и использования операцииvector< string > svec;void init_and_assign(){// один вектор инициализируется другимvector< string > user_names( svec );// ...// один вектор копируется в другойsvec = user_names;присваивания для копирования объектов. Например:}Говоря об идиоме STL6, мы подразумеваем совсем другой подход к использованиювектора. Вместо того чтобы сразу задать нужный размер, мы определяем пустой вектор:vector< string > text;6 STL расшифровывается как Standard Template Library.
До появлениястандартной библиотеки С++ классы vector, string и другие, а такжеобобщенные алгоритмы входили в отдельную библиотеку с названием STL.120С++ для начинающихЗатем добавляем к нему элементы при помощи различных функций. Например, функцияpush_back()вставляет элемент в конец вектора. Вот фрагмент кода, считывающегоstring word;while ( cin >> word ) {text.push_back( word );// ...последовательность строк из стандартного ввода и добавляющего их в вектор:}cout << "считаны слова: \n";for ( int ix =0; ix < text.size(); ++ix )cout << text[ ix ] << ' ';Хотя мы можем использовать операцию взятия индекса для перебора элементов вектора:cout << endl;cout << "считаны слова: \n";for ( vector<string>::iterator it = text.begin();it != text.end(); ++it )cout << *it << ' ';более типичным в рамках данной идиомы будет использование итераторов:cout << endl;Итератор – это класс стандартной библиотеки, фактически являющийся указателем наэлемент массива.Выражение*it;разыменовывает итератор и дает сам элемент вектора.
Инструкция++it;сдвигает указатель на следующий элемент. Не нужно смешивать эти два подхода. Еслиследовать идиоме STL при определении пустого вектора:vector<int> ivec;будет ошибкой написать:ivec[0] = 1024;У нас еще нет ни одного элемента вектора ivec; количество элементов выясняется спомощью функции size().121С++ для начинающихМожно допустить и противоположную ошибку. Если мы определили вектор некоторогоразмера, например:vector<int> ia( 10 );то вставка элементов увеличивает его размер, добавляя новые элементы ксуществующим.
Хотя это и кажется очевидным, тем не менее, начинающий программистconst int size = 7;int ia[ size ] = { 0, 1, 1, 2, 3, 5, 8 };vector< int > ivec( size );for ( int ix = 0; ix < size; ++ix )вполне мог бы написать:ivec.push_back( ia[ ix ] );Имелась в виду инициализация вектора ivec значениями элементов ia, вместо чегополучился вектор ivec размера 14.Следуя идиоме STL, можно не только добавлять, но и удалять элементы вектора. (Все этомы рассмотрим подробно и с примерами в главе 6.)Упражнение 3.24int ia[ 7 ] = { 0, 1, 1, 2, 3, 5, 8 };(a)(b)(c)(d)vector<vector<vector<vector<vector< int > > ivec;int > ivec = { 0, 1, 1, 2, 3, 5, 8 };int > ivec( ia, ia+7 );string > svec = ivec;Имеются ли ошибки в следующих определениях?(e) vector< string > svec( 10, string( "null" ));Упражнение 3.25bool is_equa1( const int*ia, int ia_size,Реализуйте следующую функцию:const vector<int> &ivec );Функция is_equal() сравнивает поэлементно два контейнера.