Программирование >>  Обобщенные обратные вызовы 

[ 1 ] 2 3 4 ... 84


обобщенные обратные вызовы

Обобщенное программирование и стандартная библиотека с++

Одной из наиболее мощных возможностей С++ является поддержка обобщенного программирования. Эта возможность находит непосредственное отражение в гибкости стандартной библиотеки С++, в особенности в контейнерах, итераторах и алгоритмах, известных под названием стандартной библиотеки шаблонов (Standard Template Library - STL).

Так же, как и предыдущая книга [Sutter02], эта книга начинается с задач, которые привлекают наше внимание к некоторым хорошо знакомым частям STL, в частности к векторам и строкам. Сможете ли вы избежать широко распространенных ловушек при использовании такого базового контейнера STL, как вектор? Как вы вьшолните обычные манипуляции со строками в С++? Какие уроки в плане конструирования библиотек вы сможете извлечь для себя из STL?

После того как мы разберемся с предопределенными шаблонами STL, мы обратимся к более общим вопросам, связанным с шаблонами и обобщенным программированием на С+ + . Как можно избежать при разработке собственного шаблонного кода его необобщенности? Почему специализация шаблонов функций - не лучшая идея, и что следует делать вместо этого? Как корректно и переносимо добиться тех же результатов, которые дают отношения дружественности? И что нам дает ключевое слово export?

Эти и другие вопросы будут рассмотрены нами в разделе, посвященном обобщенному программированию и стандартной библиотеке С++.



Задача 1. Вектор: потребление

и злоупотребление Сложность: 4

Почти все используют std:: vector, и это хорошо. К сожалению, многие не всегда верно понимают его семантику и в результате невольно применяют его странными, а порой и опасными способами. Сколько из перечисленных ниже проблем можно найти в ваших программах?

Вопрос для новичка

1. В чем разница между строками А и В?

void fС vector<int>& v ) {

v[0]; A

v.at(O); В

Вопрос для профессионала

2. Рассмотрим следующий код. vector<int> v;

V. reserveC 2 ) ;

assertC v.capacityO == 2 );

v[0] = 1;

v[l] = 2;

for(vector<int>::i terator i = v.beginC); i<v.end(); i++){ cout *i endl;

cout v[0];

V.reserveC 100 );

assertC V.capacityO == 100 );

cout v[0];

v[2] = 3; v[3] = 4;

v[99i = 100;

for( vector<int>::iterator i = v.beginC); i<v.end(); i++){ cout *i endl;

Раскритикуйте этот код, как с точки зрения стиля, так и с точки зрения корректности.

Решение

Обращение к элементу вектора

1. В чем разница между строками А и В?

void f( vector<int>& v ) {

v[0]; A

v.at(O); в



в примере 1-1, если вектор v не пуст, разницы между строками А и В нет. Если же V пуст, то в строке В будет гарантированно сгенерировано исключение std: :out of range, но что произойдет в строке А, сказать невозможно.

Имеется два способа обращения к элементам, содержащимся в векторе. Первый, vector<T>::at, выполняет проверку диапазона значения индекса, чтобы убедиться, что требуемый элемент действительно содержится в векторе. Не имеет никакого смысла обращение к сотому элементу вектора, в котором содержится всего 10 элементов, и если вы попытаетесь сделать это, то функция at защитит вас от неверных действий, генерируя исключение std: :out..of range.

Оператор vector<T>: :operator[] может, но не обязан выполнять проверку диапазона. В стандарте об этом ничего не сказано, так что разработчик вашей стандартной библиотеки имеет полное право как добавить такую проверку, так и обойтись без нее. Если вы используете operator[] для обращения к элементу, отсутствующему в векторе, вы делаете это на свой страх и риск, и стандарт ничего не говорит о том, что может произойти в данном случае (хотя описание этой ситуации может оказаться в документации к используемой вами реализации стандартной библиотеки). Возможно ваша программа аварийно завершится, или будет сгенерировано исключение, или же программа будет продолжать работать, выдавая неверные результаты, или аварийно завершится в каком-то совершенно ином месте.

Такая проверка диапазона защищает нас от множества проблем. Так почему же стандарт не требует ее выполнения в операторе operator[]? Краткий ответ прост: эффективность. Постоянная проверка диапазона может привести к накладным расходам (возможно, небольшим) во всех ваших программах, даже там, где гарантировашю не может быть нарушения границ. Согласно принципам С+ + , вы не должны платить за то, чего не используете, и поэтому проверка диапазона в операторе operator[] не является обязательной. В конкретном случае с векторами у нас есть еще одна причина для приоритета эффективности: векторы предназначены для использования вместо встроенных массивов, и поэтому они должны быть настолько же эффективны, как и массивы (в которых не выполняются проверки диапазона). Если вы хотите, чтобы такая проверка осуществлялась, - используйте функцию at.

Увеличение размера вектора

Теперь обратимся к примеру 1-2, который работает с vector<int> при помощи некоторых простых операций. 2. Рассмотрим следующий код.

vector<int> v;

V.reserveC 2 );

assert( v.capacityC) == 2 );

Раскритикуйте этот код, как с точки зрения стиля, так и с точки зрения корректности.

Данная проверка связана с двумя проблемами, смысловой и стилистической.

Смысловая проблема состоит в том, что проверка может сработать неверно. Почему? Потому что вызов reserve гарантирует, >гго емкость вектора становится равной как минимум 2, но может быть и больше 2. Обычно это так и есть, потому что типичная реализация вектора может всегда увеличивать внутренний буфер экспоненциально, невзирая на конкретный запрос посредством функции reserve. Поэтому корректная проверка до.тжна использовать оператор сравнения >=, а не строгое равенство.

assert( V.capacityC) >= 2 );

Во-вторых, стилистическая ошибка заключается в том, что проверка избыточна. Почему? Потому что стандарт гарантирует выполнение проверяемого условия. Зачем же нужна явная проверка? Она не имеет смысла, если только вы не подозреваете о



[ 1 ] 2 3 4 ... 84

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.002
При копировании материалов приветствуются ссылки.
Яндекс.Метрика