В этом пункте, как и в предыдущем, аргументы нельзя считать универсальными. Несмотря на то что они разделяются практически всеми программистами и не могут быть просто отброшены, если вы начинаете писать новую программу в среде, не связанной с вычислениями в реальном времени (см. раздел 25.2.1), то используйте обработку ошибок с помощью исключений и векторы с проверкой выхода за пределы допустимого диапазона.
19.4.1.4. Необязательная проверка
Стандарт ISO C++ утверждает, что выход за пределы допустимого диапазона вектора не имеет гарантированной семантики, поэтому его следует избегать. В соответствии со стандартом при попытке выхода за пределы допустимого диапазона следует генерировать исключение. Следовательно, если вы хотите, чтобы класс vector
генерировал исключения и не создавал проблем, связанных с первыми тремя аргументами, в конкретном приложении следует использовать класс vector
с проверкой выхода за пределы допустимого диапазона. Именно этого принципа мы придерживаемся в нашей книге.
19.4.2. Признание: макрос
Как и наш класс vector, большинство реализаций стандартного класса vector
не гарантирует проверку выхода за пределы допустимого диапазона с помощью оператора индексирования ([]
), а вместо этого содержит функцию at()
, выполняющую такую проверку. В каком же месте нашей программы возникают исключения std::out_of_range
? По существу, мы выбрали вариант 4 из раздела 19.4.1: реализация класса vector
не обязана проверять выход за пределы допустимого диапазона с помощью оператора []
, но ей не запрещено делать это иным способом, и мы решили воспользоваться этой возможностью. Однако в нашей отладочной версии под названием Vector
, разрабатывая код, мы реализовали проверку в операторе []
. Это позволяет сократить время отладки за счет небольшой потери производительности программы.
struct Range_error:out_of_range { // подробное сообщение
// о выходе за пределы допустимого диапазона
int index;
Range_error(int i):out_of_range("Range error"), index(i)
{ }
};
template
typedef typename std::vector
Vector() { }
explicit Vector(size_type n):std::vector
Vector(size_type n, const T& v):std::vector
T& operator[](size_type int i) // rather than return at(i);
{
if (i<0||this–>size()<=i) throw Range_error(i);
return std::vector
}
const T& operator[](size_type int i) const
{
if (i<0||this–>size()<=i) throw Range_error(i);
return std::vector
}
};
Мы используем класс Range_error
, чтобы облегчить отладку операции индексирования. Оператор typedef
вводит удобный синоним, который подробно описан в разделе 20.5.
Класс Vector
очень простой, возможно, слишком простой, но он полезен для отладки нетривиальных программ. В качестве альтернативы нам пришлось бы использовать реализацию стандартного класса vector
, предусматривающую систематическую проверку, — возможно, именно это нам и следовало сделать; у нас нет информации, насколько строгой является проверка, предусмотренная вашим компилятором и библиотекой (поскольку это выходит за рамки стандарта).
std_lib_facilities.h
мы используем ужасный трюк (макроподстановку), указывая, что слово vector означает Vector
.
// отвратительный макрос, чтобы получить вектор
// с проверкой выхода за пределы допустимого диапазона
#define vector Vector
Это значит, что там, где вы написали слово vector
, компилятор увидит слово Vector
. Этот трюк ужасен тем, что вы видите не тот код, который видит компилятор. В реальных программах макросы являются источником довольно большого количества запутанных ошибок (разделы 27.8 и A.17).
Мы сделали то же самое, чтобы реализовать проверку выхода за пределы допустимого диапазона для класса string
.