C++STL第二篇(vector的原理用法)
vector
vector的数据安排以及操作方式,与array非常相似,两者的唯一差别在于空间的运用的灵活性。Array是静态空间,一旦配置了就不能改变,要换大一点或者小一点的空间,可以,一切琐碎得由自己来,首先配置一块新的空间,然后将旧空间的数据搬往新空间,再释放原来的空间。Vector是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素。因此vector的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必害怕空间不足而一开始就要求一个大块头的array了。
Vector的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率,一旦vector旧空间满了,如果客户每新增一个元素,vector内部只是扩充一个元素的空间,实为不智,因为所谓的扩充空间(不论多大),一如刚所说,是”配置新空间-数据移动-释放旧空间”的大工程,时间成本很高,应该加入某种未雨绸缪的考虑,稍后我们便可以看到vector的空间配置策略。
Vector维护一个线性空间,所以不论元素的型别如何,普通指针都可以作为vector的迭代器,因为vector迭代器所需要的操作行为,如operaroe*, operator->, operator++, operator--, operator+, operator-, operator+=, operator-=, 普通指针天生具备。Vector支持随机存取,而普通指针正有着这样的能力。所以vector提供的是随机访问迭代器(Random Access Iterators).
#include <vector> // 包含 vector 头文件
// 创建一个存储 int 类型的 vector
std::vector<int> intVector;
// 向 vector 尾部添加元素
intVector.push_back(42);
// 获取 vector 的大小(元素个数)
int size = intVector.size();
// 获取 vector 的容量(当前分配的存储空间大小)
int capacity = intVector.capacity();
// 访问 vector 中的元素
int element = intVector[0]; // 使用下标访问
// 遍历 vector 中的所有元素
for (int i = 0; i < intVector.size(); ++i) {
std::cout << intVector[i] << " ";
}
// 使用迭代器进行遍历
for (auto it = intVector.begin(); it != intVector.end(); ++it) {
std::cout << *it << " ";
}
以上就是所有vector常用的语法,具体通过下述一个小例子来说。
vector<int> regina;
for (int i = 0; i < 10; i++) {
regina.push_back(i);
cout << regina.capacity() << endl;
}
int* start = ®ina[0];
int* end = ®ina[regina.size() - 1];
for (; start <= end; start++) {
cout << *start << endl;
}
Vector所采用的数据结构非常简单,线性连续空间,它以两个迭代器_Myfirst和_Mylast分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器_Myend指向整块连续内存空间的尾端
。所谓动态增加大小,并不是在原空间之后续接新空间(因为无法保证原空间之后尚有可配置的空间),而是一块更大的内存空间,然后将原数据拷贝新空间,并释放原空间。因此,对vector的任何操作,一旦引起空间的重新配置,指向原vector的所有迭代器就都失效了。这是程序员容易犯的一个错误,务必小心。
常用操作
assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);//将n个elem拷贝赋值给本身。
vector&operator=(const vector &vec);//重载等号操作符
swap(vec);// 将vec与本身的元素互换。
--------
size();//返回容器中元素的个数
empty();//判断容器是否为空
resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
resize(int num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长>度的元素被删除。
capacity();//容器的容量
reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。
-------------
at(int idx); //返回索引idx所指的数据,如果idx越界,抛出out_of_range异常。
operator[];//返回索引idx所指的数据,越界时,运行直接报错
front();//返回容器中第一个数据元素
back();//返回容器中最后一个数据元素
-------------
insert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count个元素ele.
push_back(ele); //尾部插入元素ele
pop_back();//删除最后一个元素
erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
erase(const_iterator pos);//删除迭代器指向的元素
clear();//删除容器中所有元素
在C++的STL中,capacity()
和size()
是vector
类的两个成员函数,用于获取vector
对象的容量和大小。
capacity()
函数返回vector
对象在不重新分配内存的情况下能够容纳的元素数量。换句话说,capacity()
表示vector
当前分配的内存空间大小,而不是vector
实际包含的元素数量。当vector
中的元素数量达到当前容量时,如果需要继续添加元素,vector
会分配更大的内存空间,并将原有元素复制到新的内存空间中。size()
函数返回vector
对象当前包含的元素数量。换句话说,size()
表示vector
中实际存储的元素数量,而不考虑vector
实际分配的内存空间大小。
举个例子,假设你有一个vector
对象regina
,初始时capacity()
可能为10,而size()
为0。这意味着regina
的内存空间能够容纳10个元素,但实际上它目前并没有存储任何元素。当你向regina
中添加元素时,size()
会逐渐增加,直到等于capacity()
,此时vector
可能会重新分配更大的内存空间。
总结一下:
capacity()
:表示vector
当前分配的内存空间大小,不考虑实际存储的元素数量。size()
:表示vector
实际存储的元素数量,不考虑分配的内存空间大小。
vector<int> regina;
for (int i = 0; i < 100000; i++) {
regina.push_back(i);
}
cout << "capacity:" << regina.capacity() << endl;
cout << "size:" << regina.size() << endl;
//此时 通过resize改变容器大小
regina.resize(10);
cout << "capacity:" << regina.capacity() << endl;
cout << "size:" << regina.size() << endl;
//容量没有改变
vector<int>(regina).swap(regina);
/*在创建临时vector对象时,它会使用刚好
足够的内存来存储regina的元素,并且不会有
额外的内存占用。然后通过swap函数,regina
会将自己的内存与临时vector进行交换,
从而达到释放多余内存的目的。*/
cout << "capacity:" << regina.capacity() << endl;
cout << "size:" << regina.size() << endl;
在这段代码中,我们首先向
regina
中添加了10万个元素,然后调用resize(10)
将regina
的大小改变为10。接着通过创建临时vector
对象并与regina
交换来释放多余的内存。让我解释一下容量是如何变小的。
- 初始状态:
- 在向
regina
中添加10万个元素后,capacity()
可能会大于或等于10万,因为vector
在需要时会分配比实际所需更多的内存,以减少频繁的重新分配和复制。- 调用
resize(10)
:
- 当调用
resize(10)
时,regina
的大小被改变为10,但它的容量仍然可能保持不变。这是因为resize
函数通常只改变vector
的大小,而不会改变其容量,除非指定了新的容量值。- 通过临时
vector
对象和swap
释放多余内存:
- 接着使用了一个巧妙的技巧,创建了临时的
vector
对象,并通过swap
函数释放了regina
中多余的内存。这样做会使regina
的容量变得刚好足够存储当前的元素数量,没有额外的内存占用。
vector VS valarray VS array
std::vector
:std::vector
是标准库中最常用的动态数组容器。- 它可以动态增长和缩小,即在运行时可以添加或删除元素。
vector
内部使用动态内存分配来存储元素,因此可以根据需要动态调整其大小。- 支持随机访问,插入和删除操作效率较高。
std::valarray
:std::valarray
代表值数组,是用于执行数学运算的数组。valarray
提供了一些数学运算函数,如对每个元素进行操作、求平方根、求平均值等。valarray
的设计旨在提供高性能的数学运算,但在某些情况下可能不够灵活。- 在实际开发中,
valarray
往往被认为是一个在数值计算方面更专业的工具,而不是通用的容器。
std::array
:std::array
是固定大小的数组容器,其大小在编译时确定。array
在内存中是连续存储的,类似于原生数组,但提供了更多的功能和安全性。array
提供了数组的基本功能,如随机访问、迭代器等,但大小固定,不能动态增长或缩小。
#include <iostream>
#include <vector>
#include <valarray>
#include <array>
int main() {
// 1. std::vector 例子
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.push_back(6); // 添加元素
for (int i : vec) {
std::cout << i << " ";
}
std::cout << "\n";
// 2. std::valarray 例子
std::valarray<int> valarr = {1, 2, 3, 4, 5};
valarr += 5; // 对每个元素加5
for (int i : valarr) {
std::cout << i << " ";
}
std::cout << "\n";
// 3. std::array 例子
std::array<int, 5> arr = {1, 2, 3, 4, 5};
for (int i : arr) {
std::cout << i << " ";
}
std::cout << "\n";
return 0;
}
本文来自博客园,作者:ivanlee717,转载请注明原文链接:https://www.cnblogs.com/ivanlee717/p/18064831