📜  向量STL中的常见细微差别(1)

📅  最后修改于: 2023-12-03 14:50:41.698000             🧑  作者: Mango

向量STL中的常见细微差别

在C++ STL中,向量(vector)是一个广泛使用的容器,常用于存储大量数据并实现动态大小调整。虽然向量看起来很简单,但在实践中,有一些细微的差别可能会影响程序的性能、正确性和可读性。

本文将介绍向量STL中的常见细微差别,让开发者更好地了解和使用向量。

1. 容器大小

向量的大小可以使用size()函数获取,也可以使用capacity()函数获取其分配的存储空间大小。但需要注意的是,向量的大小和容量两者并非一样的概念。

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v;
    cout << "Initial size of vector: " << v.size() << endl;
    cout << "Initial capacity of vector: " << v.capacity() << endl;

    for (int i = 1; i <= 5; i++) {
        v.push_back(i);
        cout << "Size of vector after inserting " << i << " elements: " << v.size() << endl;
        cout << "Capacity of vector after inserting " << i << " elements: " << v.capacity() << endl;
    }

    return 0;
}

上述代码输出结果为:

Initial size of vector: 0
Initial capacity of vector: 0
Size of vector after inserting 1 elements: 1
Capacity of vector after inserting 1 elements: 1
Size of vector after inserting 2 elements: 2
Capacity of vector after inserting 2 elements: 2
Size of vector after inserting 3 elements: 3
Capacity of vector after inserting 3 elements: 4
Size of vector after inserting 4 elements: 4
Capacity of vector after inserting 4 elements: 4
Size of vector after inserting 5 elements: 5
Capacity of vector after inserting 5 elements: 8

可以看到,向量在不断插入元素后,其容器大小每次都会增加1,但容量并非每次都增加1,而是遵循一定的策略进行动态分配的。当容器不足以容纳所有元素时,向量会自动重新分配更大的内存空间(通常是当前容量的两倍)。

2. 向量的初始化

向量可以通过以下方式进行初始化:

2.1 直接初始化
vector<int> v1 = {1, 2, 3};
vector<string> v2 = {"hello", "world"};
2.2 构造函数初始化
vector<int> v3(5);
vector<char> v4(10, 'a');
2.3 复制初始化
vector<int> v5(v4);

需要注意的是,大括号初始化方法仅适用于C++11及以后的标准。复制初始化比直接初始化更耗时,因为它需要将一个向量复制到另一个向量中。

3. 向量的存储

向量是一个连续存储的序列,默认情况下,在向量的末尾添加或删除元素的时间复杂度为常数级别(O(1));但在其它位置添加或删除元素则需要进行移动操作,时间复杂度为线性级别(O(n))。

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v = {1, 2, 3, 4, 5};
    v.insert(v.begin()+2, 6);
    v.erase(v.begin()+3);

    for (auto i : v) {
        cout << i << " ";
    }
    cout << endl;

    return 0;
}

上述代码输出结果为:

1 2 6 4 5
4. 向量的迭代器

向量的迭代器是指向向量元素的指针。有以下几种迭代器类型:

4.1 begin()和end()

begin()函数返回指向第一个元素的迭代器,end()函数返回指向最后一个元素的下一个位置的迭代器。

vector<int> v = {1, 2, 3, 4, 5};
for (auto i = v.begin(); i != v.end(); i++) {
    cout << *i << " ";
}
cout << endl;

输出结果为:

1 2 3 4 5
4.2 rbegin()和rend()

rbegin()函数返回指向最后一个元素的迭代器,rend()函数返回指向第一个元素的前一个位置的迭代器。

vector<int> v = {1, 2, 3, 4, 5};
for (auto i = v.rbegin(); i != v.rend(); i++) {
    cout << *i << " ";
}
cout << endl;

输出结果为:

5 4 3 2 1
4.3 cbegin()和cend()

cbegin()函数与begin()函数功能相同,但是返回的是常量迭代器。cend()函数与end()函数功能相同,也返回常量迭代器。

4.4 crbegin()和crend()

crbegin()函数与rbegin()函数功能相同,但返回的是常量迭代器。crend()函数与rend()函数功能相同,也返回常量迭代器。

需要注意的是,const_iterator类型的迭代器只能进行读取操作,不能进行修改操作。

5. 向量的内存管理

向量的内存管理实际上由其分配器(allocator)实现。向量默认使用std::allocator作为分配器,它在C++标准库中已经预定义。在实际开发中,可以根据需要使用自定义的分配器。

#include <iostream>
#include <vector>
#include <memory>
using namespace std;

template<typename T>
class my_allocator : public std::allocator<T> {
public:
    T* allocate(size_t n) {
        cout << "Allocating " << n * sizeof(T) << " bytes" << endl;
        return std::allocator<T>::allocate(n);
    }

    void deallocate(T* p, size_t n) {
        cout << "Deallocating " << n * sizeof(T) << " bytes" << endl;
        std::allocator<T>::deallocate(p, n);
    }
};

int main() {
    vector<int, my_allocator<int>> v;
    v.push_back(1);
    v.push_back(2);

    return 0;
}

上述代码输出结果为:

Allocating 16 bytes
Allocating 32 bytes
Deallocating 16 bytes

可以看到,自定义分配器my_allocator中的allocate()和deallocate()函数被调用了两次,分别用于向量v的两次尝试插入元素。这表明自定义分配器已经被成功地使用。

总结

本文介绍了向量STL中的常见细微差别,包括容器大小、向量的初始化、向量的存储、向量的迭代器和向量的内存管理等方面。这些差别并非单独存在,它们相互关联、相互影响,共同构建了向量这一强大的工具。开发者可以根据自己的实际需求,选择最适合自己项目的使用方式。