前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >动态数组和C++ std::vector详解

动态数组和C++ std::vector详解

作者头像
艰默
发布2023-09-05 18:44:20
5690
发布2023-09-05 18:44:20
举报
文章被收录于专栏:iDoitnow

1. std::vector

std::vector是C++的默认动态数组,其与array最大的区别在于vector的数组是动态的,即其大小可以在运行时更改。std::vector是封装动态数组的顺序容器,且该容器中元素的存取是连续的。

vector的存储是自动管理,不需要人为操作自动实现按需扩张收缩。但实现自动管理的代价就是:vector通常占用多于静态数组的空间,因为其需要更多的内存以管理将来的增长。vector在分配内存的时候是先分配一定数量的内存,然后在内存耗尽时再重新申请分配。

2. vector的用法

2.1 vector的定义和声明

std::vector在头文件<vector>中定义,其声明如下:

代码语言:javascript
复制
template<
    class T,
    class Allocator = std::allocator<T>
> class vector;

namespace pmr {
    template< class T >
    using vector = std::vector<T, std::pmr::polymorphic_allocator<T>>; //C++17 起
}                                                                      

其中,参数T为容器要存储的元素类型,对于T需要满足:

  • 可复制赋值和可复制构造(C++11前)。
  • 要求元素类型是完整类型并满足可擦除,即元素类型的对象能以给定的分配器(Allocator)销毁(C++11 起,C++17 前)。
  • 要求元素类型是完整类型并满足可擦除,但许多成员函数附带了更严格的要求。(C++17 起)。

Allocator为用于获取/释放内存及构造/析构内存中元素的分配器。

2.2 成员函数

2.2.1 基本函数

operator=

operator=函数主要适用于赋值给容器,其函数声明如下:

代码语言:javascript
复制
/*1. 复制赋值运算符。以 other 的副本替换内容。*/
vector& operator=( const vector& other ); //C++20 前
constexpr vector& operator=( const vector& other ); //C++20 起

/*2. 移动赋值运算符。用移动语义以 other 的内容替换内容(即从 other 移动 other 中的数据到此容器中)。
     之后 other 在合法但未指定的状态。*/
vector& operator=( vector&& other ); //C++11 起, C++17 前
vector& operator=( vector&& other ) noexcept(); //C++17 起, C++20 前
constexpr vector& operator=( vector&& other ) noexcept(); //C++20 起

/*3. 以 initializer_list ilist 所标识者替换内容。*/
vector& operator=( std::initializer_list<T> ilist ); //C++11 起, C++20 前
constexpr vector& operator=( std::initializer_list<T> ilist ); //C++20 起

复杂度:

  • 1的复杂度与 *thisother 的大小成线性。
  • 2的复杂度与 *this 的大小成线性,除非分配器不比较相等且不传播,该情况下与 *thisother 的大小成线性。
  • 3的复杂度与 *thisilist 的大小成线性。

具体的示例如下:

代码语言:javascript
复制
std::vector<int> nums1 {3, 1, 4, 6, 5, 9};
std::vector<int> nums2;
std::vector<int> nums3;

// 从 nums1 复制赋值数据到 nums2
nums2 = nums1;
//此时nums2 = {3, 1, 4, 6, 5, 9}

// 从 nums1 移动赋值数据到 nums3,
// 修改 nums1 和 nums3
nums3 = std::move(nums1);
//此时 nums1 = {}, nums3 = {3, 1, 4, 6, 5, 9}


// initializer_list 的复制赋值复制数据给 nums3
nums3 = {1, 2, 3};
//此时nums3 = {1, 2, 3}
assign

assign函数的主要作用是将值赋给容器。其函数声明如下:

代码语言:javascript
复制
/*1. 以 count 份 value 的副本替换内容。*/
void assign( size_type count, const T& value ); //C++20 前
constexpr void assign( size_type count, const T& value ); //C++20 起

/*2. 以范围 [first, last) 中元素的副本替换内容。其中有任何一个迭代器是指向 *this 中的迭代器时行为未定义。*/
template< class InputIt >
void assign( InputIt first, InputIt last ); //C++20 前
template< class InputIt >
constexpr void assign( InputIt first, InputIt last ); //C++20 起

/*3. 以来自 initializer_list ilist 的元素替换内容。*/
void assign( std::initializer_list<T> ilist ); //C++11 起,C++20 前
constexpr void assign( std::initializer_list<T> ilist ); //C++20 起

复杂度:

  • 1的复杂度与count 呈线性。
  • 2的负载度与 firstlast 间的距离呈线性。
  • 3的复杂度与与 ilist.size() 呈线性。

其具体用法如下:

代码语言:javascript
复制
std::vector<char> c;

c.assign(5, 'a');//此时c = {'a', 'a', 'a', 'a', 'a'}

const std::string str(6, 'b');
c.assign(str.begin(), str.end());//此时c = {'b', 'b', 'b', 'b', 'b', 'b'}

c.assign({'C', '+', '+', '1', '1'});//此时c = {'C', '+', '+', '1', '1'}
get_allocator

get_allocator函数的主要作用是返回相关的分配器。其函数声明如下:

代码语言:javascript
复制
allocator_type get_allocator() const; //C++11 前
allocator_type get_allocator() const noexcept; //C++11 起, C++20 前
constexpr allocator_type get_allocator() const noexcept; //C++20 起

其返回值为与容器关联的分配器。

2.2.2 元素访问

at

at用于访问指定的元素,同时进行越界检查,该函数返回位于指定位置pos的元素的引用,如果pos不在容器的范围内,则抛出std::out_of_range异常。其函数声明如下:

代码语言:javascript
复制
reference at( size_type pos ); //C++20 前
constexpr reference at( size_type pos ); //C++20 起
const_reference at( size_type pos ) const; //C++20 前
constexpr const_reference at( size_type pos ) const; //C++20 起

其具体用法如下:

代码语言:javascript
复制
std::vector<int> data = {1, 2, 3};

std::cout<<data.at(1)<<std::endl; //2
data.at(1)=8; //此时data={1, 8, 3}
operator[]

operator[]与at功能相同,即用来访问指定的元素,但其与at不同的是:operator[]不进行边界的检查。其函数声明如下所示:

代码语言:javascript
复制
reference operator[]( size_type pos ); //C++20 前
constexpr reference operator[]( size_type pos ); //C++20 起
const_reference operator[]( size_type pos ) const; //C++20 前
constexpr const_reference operator[]( size_type pos ) const; //C++20 起
front

front用于访问容器的第一个元素,其返回值为容器首元素的引用,其函数原型如下:

代码语言:javascript
复制
reference front(); //C++20 前
constexpr reference front(); //C++20 起
const_reference front() const; //C++20 前
constexpr const_reference front() const; //C++20 起

:在空容器上对 front 的调用是未定义的。

back

back主要功能是用来访问容器最后一个元素,其返回值为容器最后一个元素的引用,其函数原型如下所示:

代码语言:javascript
复制
reference back(); //C++20 前
constexpr reference back(); //C++20 起
const_reference back() const; //C++20 前
constexpr const_reference back() const; //C++20 起

:在空容器上对 back 的调用是未定义的。

data

data函数主要是用来返回容器底层的数组,其函数原型如下:

代码语言:javascript
复制
T* data(); //C++11 前
T* data() noexcept; //C++11 起, C++20 前
constexpr T* data() noexcept; //C++20 起
const T* data() const; //C++11 前
const T* data() const noexcept; //C++11 起, C++20 前
constexpr const T* data() const noexcept; //C++20 起

data函数返回指向作为元素存储工作的底层数组的指针。返回的指针使得范围 [data(), data() + size()) 始终是合法范围,即使容器为空(此时 data() 不可解引用)。

:如果 size() 是 0,那么 data() 有可能会也有可能不会返回空指针。

2.2.3 迭代器

begin、end和cbegin、cend

begin和cbegin返回指向vector首元素的迭代器,end和cend返回指向vector末元素后一元素的迭代器。其函数声明如下:

代码语言:javascript
复制
iterator begin(); //C++11 前
iterator begin() noexcept; //C++11 起,C++20 前
constexpr iterator begin() noexcept; //C++20 起
const_iterator begin() const; //C++11 前
const_iterator begin() const noexcept; //C++11 起,C++20 前
constexpr const_iterator begin() const noexcept; //C++20 起
const_iterator cbegin() const noexcept; //C++11 起,C++20 前
constexpr const_iterator cbegin() const noexcept; //C++20 起


iterator end(); //C++11 前
iterator end() noexcept; //C++11 起,C++20 前
constexpr iterator end() noexcept; //C++20 起
const_iterator end() const; //C++11 前
const_iterator end() const noexcept; //C++11 起,C++20 前
constexpr const_iterator end() const noexcept; //C++20 起
const_iterator cend() const noexcept; //C++11 起,C++20 前
constexpr const_iterator cend() const noexcept; //C++20 起

如果vector为空,则返回的迭代器将等于end或cend。end和cend指向vector末元素后一元素的迭代器,该元素的表现为占位符,试图访问它将导致未定义行为。

rbegin、rend和crbegin、crend

rbegin和crbegin返回指向vector首元素的逆向迭代器。它对应非逆向vector的末元素,若vector为空,则返回的迭代器等于rend或crend。rend和crend返回指向逆向vector末元素后一元素的逆向迭代器,它对应非逆向vector首元素的前一元素,此元素表现为占位符,试图访问它导致未定义行为。它们的声明如下:

代码语言:javascript
复制
reverse_iterator rbegin(); //C++11 前
reverse_iterator rbegin() noexcept; //C++11 起,C++20 前
constexpr reverse_iterator rbegin() noexcept; //C++20 起
const_reverse_iterator rbegin() const; //C++11 前
const_reverse_iterator rbegin() const noexcept; //C++11 起,C++20 前
constexpr const_reverse_iterator rbegin() const noexcept; //C++20 起
const_reverse_iterator crbegin() const noexcept; //C++11 起,C++20 前
constexpr const_reverse_iterator crbegin() const noexcept; //C++20 起

reverse_iterator rend(); //C++11 前
reverse_iterator rend() noexcept; //C++11 起,C++20 前
constexpr reverse_iterator rend() noexcept; //C++20 起
const_reverse_iterator rend() const; //C++11 前
const_reverse_iterator rend() const noexcept; //C++11 起,C++20 前
constexpr const_reverse_iterator rend() const noexcept; //C++20 起
const_reverse_iterator crend() const noexcept; //C++11 起,C++20 前
constexpr const_reverse_iterator crend() const noexcept; //C++20 起

2.2.4 容量

empty

empty用来检查容器是否为空,若为空则返回true,否则为false。其函数声明如下:

代码语言:javascript
复制
bool empty() const; //C++11 前
bool empty() const noexcept; //C++11 起, C++20 前
[[nodiscard]] bool empty() const noexcept; //C++20 起

其底层实现就是检查容器是否无元素,即判断是否begin() == end()

size

size函数返回容器中元素数量,即std::distance(begin(), end()) 。其函数声明如下:

代码语言:javascript
复制
size_type size() const; //C++11 前
size_type size() const noexcept; //C++11 起,C++20 前
constexpr size_type size() const noexcept; //C++20 起
max_size

max_size函数返回根据系统或库实现限制的容器可保有的元素最大数量,即对于最大容器std::distance(begin(), end())。其函数声明为:

代码语言:javascript
复制
size_type max_size() const; //C++11 前
size_type max_size() const noexcept; //C++11 起

:此值通常反映容器大小上的理论极限,至多为 std::numeric_limits<difference_type>::max() 。运行时,可用 RAM 总量可能会限制容器大小到小于 max_size() 的值。

capacity

capacity函数的主要作用是返回当前存储空间能够容纳的元素数(即当前分配存储的容量)。其函数原型如下:

代码语言:javascript
复制
size_type capacity() const; //C++11 前
size_type capacity() const noexcept; //C++11 起, C++20 前
constexpr size_type capacity() const noexcept; //C++20 起
reserve

reserve函数是用来为vector预留存储空间,其函数声明如下:

代码语言:javascript
复制
//new_cap: vector 的新容量
void reserve( size_type new_cap ); //C++20 前
constexpr void reserve( size_type new_cap ); //C++20 起

该函数主要用来增加vector的容量(即 vector 在不重新分配存储的情况下能最多能持有的元素的数量)到大于或者等于new_cap的值。如果new_cap大于当前vector的capacity(),那么就会重新分配新的存储,否则该方法不做任何事情。

  • reserve() 不会更改 vector 的大小。
  • 如果 new_cap 大于 capacity(),那么所有迭代器,包含 end()迭代器和所有到元素的引用都会失效。否则,没有迭代器或引用会失效。
  • 在调用 reserve() 后,插入只会在它将导致 vector 的大小大于capacity()的值时触发重新分配。
  • new_cap > max_size() 时抛出std::length_error
  • 不能用 reserve() 减少容器容量。为该目的提供的是 shrink_to_fit()。(文章后面有详细的介绍)

正确的使用reserve能够避免减少不必要的分配,例如在向vector添加元素之前提前知道元素的大致数量,使用reserve,可以提前合理分配好存储空间,避免在vector增长阶段不必要的内存分配和复制,进而提高效率和存储空间的利用率。

shrink_to_fit

shrink_to_fit函数主要是用来请求移除未使用的容量。其函数原型如下:

代码语言:javascript
复制
void shrink_to_fit();

它是减少 capacity() size()非强制性请求。请求是否达成依赖于实现。如果发生重分配,那么所有迭代器,包含 end()迭代器,和所有到元素的引用都会失效。如果没有发生重分配,那么没有迭代器或引用会失效。

具体的示例如下:

代码语言:javascript
复制
std::vector<int> vec = {1, 2, 3};
vec.reserve(100);
std::cout << "vec的capacity : " << vec.capacity() << std::endl; //vec的capacity : 100
vec.shrink_to_fit();
std::cout << "vec的capacity : " << vec.capacity() << std::endl; //vec的capacity : 3

2.2.5 修改器

clear

clear函数主要用来擦除所有元素,使用clear()后,再次调用size(),size函数返回0。clear函数的声明如下:

代码语言:javascript
复制
void clear(); //C++11 前
void clear() noexcept; //C++11 起, C++20 前
constexpr void clear() noexcept; //C++20 起

:clear不会影响vector的capacity()的大小。

insert

insert函数主要用于插入元素到容器的指定位置,其函数原型如下所示:

代码语言:javascript
复制
//在 pos 前插入 value
//返回值:指向被插入 value 的迭代器。
iterator insert( const_iterator pos, const T& value ); //C++20 前
constexpr iterator insert( const_iterator pos, const T& value ); //C++20 起

//在 pos 前插入 value
//返回值:指向被插入 value 的迭代器。
iterator insert( const_iterator pos, T&& value ); //C++11 起,C++20 前
constexpr iterator insert( const_iterator pos, T&& value ); //C++20 起

//在 pos 前插入 value 的 count 个副本。
//返回值:指向首个被插入元素的迭代器,或者在 count == 0 时返回 pos。
iterator insert( const_iterator pos, size_type count, const T& value ); //C++20 前
constexpr iterator
    insert( const_iterator pos, size_type count, const T& value ); //C++20 起

//在 pos 前插入来自范围 [first, last) 的元素。
//返回值:指向首个被插入元素的迭代器,或者在 first == last 时返回 pos。
template< class InputIt >
iterator insert( const_iterator pos, InputIt first, InputIt last ); //C++20 前
template< class InputIt >
constexpr iterator insert( const_iterator pos, InputIt first, InputIt last ); //C++20 起

//在 pos 前插入来自 initializer_list ilist 的元素。
//返回值:指向首个被插入元素的迭代器,或者在 ilist 为空时返回 pos。
iterator insert( const_iterator pos, std::initializer_list<T> ilist ); //C++11 起,C++20 前
constexpr iterator insert( const_iterator pos,
                           std::initializer_list<T> ilist ); //C++20 起

具体用法示例如下:

代码语言:javascript
复制
std::vector<int> c1(3, 100); //初始化c1,此时c1 = {100, 100, 100}

auto it = c1.begin();
it = c1.insert(it, 200); //在it前插入元素200
//c1 = {200,100, 100, 100}

c1.insert(it, 2, 300); //在it前插入两个元素值都为300
//c1 = {300,300,200,100, 100, 100}

// it 已经失效,提供新迭代器
it = c1.begin();

std::vector<int> c2(2, 400); //c2 = {400, 400}
c1.insert(std::next(it, 2), c2.begin(), c2.end()); //在it后两个元素(即200)的前面插入c2
//c1 = {300,300,400,400,200,100, 100, 100}

int arr[] = {501, 502, 503};
c1.insert(c1.begin(), arr, arr + std::size(arr));
//c1 = {501,502,503,300,300,400,400,200,100, 100, 100}

c1.insert(c1.end(), {601, 602, 603});
//c1 = {501,502,503,300,300,400,400,200,100, 100, 100,601,602,603}
emplace

emplace函数的声明如下:

代码语言:javascript
复制
/*----------------------------------
  pos:将构造新元素到其前的迭代器
  args:转发给元素构造函数的参数
  返回值iterator:指向被安置的元素的迭代器
------------------------------------*/
template< class... Args >
iterator emplace( const_iterator pos, Args&&... args ); //C++11 起, C++20 前

template< class... Args >
constexpr iterator emplace( const_iterator pos, Args&&... args ); //C++20 起

其主要作用就是原位构造元素并将其在pos前插入到容器中。

earse

earse的函数主要功能是擦除元素,其声明如下:

代码语言:javascript
复制
//移除位于pos的元素
//返回值:最后移除元素之后的迭代器。如果pos指代末元素,则返回end()迭代器
iterator erase( iterator pos ); //C++11 前
iterator erase( const_iterator pos ); //C++11 起,C++20 前
constexpr iterator erase( const_iterator pos ); //C++20 起

//移除范围[first, last)中的元素。
/*返回值:最后移除元素之后的迭代器。
         如果在移除前last == end(),那么最终返回end()迭代器
         如果范围[first, last) 为空,那么返回 last。*/
iterator erase( iterator first, iterator last ); //C++11 前
iterator erase( const_iterator first, const_iterator last ); //C++11 起,C++20 前
constexpr iterator erase( const_iterator first, const_iterator last ); //C++20 起

具体的用法如下所示:

代码语言:javascript
复制
std::vector<int> c{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

c.erase(c.begin());
//c = {1, 2, 3, 4, 5, 6, 7, 8, 9}

c.erase(c.begin() + 2, c.begin() + 5);
//c = {1, 2, 6, 7, 8, 9}

// 移除所有偶数
for (std::vector<int>::iterator it = c.begin(); it != c.end();)
{
  if (*it % 2 == 0)
    it = c.erase(it);
  else
    ++it;
}
//c = {1, 7, 9}
push_back

push_back函数的主要作用是将元素添加到容器末尾,其声明如下:

代码语言:javascript
复制
//后附给定元素 value 到容器尾。初始化新元素为 value 的副本。
void push_back( const T& value ); //C++20 前
constexpr void push_back( const T& value ); //C++20 起

//后附给定元素 value 到容器尾。移动 value 进新元素。
void push_back( T&& value ); //C++11 起,C++20 前
constexpr void push_back( T&& value ); //C++20 起

:如果新的 size()大于 capacity(),那么所有迭代器和引用(包含 end() 迭代器)都会失效。否则只有 end() 迭代器会失效。

emplace_back

emplace_back函数与emplace类似,只不过是在容器末尾就地构造元素,其函数声明如下:

代码语言:javascript
复制
template< class... Args >
void emplace_back( Args&&... args ); //C++11 起, C++17 前

template< class... Args >
reference emplace_back( Args&&... args ); //C++17 起, C++20 前

template< class... Args >
constexpr reference emplace_back( Args&&... args ); //C++20 起

由于emplace_back是原地构造元素,因此其插入效率要高于push_back。

:如果新的 size()大于 capacity(),那么所有迭代器和引用(包含 end() 迭代器)都会失效。否则只有 end() 迭代器会失效。

pop_back

pop_back函数的主要作用就是移除末元素,其函数声明如下:

代码语言:javascript
复制
void pop_back(); //C++20 前
constexpr void pop_back(); //C++20 起

如果在空容器上调用pop_back会导致未定义行为。

:使指向末元素的迭代器和引用,以及end()迭代器失效。

resize

resize函数的主要作用是改变容器中可存储元素的个数,通过该函数可以重新设置容器大小,其函数声明如下:

代码语言:javascript
复制
/*
该函数重设容器的大小为count,在count==size()时不做任何操作。
如果当前大小大于 count,那么减小容器到它的开头 count 个元素。
如果当前大小小于 count,那么后附额外的默认插入的元素。
*/
void resize( size_type count ); //C++20 前
constexpr void resize( size_type count ); //C++20 起

/*
该函数重设容器的大小为count,在count==size()时不做任何操作。
如果当前大小大于 count,那么减小容器到它的开头 count 个元素。
如果当前大小小于 count,那么后附额外的 value 的副本
*/
void resize( size_type count, const value_type& value ); //C++20 前
constexpr void resize( size_type count, const value_type& value ); //C++20 起

其具体用法示例如下:

代码语言:javascript
复制
std::vector<int> c = {1, 2, 3};

c.resize(5); //将其size增加大小到5
//c = {1, 2, 3, 0, 0}

c.resize(2); //将其size减少大小到2
//c = {1, 2}

c.resize(6, 4); //将其size增加大小到6,填充值为4";
//c = {1, 2, 4, 4, 4,4}
swap

swap函数的主要作用是交换两个vector容器的内容,不在单独的元素上调用任何移动、复制或交换操作。所有迭代器和引用保持有效。end()迭代器会失效。其函数声明如下:

代码语言:javascript
复制
void swap( vector& other ); //C++17 前
void swap( vector& other ) noexcept(); //C++17 起, C++20 前
constexpr void swap( vector& other ) noexcept(); //C++20 起

其用法示例如下图所示:

代码语言:javascript
复制
std::vector<int> a1{1, 2, 3}, a2{4, 5};

auto it1 = std::next(a1.begin()); //*it1 = 2 
auto it2 = std::next(a2.begin()); //*it2 = 5 

int& ref1 = a1.front(); //ref1 = 1
int& ref2 = a2.front(); //ref1 = 4

std::cout <<*it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
//打印结果为2 5 1 4

a1.swap(a2);

//此时a1 = {4, 5},a2 = {1, 2, 3}
std::cout <<*it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
//打印结果仍为2 5 1 4

/*注:
    交换后迭代器与引用保持与原来的元素关联,
    例如尽管 'a1' 中值为 2 的元素被移动到 'a2' 中,
    原来指向它的 it1 仍指向同一元素。*/

2.2 非成员函数

operator==,!=,<,<=,>,>=,<=>(std::vector)

C++提供operator==,!=,<,<=,>,>=,<=>(std::vector)非成员函数用来比较两个vector的大小,相关函数及函数声明如下:

代码语言:javascript
复制
//1. ==
//返回值:在 vector 内容相等时返回 true,否则返回 false
template< class T, class Alloc >
bool operator==( const std::vector<T, Alloc>& lhs,
                 const std::vector<T, Alloc>& rhs ); //C++20 前
template< class T, class Alloc >
constexpr bool operator==( const std::vector<T, Alloc>& lhs,
                           const std::vector<T, Alloc>& rhs ); //C++20 起

//2. !=
//返回值:在 vector 内容不相等时返回 true,否则返回 false
template< class T, class Alloc >
bool operator!=( const std::vector<T, Alloc>& lhs,
                 const std::vector<T, Alloc>& rhs ); //C++20 前

//3. <
//返回值:在 lhs 的内容按字典序小于 rhs 的内容时返回 true,否则返回 false
template< class T, class Alloc >
bool operator<( const std::vector<T, Alloc>& lhs,
                const std::vector<T, Alloc>& rhs ); //C++20 前

//4. <=
//返回值:在 lhs 的内容按字典序小于或等于 rhs 的内容时返回 true,否则返回 false
template< class T, class Alloc >
bool operator<=( const std::vector<T, Alloc>& lhs,
                 const std::vector<T, Alloc>& rhs ); //C++20 前

//5. >
//返回值:在 lhs 的内容按字典序大于 rhs 的内容时返回 true,否则返回 false
template< class T, class Alloc >
bool operator>( const std::vector<T, Alloc>& lhs,
                const std::vector<T, Alloc>& rhs ); //C++20 前

//6. >=
//返回值:在 lhs 的内容按字典序大于或等于 rhs 的内容时返回 true,否则返回 false
template< class T, class Alloc >
bool operator>=( const std::vector<T, Alloc>& lhs,
                 const std::vector<T, Alloc>& rhs ); //C++20 前

//7. <=>
//返回值:lhs 与 rhs 中的首对不等价元素的相对顺序,如果有这种元素;否则是 lhs.size() <=> rhs.size()。
template< class T, class Alloc >
constexpr  operator<=>( const std::vector<T, Alloc>& lhs,
                                       const std::vector<T, Alloc>& rhs ); //C++20 起

1,2中会检查lhs和rhs的内容是相等,即他们是否拥有相同数量的元素且lhs中每个元素与rhs的相同位置元素比较相等。同时函数中T 必须符合可相等比较 (EqualityComparable) 的要求

3-6中按照字典比较lhs和rhs的内容,其内部等价于调用std::lexicographical_compare函数进行比较。同时函数中T 必须符合[可小于比较 (LessThanComparable) 的要求。

7中也是按字典序比较lhs和rhs的内容。其内部等价于调用std::lexicographical_compare_three_way 进行比较。返回类型同合成三路比较的结果类型。其逻辑大致如下:

代码语言:javascript
复制
lhs < rhs ? std::weak_ordering::less :
rhs < lhs ? std::weak_ordering::greater :
            std::weak_ordering::equivalent
//注:通常情况下less对应的是-1,greater对应1,equivalent对应0

lhs与rhs中的首对不等价元素的相对顺序,如果有这种元素;否则是 lhs.size() <=> rhs.size()

其具体的应用示例如下所示:

代码语言:javascript
复制
std::vector<int> alice{1, 2, 3};
std::vector<int> bob{7, 8, 9, 10};
std::vector<int> eve{1, 2, 3};

std::cout << std::boolalpha;

// 比较不相等的容器
std::cout << "alice == bob returns " << (alice == bob) << '\n';
std::cout << "alice != bob returns " << (alice != bob) << '\n';
std::cout << "alice <  bob returns " << (alice < bob) << '\n';
std::cout << "alice <= bob returns " << (alice <= bob) << '\n';
std::cout << "alice >  bob returns " << (alice > bob) << '\n';
std::cout << "alice >= bob returns " << (alice >= bob) << '\n';

std::cout << '\n';

// 比较相等的容器
std::cout << "alice == eve returns " << (alice == eve) << '\n';
std::cout << "alice != eve returns " << (alice != eve) << '\n';
std::cout << "alice <  eve returns " << (alice < eve) << '\n';
std::cout << "alice <= eve returns " << (alice <= eve) << '\n';
std::cout << "alice >  eve returns " << (alice > eve) << '\n';
std::cout << "alice >= eve returns " << (alice >= eve) << '\n';

输出结果为

代码语言:javascript
复制
alice == bob returns false
alice != bob returns true
alice <  bob returns true
alice <= bob returns true
alice >  bob returns false
alice >= bob returns false
 
alice == eve returns true
alice != eve returns false
alice <  eve returns false
alice <= eve returns true
alice >  eve returns false
alice >= eve returns true

std::swap(std::vector)

std::swap(std::vector)函数是为std::vector特化std::swap 算法。其函数声明如下:

代码语言:javascript
复制
template< class T, class Alloc >
void swap( std::vector<T, Alloc>& lhs,
           std::vector<T, Alloc>& rhs ); //C++11 起, C++17 前

template< class T, class Alloc >
void swap( std::vector<T, Alloc>& lhs,
           std::vector<T, Alloc>& rhs ) noexcept();//C++17 起, C++20 前

template< class T, class Alloc >
constexpr void swap( std::vector<T, Alloc>& lhs,
                     std::vector<T, Alloc>& rhs ) noexcept(); //C++20 起

交换 lhsrhs 的内容。调用lhs.swap(rhs)。其具体用法如下:

代码语言:javascript
复制
std::vector<int, 3> a1{1, 2, 3}, a2{4, 5, 6};

auto it1 = a1.begin(); //*it1 = 1
auto it2 = a2.begin(); //*it2 = 4

int &ref1 = a1[1]; // ref1 = 2
int &ref2 = a2[1]; // ref1 = 5

std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
// 打印结果为1 4 2 5

std::swap(a1, a2);

// 此时a1 = {4, 5, 6},a2 = {1, 2, 3}
std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
// 打印结果为4 1 5 2
std::erase, std::erase_if (std::vector)

std::erase, std::erase_if (std::vector)函数主要用来擦除所有满足特定判别标准的元素。其函数声明如下:

代码语言:javascript
复制
//从容器中擦除所有比较等于 value 的元素
template< class T, class Alloc, class U >
constexpr typename std::vector<T, Alloc>::size_type
    erase(std::vector<T, Alloc>& c, const U& value); //C++20 起

//从容器中擦除所有满足 pred 的元素
template< class T, class Alloc, class Pred >
constexpr typename std::vector<T, Alloc>::size_type
    erase_if(std::vector<T, Alloc>& c, Pred pred); //C++20 起

std::erase(std::vector)从容器中擦除所有比较等于 value 的元素,其返回值为被擦除的元素个数,其等价于

代码语言:javascript
复制
auto it = std::remove(c.begin(), c.end(), value);
auto r = std::distance(it, c.end());
c.erase(it, c.end());
return r;

std::erase_if (std::vector)从容器中擦除所有满足 pred 的元素,其返回值为被擦除的元素个数,其等价于

代码语言:javascript
复制
auto it = std::remove_if(c.begin(), c.end(), pred);
auto r = std::distance(it, c.end());
c.erase(it, c.end());
return r;

示例:

代码语言:javascript
复制
std::vector<int> c{1, 2, 3, 4, 6};
// 擦除c中的值等于3的元素
auto erased1 = std::erase(c, 3); // erased1 = 1
// 此时c = {1, 2, 4, 6}

// 擦除c中的偶数
auto erased2 = std::erase_if(c, [](int n)
                             { return n % 2 == 0; }); // erased2 = 3
// 此时c = {1}

3. 总结

vector容器的优势和劣势:

优势

  • 支持随机访问,访问无开销,时间恒定。
  • 线性遍历/搜索。
  • 在容量满足的情况下,末端插入元素效率高。

劣势

  • 如果元素类型具有较高的复制/分配成本,则插入元素速度比较慢。
  • 如果随之位置插入或擦除占程序的主导地位,程序会变慢。

vector容器在具体的应用中需要注意一下几点:

  1. 创建一个新vector
代码语言:javascript
复制
// 值列表初始化: C++11
vector<int> v {0, 1, 2, 3};   // v = {0, 1, 2, 3}

// 初始化一个长度为4,所有元素值都为2的vector
vector<int> w (4, 2)  // w = {2, 2, 2, 2}
  
// 深拷贝,以v初始化vector对象b
vector<int> b {v}; // b = {0, 1, 2, 3}
  1. vector的size和capacity
  • size()代表vector中元素的数量
  • capacity()代表当前vector中可以存储元素数量的最大值。 如果在向vector中添加元素之前提前知道元素(大致的)数量n,及时使用resrve(n),这样可以避免在元素插入阶段可能产生的不必要内存分配和复制。
  1. 插入元素和擦除元素的效率 在末尾插入元素的效率最快,但插入任意位置可能会很慢,因为中间可能涉及到元素的复制和移动。擦除元素同理。
  2. 使用shrink_to_fit()降低内存 从vector中擦除元素不会改变其容量,因此未存放的元素的位置对应内存不会被释放,如果后续不需要再使用这些空闲的内存,可以使用shrink_to_fit()对该内存进行释放,提高内存使用效率。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-07-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iDoitnow 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. std::vector
  • 2. vector的用法
    • 2.1 vector的定义和声明
      • 2.2 成员函数
        • 2.2.1 基本函数
        • 2.2.2 元素访问
        • 2.2.3 迭代器
        • 2.2.4 容量
        • 2.2.5 修改器
      • 2.2 非成员函数
        • operator==,!=,<,<=,>,>=,<=>(std::vector)
        • std::swap(std::vector)
    • 3. 总结
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档