Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 33 34 35 36 37 38 39 40 41 ... 297
Перейти на страницу:

Доступ к элементам вектора осуществляется таким же способом, как и к символам строки: по их позиции в векторе. Например, для обработки все элементов вектора можно использовать серийный оператор for (раздел 3.2.3).

vector<int> v{1,2,3,4,5,6,7,8,9};

for (auto &i : v)  // для каждого элемента вектора v

                   // (обратите внимание: i - ссылка)

 i *= i;           // квадрат значения элемента

for (auto i : v)   // для каждого элемента вектора v

 cout << i << " "; // вывод элемента

cout << endl;

В первом цикле управляющая переменная i определяется как ссылка, чтобы использовать ее для присвоения новых значений элементам вектора v. Используя спецификатор auto, позволим вывести ее тип автоматически. Этот цикл использует новую форму составного оператора присвоения (раздел 1.4.1). Как известно, оператор += добавляет правый операнд к левому и сохраняет результат в левом операнде. Оператор *= ведет себя точно так же, но перемножает левый и правый операнды, сохраняя результат в левом операнде. Второй серийный оператор for отображает каждый элемент.

Функции-члены empty() и size() вектора ведут себя так же, как и соответствующие функции класса string (раздел 3.2.2): функция empty() возвращает логическое значение, указывающее, содержит ли вектор какие-нибудь элементы, а функция size() возвращает их количество. Функция-член size() возвращает значение типа size_type, определенное соответствующим типом шаблона vector.

Чтобы использовать тип size_type, необходимо указать тип, для которого он определен. Для типа vector всегда необходимо указывать тип хранимого элемента (раздел 3.3).

vector<int>::size_type // ok

vector::size_type      // ошибка

Операторы равенства и сравнения вектора ведут себя как соответствующие операторы класса string (раздел 3.2.2). Два вектора равны, если у них одинаковое количество элементов и значения соответствующих элементов совпадают. Операторы сравнения полагаются на алфавитный порядок: если у векторов разные размеры, но соответствующие элементы равны, то вектор с меньшим количеством элементов меньше вектора с большим количеством элементов. Если у элементов векторов разные значения, то их отношения определяются по первым отличающимся элементам.

Сравнить два вектора можно только в том случае, если возможно сравнить элементы этих векторов. Некоторые классы, такие как string, определяют смысл операторов равенства и сравнения. Другие, такие как класс Sales_item, этого не делают. Операции, поддерживаемые классом Sales_item, перечислены в разделе 1.5.1. Они не включают ни операторов равенства, ни сравнения. В результате нельзя сравнить два вектора объектов класса Sales_item.

Вычисление индекса вектора

Используя оператор индексирования (раздел 3.2.3), можно выбрать указанный элемент. Подобно строкам, индексирование вектора начинаются с 0; индекс имеет тип size_type соответствующего типа; и если вектор не константен, то в возвращенный оператором индексирования элемент можно осуществить запись. Кроме того, как было продемонстрировано в разделе 3.2.3, можно вычислить индекс и непосредственно обратиться к элементу в данной позиции.

Предположим, имеется набор оценок степеней в диапазоне от 0 до 100. Необходимо рассчитать, сколько оценок попадает в кластер по 10. Между нулем и 100 возможна 101 оценка. Эти оценки могут быть представлены 11 кластерами: 10 кластеров по 10 оценок каждый плюс один кластер для наивысшей оценки 100. Первый кластер подсчитывает оценки от 0 до 9, второй — от 10 до 19 и т.д. Заключительный кластер подсчитывает количество оценок 100.

Таким образом, если введены следующие оценки:

42 65 95 100 39 67 95 76 88 76 83 92 76 93

результат их кластеризации должен быть таким:

0 0 0 1 1 0 2 3 2 4 1

Он означает, что не было никаких оценок ниже 30, одна оценка в 30-х, одна в 40-х, ни одной в 50-х, две в 60-х, три в 70-х, две в 80-х, четыре в 90-х и одна оценка 100.

Используем для содержания счетчиков каждого кластера вектор с 11 элементами. Индекс кластера для данной оценки можно определить делением этой оценки на 10. При делении двух целых чисел получается целое число, дробная часть которого усекается. Например, 42/10=4, 65/10=6, а 100/10=10.

Как только индекс кластера будет вычислен, его можно использовать для индексирования вектора и доступа к счетчику, значение которого необходимо увеличить.

// подсчет количества оценок в кластере по десять: 0--9,

// 10--19, ... 90--99, 100

vector<unsigned> scores(11, 0); // 11 ячеек, все со значением 0

unsigned grade;

while (cin >> grade) { // читать оценки

 if (grade <= 100)     // обрабатывать только допустимые оценки

   ++scores[grade/10]; // приращение счетчика текущего кластера

Код начинается с определения вектора для хранения счетчиков кластеров. В данном случае все элементы должны иметь одинаковое значение, поэтому резервируем 11 элементов, каждый из которых инициализируем значением 0. Условие цикла while читает оценки. В цикле проверяется допустимость значения прочитанной оценки (т.е. оно меньше или равно 100). Если оценка допустима, то увеличиваем соответствующий счетчик.

Оператор, осуществляющий приращение, является хорошим примером краткости кода С++:

++scores[grade/10]; // приращение счетчика текущего кластера

Это выражение эквивалентно следующему:

auto ind = grade/10;           // получить индекс ячейки

scores[ind] = scores[ind] + 1; // приращение счетчика

Индекс ячейки вычисляется делением значения переменной grade на 10. Полученный результат используется для индексирования вектора scores, что обеспечивает доступ к соответствующему счетчику для этой оценки. Увеличение значения этого элемента означает принадлежность текущей оценки данному диапазону.

Как уже упоминалось, при использовании индексирования следует позаботиться о том, чтобы индексы оставались в диапазоне допустимых значений (см. раздел 3.2.3). В этой программе проверка допустимости подразумевает принадлежность оценки к диапазону 0-100. Таким образом, можно использовать индексы от 0 до 10. Они расположены в пределах от 0 до scores.size() - 1.

Индексация не добавляет элементов

Новички в С++ иногда полагают, что индексирование вектора позволяет добавлять в него элементы, но это не так. Следующий код намеревается добавить десять элементов в вектор ivec:

vector<int> ivec; // пустой вектор

for (decltype(ivec.size()) ix = 0; ix != 10; ++ix)

 ivec[ix] = ix;   // катастрофа: ivec не имеет элементов

Причина ошибки — вектор ivec пуст; в нем нет никаких элементов для индексирования! Как уже упоминалось, правильный цикл использовал бы функцию push_back():

for (decltype(ivec.size()) ix = 0; ix != 10; ++ix)

 ivec.push_back(ix); // ok: добавляет новый элемент со значением ix

Оператор индексирования вектора (и строки) лишь выбирает существующий элемент; он не может добавить новый элемент.

Внимание! Индексировать можно лишь существующие элементы!

Очень важно понять, что оператор индексирования ([]) можно использовать для доступа только к фактически существующим элементам. Рассмотрим пример.

vector<int> ivec;      // пустой вектор

cout << ivec[0];       // ошибка: ivec не имеет элементов!

vector<int> ivec2(10); // вектор из 10 элементов

cout << ivec2[10];     // ошибка: ivec2 имеет элементы 0...9

Попытка обращения к несуществующему элементу является серьезной ошибкой, которую вряд ли обнаружит компилятор. В результате будет получено случайное значение.

Попытка индексирования несуществующих элементов, к сожалению, является весьма распространенной и грубой ошибкой программирования. Так называемая ошибка переполнения буфера (buffer overflow) — результат индексирования несуществующих элементов. Такие ошибки являются наиболее распространенной причиной проблем защиты приложений.

Наилучший способ гарантировать невыход индекса из диапазона — это избежать индексации вообще. Для этого везде, где только возможно, следует использовать серийный оператор for.

Упражнения раздела 3.3.3

Упражнение 3.16. Напишите программу, выводящую размер и содержимое вектора из упражнения 3.13. Проверьте правильность своих ответов на это упражнение. При неправильных ответах повторно изучите раздел 3.3.1.

1 ... 33 34 35 36 37 38 39 40 41 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии