C++的vector类

目录

简介

特点

接口

构造函数

迭代器函数

Capacity系列

element access系列

modifiers系列

定义在全局的重载函数

find

总结


简介

vector 是 C++ 标准模板库(Standard Template Library,简称 STL)中的一个模板类,用于表示可以改变大小的数组。它是一种动态数组,能够在运行时自动调整其大小以适应元素的添加和删除。以下是 vector 的一些主要特点和用法:

特点

  1. 动态大小vector 的长度可以根据需要动态地增长或收缩。
  2. 连续存储vector 中的所有元素都存储在连续的内存位置中,这使得通过索引访问元素非常快速。
  3. 类型安全vector 是模板类,因此可以存储任何类型的元素,同时保持类型安全。
  4. 迭代器支持vector 提供了迭代器,允许使用迭代器进行元素遍历。
  5. 内存管理vector 自动管理内存分配和释放,避免了手动内存管理的复杂性。

下面通过vector的接口函数来进行vector的讲解。

接口

这是vector提供的内置成员函数

构造函数

提供了缺省、迭代器构造等多种构造

1)缺省:

Constructs an empty container, with no elements.生成一个空白的vector容器,没有任何元素。

2)用n个value去初始化

Constructs a container with n elements. Each element is a copy of val.

int main()
{
    vector<int> arr(10, 0);

    for (auto ch : arr)
        cout << ch << endl;
    return 0;
}

我们用10个0去初始化vector,如下是运行结果。

3)拷贝构造

int main()
{
    vector<int> arr(10, 0);
    vector<int> arr2(arr);

    for (auto ch : arr2)
        cout << ch << endl;
    return 0;
}

借助arr去拷贝出arr2,打印的结果同上。

4)采用迭代器区间

主要借助begin和end函数

这样就是打印8个0。

迭代器函数

begin

迭代器函数的行为类似指针,但不等价于指针,在使用时可以类比指针理解。

end

需要注意的是end指向最后一个元素的下一个位置。而不是最后一个元素。

Return iterator to end

Returns an iterator pointing to the past-the-end character of the string.

下面是示例:

// string::begin/end
#include <iostream>
#include <string>

int main ()
{
  std::string str ("Test string");
  for ( std::string::iterator it=str.begin(); it!=str.end(); ++it)
    std::cout << *it;
  std::cout << '\n';

  return 0;
}

由于行为类似指针所以可以采用*(解引用)操作符去访问it。当然,果如繁琐的类型可以用auto推理。

rbegin()、rend()

相当于rbegin函数返回的是倒数第一个。rend则返回第一个的索引。

由于迭代器行为类似指针,因此也支持进行数据的修改。需要注意的是,在迭代的过程中,仍用++迭代。

cbegin cend、 crbegin crend则是表示const修饰过的迭代器

若要求只读,则可以用cbegin。

Capacity系列

这一系列主要是与容量的调整有关的接口

size() max_size()分别用来调出当然容量(含多少元素)是多少,可以容纳的最大容量是多少。

resize

用来修正size的大小。同时用来预开辟空间。预开辟空间十分重要,可以避免多次异地扩容。

这样size从10变成了20.

capacity

capacity返回的是容量,而不是size,体现的是可用空间。

int main()
{
	vector<int> arr(10, 0);
	arr[0] = 1;
	arr[9] = 9;
	cout << arr.size() << endl;
	cout << arr.capacity() << endl;

	arr.resize(20);
	cout << arr.size() << endl;
	cout << arr.capacity();

	return 0;
}

empty

Test whether vector is empty.用来检测容器是否为空。

reserve

对capacity进行修改,用来预开辟空间和进行空间修正。一般来说只大不小。需要注意的是,reserve只能将capcacity进行修正,不能对size进行修改。因此reserve(10)之后,可能size仍然为0。此时访问下标可能会造成越界访问

对于string类而言开辟n,实际内部开辟了n + 1个空间,用来存放\0。


int main()
{
	vector<int> arr;
	arr.reserve(11);
	
	for (int i = 0; i < 11; i++)
	{
		arr.push_back(i);
		cout << arr[i] << endl;
	}
	return 0;
}

int main()
{
	vector<int> arr;
	arr.reserve(11);
	arr.resize(11);
	
	for (int i = 0; i < 11; i++)
	{
		arr[i] = i;
		cout << arr[i] << endl;
	}
	return 0;
}

shrink_to_fit

调整到合适的capacity大小,一般用处不大。

element access系列

1)

支持解引用,有专门的读与写的重载函数。

2)at

也适用于访问成员,一般用出不多。给定下标,访问成员

3)front、back系列

一般用back用的比较多。front用arr[0]替代

data()函数

可以得到一个指向有效数据的指针。这个功能在C++11及以后的版本中可用。

以下是一个使用 data() 函数的例子:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 使用 data() 函数获取指向 vector 数据的指针
    int* dataPtr = vec.data();

    // 通过指针访问 vector 的元素
    for (size_t i = 0; i < vec.size(); ++i) {
        std::cout << dataPtr[i] << ' ';
    }
    std::cout << std::endl;

    return 0;
}
在上面的代码中,vec.data() 返回了一个指向 vec 中第一个元素的指针。然后我们使用这个指针来遍历并打印 vector 中的所有元素。

data() 返回的指针在 vector 被重新分配内存(例如通过 resize, reserve 或添加元素使得 vector 需要更多内存)后可能变得无效。因此,在使用 data() 返回的指针时,应避免修改 vector 的大小。

modifiers系列

1)assign

重新分配内容及size,但不会修改capacity

Assigns new contents to the vector, replacing its current contents, and modifying its size accordingly.

可以传入迭代器、或者正常传参。

2)push_back()

尾插

3)pop_back尾删

4)insert插入

用来在任意位置插入元素

需要注意的是,在插入的位置需要传入的是迭代器,而不是下标.

int main()
{
    vector<int> arr;
    arr.reserve(100);
    auto it = arr.begin();
    arr.insert(it, 5, 10);
    for (auto& ch : arr)
        cout << ch << endl;
    return 0;
}

5)erase

用来删除某个或者某一段位置的数据,需要传入迭代器

6)swap

swap不只能交换内容,还可以交换size、capacity等信息。

int main()
{
	vector<int> arr;
	vector<int> arr2;
	arr.resize(100);
	arr2.resize(200);
	cout << arr.size() << " " << arr2.size() << endl;
	cout << arr.capacity() << " " << arr2.capacity() << endl;

	arr.swap(arr2);

	cout << arr.size() << " " << arr2.size() << endl;
	cout << arr.capacity() << " " << arr2.capacity() << endl;

	return 0;
}

7)clear

用于清空数据,包括size、content但不会销毁capacity

int main()
{
	vector<int> arr;
	arr.resize(100);

	cout << arr.capacity()<<" ";
	cout << arr.size() <<  endl;
	arr.clear();

	cout << arr.capacity() << " ";
	cout << arr.size() << endl;

	return 0;
}

定义在全局的重载函数

拿<举例

在C++中,std::vector 的 operator< 用于比较两个 vector 容器的大小。比较的规则是从两个 vector 的第一个元素开始,依次比较对应位置的元素,直到找到一个元素对,这两个元素在它们各自的 vector 中有不同的比较结果。以下是 operator< 的工作原理:

  1. 比较两个 vector 的对应元素,使用元素类型的 < 运算符。
  2. 如果在某个索引位置上,lhs 的元素小于 rhs 的对应元素,则认为 lhs 小于 rhs
  3. 如果在某个索引位置上,lhs 的元素大于 rhs 的对应元素,则认为 lhs 大于 rhs
  4. 如果所有对应元素都相等,但 lhs 的长度小于 rhs 的长度,则认为 lhs 小于 rhs
  5. 如果所有对应元素都相等,且两个 vector 的长度也相等,则认为它们相等,operator< 将返回 false

对于包含小数的 vector,比较过程如下:

  • 首先比较两个小数的整数部分。
  • 如果整数部分相同,则比较小数部分的十分位。
  • 如果十分位也相同,则比较百分位,依此类推。

以下是一个简化的示例,说明如何实现比较两个包含小数的 vector

#include <iostream>
#include <vector>
#include <algorithm> // 用于 std::lexicographical_compare

int main() {
    std::vector<double> vec1 = {1.1, 2.2, 3.3};
    std::vector<double> vec2 = {1.1, 2.3, 3.3};

    // 使用 std::lexicographical_compare 比较两个 vector
    bool result = std::lexicographical_compare(vec1.begin(), vec1.end(),
                                               vec2.begin(), vec2.end());

    if (result) {
        std::cout << "vec1 is less than vec2" << std::endl;
    } else {
        std::cout << "vec1 is not less than vec2" << std::endl;
    }

    return 0;
}

在上面的代码中,std::lexicographical_compare 函数按照字典顺序比较两个序列,如果第一个序列在字典顺序上小于第二个序列,则返回 true。对于小数,它会使用 < 运算符,该运算符已经按照整数部分和小数部分的大小顺序进行了比较。

对于自定义类型,你需要确保 < 运算符对于该类型是定义好的,以 std::vector 的 operator< 能够正确比较元素。

如果 < 运算符没有为你的自定义类型定义,你需要自己定义它

下面的官方的示例

比较的是元素对,而不是元素的多少。

SWAP

定义在全局之后,就需要传入两个vector<T>对象。

find

这是他的头文件。

Find value in range

Returns an iterator to the first element in the range [first,last) that compares equal to val. If no such element is found, the function returns last.

找不到,返回end - 1的索引,打印7

找得到,返回4在arr中的索引,打印4

总结

1. vector是表示可变大小数组的序列容器。
2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理
3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
6. 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。
  • 16
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值