Cherno C++学习笔记 P47 动态数组Vector
这一篇文章我们会介绍第一个标准模板库Standard Template Library当中的第一个容器,动态数组Vector。首先我们说一下有关于C++ STL,这些标准动态库为我们提供了各种容器来装我们的数据,根据我们有不同的需求,可以选择对应的容器。而且这些库都是模板库,也就是说什么类型的数据都可以装到里面去。但是如果只是使用STL,我们是不需要知道任何有关于模板类的知识的,我们只需要知道需要我们自己提供数据类型就可以了。
Vector本质上是一个动态数组容器,但是它的这个名字起的确实不是很合适,因为我们知道vector这个单词通常是用来表示向量的,但是C++ STL Vector确实和向量不能说是没有半毛钱关系,至少也是没什么相关性。所以就别纠结这个名字了,确实起的不合适。Vector与array相比,不同的地方在于,它可以随时调整大小。比如如果我们有一个长度为5的array,我们想要一个长度为6的,那么只能新建一个array,但是对于Vector,我们可以直接在后面多添加一个格子,那它就有6这么长了。
Vector是如何实现动态调整长度的?其实方式也是挺粗暴的。它的办法就是在我们需要添加新的格子的时候,去寻找新的更长的内存块,然后直接把原来内存里面的内容复制粘贴过去,然后再删除掉原来的内存里面的内容。这个其实并不是一个明智的选择,而且这也导致了STL Vector并不是我们可以选择的实现速度最快的Vector,很多时候我们都需要自己去实现。Cherno所在的EA就有EASTL,可以直接在GitHub上找到,有兴趣的朋友可以去看:https://github.com/electronicarts/EASTL
我们举一个例子来展示一下如何使用Vector,比如我们有这样一个Vertex类型:
struct Vertex {
float x, y, z;
};
我们如果想要建立一个数组,可以使用原始数组:
Vertex a[5];
但是如果我们希望可以把数组长度设置为变量,比如说我们需要在程序运行期间才能够知道需要多少内存,那么我们使用变量来确定原始数组的长度就会报错:
int m = 4;
Vertex a[m];
这样的话是无法通过的。当然我们可以给原始数组直接分配一个超大的内存,来保障肯定不会超过内存,但是我们都知道这种办法会造成很大的内存浪费,而且看起来也很呆。那么这个时候我们就可以使用我们的vector容器了:
int b = 10;
std::vector<Vertex>vertices(b);
比如用这种办法,我们就可以轻松的通过变量来分配内存。
但是需要注意的一点是,这样分配是直接使用的vertex object本身。那么如果我们这么写:
std::vector<Vertex*>vertices(b);
分配的也就成了Vertex指针。所以应该是使用指针更好还是object本身更好?这个其实是一个看情况且不确定的问题。最开始的时候肯定是倾向于直接使用object本身的,因为这样的一个好处是所有的object内存都整齐的排列在一起,这样进行操作会更加容易,更好进行迭代。但是这样带来的一个问题就是,当我们要进行扩展的时候,我们需要申请到同样是连续的一段内存,然后把这部分数据拷贝到这部分内存当中,所以扩展起来会非常麻烦。如果是用的指针,那么扩展的时候只需要多分配给一个object的内存即可。但是如果用指针迭代,需要在内存里面跳来跳去,会影响性能,所以一般在有大量存储且有扩展需求的时候,我们一般更倾向于使用指针。但是通常使用中我们还是更多的会直接存储object。
接下来我们看看vertex的一些基本功能,首先是在后面添加一个新的项,通常我们会使用的是push_back方法,也是非常常用的vector的方法,功能就是给我们的vector末尾再添加一项。
Vertex plus = { 3,5,8 };
vertices.push_back(plus);
如果我们想要返回一个vector的长度,就直接使用size方法即可:
std::cout << vertices.size() << std::endl;
如果我们想要循环访问一个vector当中的所有元素,当然可以用i++ for循环这种常规方法:
for (int i = 0; i < vertices.size(); i++) {
}
但是除了这个之外,我们还有可以使用range风格的for循环:
for (Vertex v: vertices) {
}
不过这个写法其实不好,因为我们会在每次循环的时候都产生一次复制粘贴,但是我们不希望有复制粘贴,所以可以改为引用:
for (Vertex& v: vertices) {
}
除此之外还有几个有用的function,一个是
vertices.clear();
可以直接把整个数组清空,size会被设为0;而如果我们想要删除某一个位置的值,那么需要使用erase功能。但是erase不能按照需要删除,如果想要删除,只能用迭代器的方式:
vertices.erase(vertices.begin() + 1);
这样我们就删掉了第二个迭代器指向的内容。
以上就是有关于vector的一些基础知识了,后面我们会讲到如何提高我们vector的运行速度。