C++STL第二篇(vector的原理用法)
  3dygdw7fVgD7 2024年03月11日 62 0
C++

vector

vector的数据安排以及操作方式,与array非常相似,两者的唯一差别在于空间的运用的灵活性。Array是静态空间,一旦配置了就不能改变,要换大一点或者小一点的空间,可以,一切琐碎得由自己来,首先配置一块新的空间,然后将旧空间的数据搬往新空间,再释放原来的空间。Vector是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素。因此vector的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必害怕空间不足而一开始就要求一个大块头的array了。

Vector的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率,一旦vector旧空间满了,如果客户每新增一个元素,vector内部只是扩充一个元素的空间,实为不智,因为所谓的扩充空间(不论多大),一如刚所说,是”配置新空间-数据移动-释放旧空间”的大工程,时间成本很高,应该加入某种未雨绸缪的考虑,稍后我们便可以看到vector的空间配置策略。

image-20240307143601112

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 = &regina[0];
	int* end = &regina[regina.size() - 1];

	for (; start <= end; start++) {
		cout << *start << endl;
	}

image-20240307151258152

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;

image-20240307155400151

在这段代码中,我们首先向regina中添加了10万个元素,然后调用resize(10)regina的大小改变为10。接着通过创建临时vector对象并与regina交换来释放多余的内存。让我解释一下容量是如何变小的。

  1. 初始状态:
    • 在向regina中添加10万个元素后,capacity()可能会大于或等于10万,因为vector在需要时会分配比实际所需更多的内存,以减少频繁的重新分配和复制。
  2. 调用resize(10)
    • 当调用resize(10)时,regina的大小被改变为10,但它的容量仍然可能保持不变。这是因为resize函数通常只改变vector的大小,而不会改变其容量,除非指定了新的容量值。
  3. 通过临时vector对象和swap释放多余内存:
    • 接着使用了一个巧妙的技巧,创建了临时的vector对象,并通过swap函数释放了regina中多余的内存。这样做会使regina的容量变得刚好足够存储当前的元素数量,没有额外的内存占用。

vector VS valarray VS array

  1. std::vector
    • std::vector 是标准库中最常用的动态数组容器。
    • 它可以动态增长和缩小,即在运行时可以添加或删除元素。
    • vector 内部使用动态内存分配来存储元素,因此可以根据需要动态调整其大小。
    • 支持随机访问,插入和删除操作效率较高。
  2. std::valarray
    • std::valarray 代表值数组,是用于执行数学运算的数组。
    • valarray 提供了一些数学运算函数,如对每个元素进行操作、求平方根、求平均值等。
    • valarray 的设计旨在提供高性能的数学运算,但在某些情况下可能不够灵活。
    • 在实际开发中,valarray 往往被认为是一个在数值计算方面更专业的工具,而不是通用的容器。
  3. 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;
}

image-20240310211149852

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2024年03月11日 0

暂无评论

推荐阅读
  8Tw5Riv1mGFK   19天前   30   0   0 C++
  BYaHC1OPAeY4   12天前   31   0   0 C++
  yZdUbUDB8h5t   15天前   22   0   0 C++
  oXKBKZoQY2lx   3天前   8   0   0 C++
3dygdw7fVgD7