函数原型
/**
*@function 申请n个元素的内存空间
*@param n 元素个数
*/
void reserve (size_type n);
应用场景
函数的作用就是申请内存空间,我们知道vector是一种可以自动扩展的容器,也即是可以根据元素个数自动申请内存,那么有什么必要去主动为它申请内存空间呢?答案是有必要的,我们来看个例子。假如要使用vector存储1000个数据:
方法1:vector<int> vec, 然后调用1000次 vec.push_back(***);
方法2:vector<int> vec,然后调用vec.reserve(1000)申请1000个元素的内存,再调用1000次 vec.push_back(***);
方法1要进行若干次内存分配,而方法2只需要进行1次内存分配,其效率立见高下,所以在需要对大量数据进行处理的时候就要使用reserve主动分配内存以提升程序执行效率。
reserve与resize的区别
看到方法2,我们可能会有疑问,它不是相当于
vector<int> vec(1000),,然后调用1000次 vec.push_back(***);
或者
int arr[1000],然后调用arr[i]=***;
么?No!效果是一样的,都是一次性分配内存,效率很高,但是它们同方法2存在本质的区别。reserve方法是只分配内存,不创建对象,而这两种方法都创建了1000个int类型的对象。而resize函数正是在分配内存的同时会创建对象。
感觉它们的功能都大同小异,那么什么情况下使用哪一个函数比较好呢?我自己的意见是在能用reserve的情况下都用reserve,只能用resize的情况下用resize,因为使用reserve只需要赋一次值(调用push_back的时候),而resize需要赋两次值(初始化和调用push_back的时候)。哪种情况下只能用resize呢?就是当vector已经有数据,需要扩展数据的时候。
异常处理
这里才是我真正想要记录的知识点,我们在使用这个函数的时候,如果申请的内存比较大的话就需要注意,如果处理不当程序很可能会出现崩溃。例如点云数据很容易上百万级,需要的内存就比较大了。
我们查看reserve这个函数,资料说它能传入的最大数据是max_size,等于1073741823,当传入的参数比它大时就会抛出一个 length_error异常。这是一个很大的数,我们可以简单的做一个计算,如果存储的数据类型为int型,每个int数据占用4个字节,max_size个int数据就相当于4096GB。想想我们怎么也不可能申请这么大的内存,所以这个异常是极少出现的。但是我们传入远远小于max_size的参数时程序也会出现崩溃,这是为什么呢?这就是因为除了这个异常,还有另一个异常也会导致程序出错。
内存的分配不只受到reserve函数参数的限制,更受到电脑总内存的限制,一般的电脑内存也就4GB、8GB,操作系统、应用程序执行也需要一部分内存,剩下的内存才是能够给reserve分配的。然后我们认为操作系统和应用程序大概也就用2GB的内存吧,那还剩下至少2GB的内存,我不申请2GB,我申请1GB总行吧,但是结果很令人沮丧,程序出现异常。我做了一个实验,申请的内存大概到200MB的时候就有一定概率出现异常。我的理解是vector的内存是连续的,电脑中虽然还剩下比较多的内存,但是连续的内存却只有几百MB,所以最多只能申请那么多内存,多了就会失败。你已经看出来了,这个异常就是bad_alloc,分配内存失败。
reserve这个函数是能抛出异常的,如果我们没有去捕获这个异常,那么程序运行到这里就直接崩溃了,加上异常捕获就能避免这个问题,只要输出异常信息或在软件界面上给出用户提示信息就可以了。另一个就是要尽量限制传入参数的值,减少出现异常的情况。
所以一定要对异常处理有一个清晰的认识,有时候软件崩溃或许就是因为你没有对能抛出异常的函数进行异常处理,而这些问题又不算真正的bug,让你陷入错误的解决道路中。所以一条有用的定律就是凡是有异常抛出的函数都加上异常处理。