[C++] 关于operator[]()和at()

在接触到STL时,我发现大部分容器都有两个成员函数:operator[]()和at()。乍一看,这实现的不就是同一种功能,为什么要存在两个函数?我们去www.cplucplus.com看一看。

array

[C++] 关于operator[]()和at()_第1张图片


deque

[C++] 关于operator[]()和at()_第2张图片


forward_list

[C++] 关于operator[]()和at()_第3张图片


list

[C++] 关于operator[]()和at()_第4张图片


map

[C++] 关于operator[]()和at()_第5张图片

multimap

[C++] 关于operator[]()和at()_第6张图片


priority_queue

[C++] 关于operator[]()和at()_第7张图片

queue

[C++] 关于operator[]()和at()_第8张图片


multiset

[C++] 关于operator[]()和at()_第9张图片

set

[C++] 关于operator[]()和at()_第10张图片


stack

[C++] 关于operator[]()和at()_第11张图片


unordered_map

[C++] 关于operator[]()和at()_第12张图片

unordered_multimap

[C++] 关于operator[]()和at()_第13张图片


unordered_multiset

[C++] 关于operator[]()和at()_第14张图片

unordered_set

[C++] 关于operator[]()和at()_第15张图片


vector

[C++] 关于operator[]()和at()_第16张图片

vector

[C++] 关于operator[]()和at()_第17张图片


不仅仅是容器类,std::string和std::basic_string中也同时存在operator[]()函数和at()函数。

basic_string

string


从以上众多截图中可以看出: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;
}

[C++] 关于operator[]()和at()_第18张图片

[C++] 关于operator[]()和at()_第19张图片

代码块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;
}

[C++] 关于operator[]()和at()_第20张图片

[C++] 关于operator[]()和at()_第21张图片

代码块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;
}

[C++] 关于operator[]()和at()_第22张图片

[C++] 关于operator[]()和at()_第23张图片

代码块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;
}

[C++] 关于operator[]()和at()_第24张图片

[C++] 关于operator[]()和at()_第25张图片

代码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.

加入最后一句,运行结果如下:

[C++] 关于operator[]()和at()_第26张图片

[C++] 关于operator[]()和at()_第27张图片

事实上,内存中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;
}

[C++] 关于operator[]()和at()_第28张图片

[C++] 关于operator[]()和at()_第29张图片

代码块8的运行结果如上图所示,在使用operator[]对isUnorderedMap中添加元素之后,用at()函数可以正常访问到该元素。试图用at()函数去添加元素时,程序抛出std::out_of_range异常。

/*code block 9*/
int main() {
    std::vector iVector;

    iVector[2] = 5;

    return 0;
}

[C++] 关于operator[]()和at()_第30张图片

代码块9的运行结果如山图所示,虽然只有简单的一行代码,仍然使得程序停止了。因为iVector是一个空的vector,而vector不支持用这种方式添加元素,所以程序停止工作。

​​/*code block 10*/
int main() {
    std::vector iVector;

    iVector.at(3) = 5;

    return 0;
}

[C++] 关于operator[]()和at()_第31张图片

[C++] 关于operator[]()和at()_第32张图片

代码块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;
}

[C++] 关于operator[]()和at()_第33张图片

[C++] 关于operator[]()和at()_第34张图片

代码块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;
}

[C++] 关于operator[]()和at()_第35张图片

[C++] 关于operator[]()和at()_第36张图片

代码块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;
}

[C++] 关于operator[]()和at()_第37张图片

[C++] 关于operator[]()和at()_第38张图片

代码块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;
}

[C++] 关于operator[]()和at()_第39张图片

[C++] 关于operator[]()和at()_第40张图片

代码块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进行编译。

注:转载请注明出处,谢谢。

你可能感兴趣的:(C++)