探索C++的存储箱:动态数组容器类vector

引言

在现代 C++ 开发中,vector 是最常用的数据容器之一。作为一个动态数组,vector 结合了数组的高效随机访问与灵活的动态内存管理功能,极大地方便了开发者对数据集合的操作。在这篇文章中,我们将深入探讨 vector 的基本用法、内存管理、性能优化以及在特定场景中的应用。

1.vector简介 

std::vector 作为 C++ 中的标准容器,提供了一系列丰富的成员函数来操作和管理其元素。这些函数可以分为以下几类:容量管理函数、元素访问函数、修改器函数和迭代器相关函数。

vector作为一种动态数组容器,有以下特点:

动态大小:可以根据需要自动调整存储元素的数量,无需手动管理内存大小。

高效的随机访问:支持通过索引快速访问元素,访问时间复杂度通常为常数。

元素同类型:存储到元素通常为相同类型。

由于其灵活性和高效的内存管理,vector常用于数据集合的存储管理动态数据结构(如栈、队列)的实现需要频繁增删元素的场景需要高效随机访问的大数据量处理

相比数组,vector 的自动扩容、边界检查、内存管理等特性让代码更加健壮,减少了程序员在内存管理和边界条件处理上的负担,使得代码更加简洁和安全。

2.vector的构造 

vector在使用时需要包含头文件<vector>。

2.1函数原型

default (1)	
explicit vector (const allocator_type& alloc = allocator_type());
fill (2)	
explicit vector (size_type n);
         vector (size_type n, const value_type& val,
                 const allocator_type& alloc = allocator_type());
range (3)	
template <class InputIterator>
  vector (InputIterator first, InputIterator last,
          const allocator_type& alloc = allocator_type());
copy (4)	
vector (const vector& x);
vector (const vector& x, const allocator_type& alloc);

move (5)	
vector (vector&& x);
vector (vector&& x, const allocator_type& alloc);

initializer list (6)
vector (initializer_list<value_type> il,
       const allocator_type& alloc = allocator_type());

explicit关键字意味着这个构造函数不能用用于隐式转换

2.2深入解析 

(1)默认构造函数

	vector<int> vec;

创建一个空的vector,此时size()和capacity()均为0,当需要再后续过程中插入元素时,可以使用这种方式。

(2) 指定大小和初始值的构造函数

explicit vector (size_type n);
         vector (size_type n, const value_type& val,
                 const allocator_type& alloc = allocator_type());

 第一种构造函数创建一个包含n个元素的vector(向量),每个元素默认初始化为类型的默认对象(对于整数为0,对于类对象调用默认构造函数)

第二种构造函数创建n个值为 val 的vector,允许你指定一个自定义的内存分配器,如果不提供分配器,则使用类型的默认分配器。

	vector<int> vec1(10);
	vector<int> vec2(5, 10);

	for (int val : vec1)
	{
		cout << val << " ";//输出10个0
	}
	cout << endl;
	for (int val : vec2)
	{
		cout << val << " ";//输出5个10
	}

需要注意的是第一种构造方式不支持隐式转换类型,第二种支持。

例如,你有一个函数,期望将vector::<int>作为参数。

void processVector(const vector<int>& vec) 
{  
    // 处理向量  
}

如果你试图像下面这样调用这个函数:

processVector(5); 这是不允许的

编译器会报错,因为你在这里传递的是一个整数 5,而不是一个std::vector<int> 对象。由于构造函数是 explicit 的,编译器不会自动将整数 5 转换为 std::vector<int> 。

如果你想要将一个整数传递给processVector函数,你需要显式构造一个std::vector对象:

processVector(std::vector<int>(5)); 这行是合法的,创建了一个包含 5 个默认值的 vector

如果你有以下调用:

processVector(5, 10); 这是合法的因为第二个构造函数不是 explicit的

在这种情况下,编译器会隐式调用 std::vector 的构造函数,创建一个包含 5 个元素且每个元素都初始化为 10 的 std::vector<int> 。

(3)范围构造函数

template <class InputIterator>
  vector (InputIterator first, InputIterator last,
          const allocator_type& alloc = allocator_type());

 这个构造函数的主要作用是通过指定的迭代器范围 [first, last) 来初始化 std::vector,它会复制该范围内的元素。这对于从另一个容器或元素集合中创建 std::vector 非常有用

	vector<int> vec1 = { 10,20,30,40 };
	std::list<int> myList = { 1,2,3,4 };

	//1.从另一个vector构造
	vector<int> vec2(vec1.begin(), vec1.end());
	//2.从部分范围构造
	vector<int> vec2(vec1.begin() + 1, vec1.begin() + 3);
	//3.从其他容器构造
	vector<int> vec2(myList.begin(), myList.end());

(4)拷贝构造函数

vector (const vector& x);
vector (const vector& x, const allocator_type& alloc);

这两个构造函数用于创建一个新的 vector,并初始化为另一个 vector 的内容。第一中构造函数使用默认的内存分配器管理内存,第二种允许使用指定的内存分配器进行内存管理

	vector<int> vec1 = { 1,2,3,4 };
	vector<int> copy1(vec1);

	allocator<int> alloc;
	vector<int> copy2(vec1, alloc);

(5)初始化列表构造函数

#include <vector>  
#include <iostream>  

int main() {  
    std::vector<int> vec = {1, 2, 3, 4, 5}; // 使用初始化列表构造 vector  

    for (int value : vec) {  
        std::cout << value << " "; // 输出: 1 2 3 4 5  
    }  

    return 0;  
}

2.3赋值运算符重载

#include <iostream>
#include <vector>

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

    // 使用赋值运算符将 vec1 的内容赋值给 vec2
    vec2 = vec1;

    // 输出 vec2 的元素
    for (int i = 0; i < vec2.size(); ++i) {
        std::cout << vec2[i] << " ";  // 输出: 1 2 3 4 5
    }

    return 0;
}

 vector迭代器与string类的迭代器用法相似,在这篇博客中已有详细介绍:C++深入学习string类成员函数(1):默认与迭代-优快云博客

在此不再赘述,包括容器管理函数,元素访问函数等,都与string类的使用类似,我们在此只简单介绍一下。

3.vector的容器管理函数

这些函数用于查询和调整 vector 的大小和容量。

(1) size()

返回当前 vector 中存储的元素个数。

	vector<int> vec = { 1,2,3,4 };
	cout << vec.size() << endl;//4

(2)capacity()

返回当前分配的存储空间的容量,也就是 vector 在不扩容的情况下可以存储的最大元素数量。

    std::vector<int> vec(5);
    std::cout << vec.capacity();  // 输出: 5

(3)max_size()

返回 vector 理论上可以容纳的最大元素数量(依赖于系统或实现的限制)。

    std::vector<int> vec;
    std::cout << vec.max_size();  // 输出: 通常是非常大的数字

(4)empty()

如果 vector 为空,返回 true(1);否则返回 false(0)。

	vector<int> vec = { 1,2,3,4 };
	cout << vec.empty() << endl;//0

(5)reserve(size_type n)

为 vector 分配至少能容纳 n 个元素的存储空间,但不改变 size。用来减少在频繁插入元素时的扩容开销。

    vector<int> vec;
    vec.reserve(10);  // 预留 10 个元素的空间

(6)shrink_to_fit()

请求将 vector 的容量减少到与其大小相匹配(非强制),释放不必要的内存。

	vector<int> vec = { 1,2,3,4 };
	vec.reserve(10);
	cout << vec.size() << endl;//4
	cout << vec.capacity() << endl;//10

	vec.shrink_to_fit();
	cout << vec.size() << endl;//4
	cout << vec.capacity() << endl;//4

4.元素访问函数(Element Access Functions)

这些函数用于访问和操作 vector 中的元素。

(1)operator[]:直接通过下标访问元素,不进行边界检查。
(2)at(size_type n):返回 vector 中第 n 个元素,并进行边界检查。如果越界则抛出。

(3)front():返回第一个元素的引用。
(4)back():返回最后一个元素的引用。

#include <iostream>
using namespace std;
#include <vector>
int main()
{
	vector<int> vec = { 1,2,3,4 };

	cout << vec.front() << endl;//1
	cout << vec[1] << endl;//2
	cout << vec.at(2) << endl;//3
	cout << vec.back() << endl;//4
	return 0;
}

(5)data():返回指向 vector 数据块的指针,适用于需要与 C 风格数组兼容的场景。

int main()
{
	vector<int> vec(5);

	int* p = vec.data();

	*p = 10;
	++p;
	*p = 20;
	p[2] = 100;

	for (int i = 0; i < vec.size(); ++i)
		cout << vec[i] << " ";//10 20 0 100 0
	return 0;
}

5.修饰函数(Modifier Functions)

这些函数用于修改 vector 的内容,如添加、删除元素等。

(1)push_back(const T& value):在 vector 末尾插入一个元素。

(2)pop_back():移除 vector 中最后一个元素。

(3)insert(iterator pos, const T& value):在 vector 中的指定位置插入一个元素,返回新元素的位置。

(4)erase(iterator pos):删除指定位置的元素,返回指向被删除元素之后的迭代器。

(5)clear():清空 vector 中的所有元素。

(6)resize(size_type count):调整 vector 的大小。如果新大小比当前小,删除多余的元素;如果比当前大,用默认值或指定值填充新元素。

(7)swap(vector& other):交换两个 vector 的内容,时间复杂度为常数。

#include <iostream>
#include <vector>

using namespace std;

int main() {
    vector<int> vec1;
    vector<int> vec2;

    // 使用 push_back 在 vec1 中添加元素
    vec1.push_back(10);
    vec1.push_back(20);
    vec1.push_back(30);
    cout << "After push_back: ";
    for (int n : vec1) {
        cout << n << " ";
    }
    cout << endl;

    // 使用 pop_back 移除最后一个元素
    vec1.pop_back();
    cout << "After pop_back: ";
    for (int n : vec1) {
        cout << n << " ";
    }
    cout << endl;

    // 使用 insert 在指定位置插入元素
    auto it = vec1.begin() + 1;
    vec1.insert(it, 25);
    cout << "After insert: ";
    for (int n : vec1) {
        cout << n << " ";
    }
    cout << endl;

    // 使用 erase 删除指定位置的元素
    vec1.erase(vec1.begin());
    cout << "After erase: ";
    for (int n : vec1) {
        cout << n << " ";
    }
    cout << endl;

    // 使用 clear 清空 vector
    vec1.clear();
    cout << "After clear, size of vec1: " << vec1.size() << endl;

    // 使用 resize 调整 vector 大小
    vec2.resize(5, 100);
    cout << "After resize: ";
    for (int n : vec2) {
        cout << n << " ";
    }
    cout << endl;

    // 使用 swap 交换 vec1 和 vec2
    vec1.swap(vec2);
    cout << "After swap, vec1: ";
    for (int n : vec1) {
        cout << n << " ";
    }
    cout << endl;

    cout << "After swap, vec2: ";
    for (int n : vec2) {
        cout << n << " ";
    }
    cout << endl;

    return 0;
}

6.vector的动态二维数组

我们以杨辉三角为例来分析vector的动态二维数组

class Solution {
public:
    vector<vector<int>> generate(int numRows) 
    {
        vector<vector<int>> vv(numRows, vector<int>());
        //动态调整二维数组的大小
        for (size_t i = 0; i < numRows; i++)
        {
            vv[i].resize(i + 1, 1);
        }
        //元素的修改与访问
        for(size_t i = 0; i < vv.size(); i++)
        {
            for (size_t j = 1; j < vv[i].size() - 1; j++)
            {
                vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
            }
        }
        //打印结果
        for (size_t i = 0; i < vv.size(); i++)
        {
            for (size_t j = 0; j < vv[i].size(); j++)
            {
                cout << vv[i][j] << " ";
            }
            cout << endl;
        }
        return vv;

    }
};
int main()
{
    Solution so;
    int n = 10;
    so.generate(n);
    return 0;
}

   vector<vector<int>> vv(numRows, vector<int>());

这段代码创建了一个numRows行,并且每行都是一个vector<int>类型元素的二维vector。

        //动态调整二维数组的大小
        for (size_t i = 0; i < numRows; i++)
        {
            vv[i].resize(i + 1, 1);
        }

这段代码为每一行的 vector<int>类型的元素 分配i+1个元素,并且每个元素都初始化为1

        //元素的修改与访问
        for(size_t i = 0; i < vv.size(); i++)
        {
            for (size_t j = 1; j < vv[i].size() - 1; j++)
            {
                vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
            }
        }

最后这段代码将将二维数组中的值修改为杨辉三角应是的值。 

118. 杨辉三角 - 力扣(LeetCode)

     总之, C++ 中 std::vector 具有强大功能和灵活性,尤其是在处理动态数组时的优势。从简单的单维 vector 操作到复杂的二维 vector 应用,我们看到了 vector 在内存管理、动态调整大小、以及高效访问和修改元素方面的便利性。掌握并灵活运用 vector 都能够大大提高我们的编程效率和代码可维护性。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值