范围检查
按照默认方式,标准库的 vector
并不提供对区间范围的检查。例如,
void f()
{
int i = phone_book[1001].number; // 1001超出了区间范围
// ...
}
这个初始化很可能将某个随机的值赋给 i
,而不是给出一个错误。这当然不是人们所希望的,所以我在下面各章里要使用 vector
的一个经过简单修订的版本,它带有区间范围检查,称为 Vec
。一个 Vec
就像是一个 vector
,只是当某个下标超出了区间范围时,它将抛出一个类型为 out_of_range
的异常。
实现像 Vec
这样的类型以及有效地使用异常的技术将在11.12节、8.3节和第14章讨论。但无论如何,下面的定义对于本书中的所有例子都足够:
template<class T> class Vec : public std::vector<T>
{
public:
Vec() : vector<T>() {}
Vec(int s) : vector<T>(s) {}
T& operator[] (int i) { return at(i); } // 检查区间范围
const T& operator[](int i) const { return at(i); } // 检查区间范围
};
这里的 at()
操作是 vector
的下标操作,如果它的参数超过了该 vector
的区间范围,at()
就会抛出一个 out_of_range
类型的异常。
现在回到保存名字和电话号码的问题。我们现在用一个 Vec
来保证所有超出范围的访问都将被抓住。例如,
Vec<Entry> phone_book(1000);
void print_entry(int i) // 简单地使用,完全像用vector
{
cout << phone_book[i].name << ' ' << phone_book[i].number << '\n';
}
一个超范围的访问将抛出一个异常,用户可以捕捉到它。例如,
void f()
{
try {
for(int i = 0; i < 10000; i++) print_entry(i);
}
catch(out_of_range) {
cout << "range error\n";
}
}
在企图用 i=1000
访问 phone_book[i]
时,异常将被抛出,而后被捕捉。
如果用户未捕捉这类异常,程序就将按照一种定义良好的方式终止,而不会继续下去,也不会以某种未定义的方式失败。使异常所造成的奇怪现象最小化的一种方式是让 main()
采用一个 try-
块作为体:
int main()
try {
// 你的代码
}
catch (out_of_range) {
cerr << "range error\n";
}
catch (...) {
cerr << "unknown exception thrown\n";
}
在这里提供了一个默认的异常处理器。这样,如果我们未能捕捉某些异常,它就会在标准的错误诊断输出流 cerr
(21.2.1节)打印出一个错误信息。
🔚