在接触到STL时,我发现大部分容器都有两个成员函数:operator[]()和at()。乍一看,这实现的不就是同一种功能,为什么要存在两个函数?我们去www.cplucplus.com看一看。
不仅仅是容器类,std::string和std::basic_string中也同时存在operator[]()函数和at()函数。
从以上众多截图中可以看出:array、deque、map、unordered_map、vector、basic_string和string类中同时存在operator[]()和at(0函数(可能其他类中也有,但是本文只研究这么多:D)。
引入operator[]()的原因,我认为是为了增强代码的可读性,并且用户使用起来也更加方便,更像是在使用c语言中的数组。
接下来上一些代码吧:
/*code block 1*/
int main() {
std::array iArray;
std::cout << iArray.at(6) << std::endl;
std::cout << "-------------" << std::endl;
std::cout << iArray[6] << std::endl;
return 0;
}
代码块1的运行结果如上图所示。
我们定义了一个大小为5的array,元素类型为int。位置为6的元素实际上不存在,代码分别使用at()和operator[]()对其访问。为了使结果更加明显,中间加入了分割线。
调用at()方法,如果下标越界,会使程序抛出一个std::out_of_range的异常。
而调用operator[]方法,如果下标越界,程序会崩溃。
你以为我写了这么多,贴了这么多图,就为了告诉你这个吗?当然不仅仅这些……
/*code block 2*/
int main() {
std::deque iDeque;
std::cout << iDeque.at(6) << std::endl;
std::cout << "-------------" << std::endl;
std::cout << iDeque[6] << std::endl;
return 0;
}
代码块2的运行结果如上图所示,可以看出,运行结果与代码块1没有太大的区别。这也说明了deque和array这两个函数的功能是一致的。
/*code block 3*/
int main() {
std::deque iDeque;
iDeque.resize(5);
std::cout << iDeque.at(6) << std::endl;
std::cout << "-------------" << std::endl;
std::cout << iDeque[6] << std::endl;
return 0;
}
代码块3的运行结果与代码块2的运行结果完全一致。
/*code block 4*/
int main() {
std::map isMap;
isMap[1] = "1";
std::cout << "-------------" << std::endl;
isMap.at(2) = "2";
return 0;
}
代码块4的运行结果如上图所示。wait,wait,wait!!!怎么会这样?map使用operator[]并没有出错,程序打印出了分隔符,接下来对at()的使用抛出了异常。但是,为什么停止工作?难道之前的猜想都是错的?
对map使用mapName[key] = value,实际上是生成一个键值对(make_pair(key, value)),将该键值对插入到map中。所以对map使用operator[]()函数不会导致错误(前提是key和value的类型要与定义一致)。
主要代码只保留一句isMap.at(2); 编译、运行、查看结果,发现运行结果与代码块4的运行结果不变。说明这次的程序崩溃是由at()函数引起的,在崩溃之前,它还抛出了std::out_of_range异常。
那么,以前的种种猜想是不是错了呢?回顾之前的代码并稍作修改:
/*code block 5*/
int main() {
std::array iArray;
iArray.at(5) = 5;
}
代码块5运行出的结果既抛出了std::out_of_range异常,又停止了工作。我们是不是冤枉了operator[]()?
/*code block 6*/
int main() {
std::array iArray;
iArray[6] = 6;
return 0;
}
代码6的运行结果如上图,当点击了“关闭程序”后,命令行显示如下:
说明我们没有冤枉operator[](),它对下标的不当操作也会导致程序停止工作。
/*code blocks 7*/
int main() {
std::deque iDeque;
iDeque[16] = 5;
std::cout << iDeque.size() << std::endl;
std::cout << iDeque[16] << std::endl;
std::cout << iDeque.at(16) << std::endl;
return 0;
}
在代码块中,首先输入iDeque[16] = 5,编译,程序能够正常运行,这已经与array有所不同了。
接下来,我想看看deque的大小是否改变了,结果输出依然为0。
再输入iDeque[16]的值,可以正常输出,是5.
加入最后一句,运行结果如下:
事实上,内存中iDeque[16]的位置上的值为5,at()函数崩溃是因为它在调用时做了下标检查,因为iDeque的size()为0,所以16这个下标是无效的,所以抛出了std::out_of_range异常。
/*code block 8*/
int main() {
std::unordered_map isUnorderedMap;
isUnorderedMap[3] = "three";
std::cout << isUnorderedMap.size() << std::endl;
std::cout << isUnorderedMap[3] << std::endl;
std::cout << isUnorderedMap.at(3) << std::endl;
return 0;
}
代码块8的运行结果如上图所示,在使用operator[]对isUnorderedMap中添加元素之后,用at()函数可以正常访问到该元素。试图用at()函数去添加元素时,程序抛出std::out_of_range异常。
/*code block 9*/
int main() {
std::vector iVector;
iVector[2] = 5;
return 0;
}
代码块9的运行结果如山图所示,虽然只有简单的一行代码,仍然使得程序停止了。因为iVector是一个空的vector,而vector不支持用这种方式添加元素,所以程序停止工作。
/*code block 10*/
int main() {
std::vector iVector;
iVector.at(3) = 5;
return 0;
}
代码块10的运行结果如上图,程序抛出了std::out_of_range异常。
/*code block 11*/
int main() {
std::vector iVector(5);
iVector.at(3) = 5;
iVector[2] = 9;
std::cout << "-------------" << std::endl;
std::cout << iVector[5] << std::endl;
std::cout << "-------------" << std::endl;
std::cout << iVector.at(5) << std::endl;
return 0;
}
代码块11的运行结果如上图,可以看出,虽然iVector只有5个元素,但是使用operator[]()访问位置为5的元素不会出错,因为operator[]()没有做下标有效性检查。使用at()函数访问位置为5的元素,因为进行了下标的有效性判断,所以抛出了std::out_of_range异常。
/*code block 12*/
int main() {
std::vector iVector;
iVector.reserve(8);
std::cout << iVector.size() << std::endl;
std::cout << iVector.capacity() << std::endl;
std::cout << "-------------" << std::endl;
std::cout << iVector[5] << std::endl;
std::cout << "-------------" << std::endl;
std::cout << iVector.at(5) << std::endl;
return 0;
}
代码块12的运行结果如上图。通过调用reserve()方法,将iVector的capacity改为8,然而size还是0,所以通过at()访问会抛出std::out_of_range异常,而使用operator[]()访问则不会。
/*code block 13*/
int main() {
std::string str1 = "str1";
std::cout << str1.size() << std::endl;
std::cout << "-------------" << std::endl;
std::cout << "[" << str1[str1.size()] << "]" << std::endl;
std::cout << "-------------" << std::endl;
std::cout << "[" << str1.at(str1.size()) << "]" << std::endl;
return 0;
}
代码块13的运行结果如上图所示。str1长度为4,用operator[]()访问位置为4的元素,输出为一个空格字符(也有可能为其他字符)。用at()访问位置为4的元素,抛出std::out_of_range异常。
/*code block 14*/
int main() {
std::basic_string str = "str";
std::cout << str.size() << std::endl;
std::cout << "-------------" << std::endl;
std::cout << "[" << str[str.size()] << "]" << std::endl;
std::cout << "-------------" << std::endl;
std::cout << "[" << str.at(str.size()) << "]" << std::endl;
return 0;
}
代码块14的运行结果如上图,std::basic_string是模板类,运行结果与代码块13一致,这里不再赘述。
STL提供的容器中:
array、deque、vector不能通过operator[]()向容器中添加元素。
map、unordered_map类可以通过operator[]向容器中添加元素。
以上容器均不能通过at()函数向容器重添加元素。
at()函数在被调用时,会检查下标的有效性(与容器的size()比较而不是capacity()(例如vector)),若下标有效则返回对应位置的元素,否则抛出std::out_of_range异常。
operator[]()函数在被调用时,不检查下标的有效性。
例外:对于array而言,无论使用operator[]还是at()都会做下标有效性检查(代码块6)。
注:以上所有代码均使用code::blocks17.12进行编译。
注:转载请注明出处,谢谢。