cplusplus-stl-vector

时间:2023-04-06 18:29:28


文章目录

  • C++ | C++ STL vector容器详解
  • C++ STL vector容器详解
  • 创建`vector`容器的几种方式
  • 创建存储 `double` 类型元素的一个 `vector` 容器:
  • 在创建的同时指定初始值以及元素个数:
  • 在创建时指定元素个数:
  • 通过存储元素类型相同的其它 `vector` 容器,也可以创建新的 `vector` 容器:
  • `vector`容器包含的成员函数
  • 实例1(`push_back()`、`size()`、`begin()`、`end()`、`at()`、`insert()`)
  • C++ STL vector容器迭代器用法详解
  • vector 支持迭代器的成员函数:
  • vector容器迭代器的基本用法
  • 实例2(`begin()`和`end()`)
  • 实例3(`cbegin()`和`cend()`)
  • 实例4(反向迭代器`rbegin()`和`rend()`)
  • vector容器迭代器的独特之处
  • 实例5(空的 `vector` 容器,不能使用迭代器)
  • 实例6(`reserve()`)
  • 实例7(迭代器重新初始化)
  • C++ STL vector容器访问元素的几种方式
  • 访问`vector`容器中单个元素
  • 实例8(使用下标`[]`访问)
  • 实例9(使用`at()`函数访问)
  • 实例10(使用`front()` 和 `back()`函数)
  • 访问vector容器中多个元素
  • 实例11(使用size()函数)
  • 实例12(基于范围的循环)
  • 实例13(使用迭代器)
  • C++ `vector`容量(`capacity`)和大小(`size`)的区别
  • 实例13(capacity()和size()函数)
  • 实例14
  • 修改`vector`容器的容量和大小
  • 实例15(`resize()`函数)
  • `vector`容器容量和大小的数据类型
  • 深度剖析C++ vector容器的底层实现机制
  • `vector`扩大容量的本质
  • C++ STL vector添加元素(push_back()和emplace_back())详解
  • 实例16(`push_back()`函数):
  • 实例17(`emplace_back()`函数)
  • `emplace_back()`和`push_back()`的区别
  • 实例18
  • 实例19
  • C++ STL `vector`插入元素(`insert()`和`emplace()`)详解
  • `insert()`
  • 实例20(`insert()`)
  • `emplace()`
  • 实例21(`emplace()成员函数`)
  • 实例22(insert()和emplace()函数)
  • `C++ STL vector`删除元素的几种方式(超级详细)
  • 删除 `vector` 容器元素的几种方式:
  • 实例23(`pop_back()`函数)
  • 实例24(`erase()`函数)
  • 实例25(`swap()`和`pop_back()`函数)
  • 实例26(`erase(begin, end)`函数)
  • 实例27(`remove()`函数)
  • 实例28(remove()函数)
  • 实例29(`clear()`函数)
  • 如何避免`vector`容器进行不必要的扩容?
  • `vector`模板类中功能类似的成员方法:
  • 实例30
  • 实例31
  • `vector swap()`成员方法还可以这样用!
  • 实例32

C++ | C++ STL vector容器详解

C++ STL vector容器详解

vector 容器是 STL 中最常用的容器之一,它和 array 容器非常类似,都可以看做是对 C++ 普通数组的“升级版”。不同之处在于,array 实现的是静态数组(容量固定的数组),而 vector 实现的是一个动态数组,即可以进行元素的插入和删除,在此过程中,vector 会动态调整所占用的内存空间,整个过程无需人工干预。

vector 常被称为向量容器,因为该容器擅长在尾部插入或删除元素,在常量时间内就可以完成,时间复杂度为O(1);而对于在容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间),时间复杂度为线性阶O(n)

Standard library header <vector>

创建vector容器的几种方式

创建存储 double 类型元素的一个 vector 容器:

std::vector<double> values;

注意,这是一个空的 vector 容器,因为容器中没有元素,所以没有为其分配空间。
当添加第一个元素(比如使用 push_back() 函数)时,vector 会自动分配内存。

在创建好空容器的基础上,还可以像下面这样通过调用 reserve() 成员函数来增加容器的容量:

values.reserve(20);

这样就设置了容器的内存分配,即至少可以容纳 20 个元素。
注意,如果 vector 的容量在执行此语句之前,已经大于或等于 20 个元素,那么这条语句什么也不做;
另外,调用 reserve() 不会影响已存储的元素,也不会生成任何元素,即 values 容器内此时仍然没有任何元素。

注意:如果调用 reserve() 来增加容器容量,之前创建好的任何迭代器(例如开始迭代器和结束迭代器)都可能会失效,这是因为,为了增加容器的容量,vector<T> 容器的元素可能已经被复制或移到了新的内存地址。所以后续再使用这些迭代器时,最好重新生成一下。

在创建的同时指定初始值以及元素个数:

std::vector<int> values = {1, 2, 4, 88, 99};//含有5个元素的vector容器

在创建时指定元素个数:

std::vector<int> values(20);//含有20个元素,默认初始值为0

注意:圆括号 () 和大括号 {} 是有区别的,前者(例如 (20) )表示元素的个数,而后者(例如 {20} ) 则表示 vector 容器中只有一个元素 20。

如果不想用 0 作为默认值,也可以指定一个其它值,例如:

std::vector<int> values(20, 1);//第二个参数指定所有元素的初始值为1

int num=20;
double value =1.0;
std::vector<double> values(num, value);//两个参数可以是变量

通过存储元素类型相同的其它 vector 容器,也可以创建新的 vector 容器:

std::vector<char>value1(5, 'c');
std::vector<char>value2(value1);

由此,value2 容器中也具有 5 个字符 'c'

在此基础上,如果不想复制其它容器中所有的元素,可以用一对指针或者迭代器来指定初始值的范围,例如:

int array[]={1,2,3};
std::vector<int>values(array, array+2);//values 将保存{1,2}
std::vector<int>value1{1,2,3,4,5};
std::vector<int>value2(std::begin(value1),std::begin(value1)+3);//value2保存{1,2,3}

vector容器包含的成员函数

函数成员

函数功能

begin()

返回指向容器中第一个元素的迭代器。

end()

返回指向容器最后一个元素所在位置后一个位置的迭代器,通常和 begin() 结合使用。

rbegin()

返回指向最后一个元素的迭代器。

rend()

返回指向第一个元素所在位置前一个位置的迭代器。

cbegin()

begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。

cend()

end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。

crbegin()

rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。

crend()

rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。

size()

返回实际元素个数。

max_size()

返回元素个数的最大值。这通常是一个很大的值,一般是 232-1,所以我们很少会用到这个函数。

resize()

改变实际元素的个数。

capacity()

返回当前容量。

empty()

判断容器中是否有元素,若无元素,则返回 true;反之,返回 false

reserve()

增加容器的容量。

shrink _to_fit()

将内存减少到等于当前元素实际所使用的大小。

operator[ ]

重载了 [ ] 运算符,可以向访问数组中元素那样,通过下标即可访问甚至修改 vector 容器中的元素。

at()

使用经过边界检查的索引访问元素。

front()

返回第一个元素的引用。

back()

返回最后一个元素的引用。

data()

返回指向容器中第一个元素的指针。

assign()

用新元素替换原有内容。

push_back()

在序列的尾部添加一个元素。

pop_back()

移出序列尾部的元素。

insert()

在指定的位置插入一个或多个元素。

erase()

移出一个元素或一段元素。

clear()

移出所有的元素,容器大小变为 0。

swap()

交换两个容器的所有元素。

emplace()

在指定的位置直接生成一个元素。

emplace_back()

在序列尾部生成一个元素。

C++ 11 标准库还新增加了 begin()end() 这 2 个函数,和 vector 容器包含的 begin()end() 成员函数不同,标准库提供的这 2 个函数的操作对象,既可以是容器,还可以是普通数组。当操作对象是容器时,它和容器包含的 begin()end() 成员函数的功能完全相同;如果操作对象是普通数组,则 begin() 函数返回的是指向数组第一个元素的指针,同样 end() 返回指向数组中最后一个元素之后一个位置的指针(注意不是最后一个元素)。

vector 容器还有一个 std::swap(x , y) 非成员函数(其中 xy 是存储相同类型元素的 vector 容器),它和 swap() 成员函数的功能完全相同,仅使用语法上有差异。

实例1(push_back()size()begin()end()at()insert())

/*******************************************************************
 *   > File Name: stl-vector.cpp
 *   > Create Time: 2021年10月10日 12:44:11
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<char> value; // 初始化一个空的vector容器
    value.push_back('S');// 添加元素'S'
    value.push_back('T');// 添加元素'T'
    value.push_back('L');// 添加元素'L'

    printf("元素个数:%ld\n", value.size());// 元素的实际个数

    for(auto i = value.begin(); i < value.end(); i++){
        cout << *i << " " ;// 输出元素
    }
    cout << endl;// 换行

    value.insert(value.begin(), 'C');// 在首位插入元素
    cout << "首个元素:" << value.at(0) << endl;// 使用at()访问元素

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day15> .\stl-vector.exe
元素个数:3
S T L
首个元素:C

C++ STL vector容器迭代器用法详解

vector 支持迭代器的成员函数:

成员函数

功能

begin()

返回指向容器中第一个元素正向迭代器;如果是 const 类型容器,在该函数返回的是常量正向迭代器

end()

返回指向容器最后一个元素之后一个位置的正向迭代器;如果是 const 类型容器,在该函数返回的是常量正向迭代器。此函数通常和 begin() 搭配使用。

rbegin()

返回指向最后一个元素的反向迭代器;如果是 const 类型容器,在该函数返回的是常量反向迭代器。

rend()

返回指向第一个元素之前一个位置的反向迭代器。如果是 const 类型容器,在该函数返回的是常量反向迭代器。此函数通常和 rbegin() 搭配使用。

cbegin()

begin() 功能类似,只不过其返回的迭代器类型为常量正向迭代器,不能用于修改元素。

cend()

end() 功能相同,只不过其返回的迭代器类型为常量正向迭代器,不能用于修改元素。

crbegin()

rbegin() 功能相同,只不过其返回的迭代器类型为常量反向迭代器,不能用于修改元素。

crend()

rend() 功能相同,只不过其返回的迭代器类型为常量反向迭代器,不能用于修改元素。

C++ 11 新添加的 begin()end() 全局函数也同样适用于 vector 容器。即当操作对象为 vector 容器时,其功能分别和上表 中的 begin()end() 成员函数相同。

cplusplus-stl-vector

以上函数在实际使用时,其返回值类型都可以使用 auto 关键字代替,编译器可以自行判断出该迭代器的类型。

vector容器迭代器的基本用法

实例2(begin()end()

/*******************************************************************
 *   > File Name: stl-vector-begin-end.cpp
 *   > Create Time: 2021年11月10日 20:08:03
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int>values{1, 2, 3, 4, 5};
    auto first = values.begin();
    auto end = values.end();

    while(first != end){
        cout << *first << " ";
        ++ first;
    }

    return 0;
}

编译、运行:

PS E:\fly-prj\cplusplus\day33> make 
g++ -o stl-vector-begin-end stl-vector-begin-end.cpp -g -Wall -std=c++11   
PS E:\fly-prj\cplusplus\day33> .\stl-vector-begin-end.exe
1 2 3 4 5

与此同时,还可以使用全局begin()end() 函数来从容器中获取迭代器

auto first = std::begin(values);
auto end = std::end(values);

cbegin()/cend() 成员函数和 begin()/end() 唯一不同的是,前者返回的是 const 类型的正向迭代器,这就意味着,由 cbegin()cend() 成员函数返回的迭代器,可以用来遍历容器内的元素,也可以访问元素,但是不能对所存储的元素进行修改。

实例3(cbegin()cend())

//*******************************************************************
 *   > File Name: stl-vector-cbegin-cend.cpp
 *   > Create Time: 2021年11月10日 20:15:24
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int>values{1 ,2 ,3 , 4 ,5};
    auto first = values.cbegin();
    auto end = values.cend();

    while(first != end){
        cout << *first << " ";//不可修改*first
        ++ first;
    }
    cout << endl;

    return 0;
}

编译、运行:

PS E:\fly-prj\cplusplus\day33> make
g++ -o stl-vector-cbegin-cend stl-vector-cbegin-cend.cpp -g -Wall -std=c++11
PS E:\fly-prj\cplusplus\day33> .\stl-vector-cbegin-cend.exe
1 2 3 4 5

vector 模板类中还提供了 rbegin()rend() 成员函数,分别表示指向最后一个元素和第一个元素前一个位置的随机访问迭代器,又称它们为反向迭代器

实例4(反向迭代器rbegin()rend()

/******************************************************************* *   > File Name: stl-vector3.cpp *   > Create Time: 2021年10月10日 18:54:46 ******************************************************************/#include <iostream>#include <vector>using namespace std;int main(int argc, char* argv[]){    vector<int> values{1, 2, 3, 4, 5};    auto first = values.rbegin();    auto end = values.rend();    while(first != end){        cout << *first << ' ';        ++first;    }    cout << endl;    return 0;}

编译、运行:

PS D:\study\cplusplus\day15> make stl-vector3g++ -o stl-vector3 stl-vector3.cpp -g -Wall -std=c++11PS D:\study\cplusplus\day15> .\stl-vector3.exe5 4 3 2 1

vector容器迭代器的独特之处

array 容器不同,vector 容器可以随着存储元素的增加,自行申请更多的存储空间。
因此,在创建 vector 对象时,我们可以直接创建一个空的 vector 容器,并不会影响后续使用该容器。

但这会产生一个问题,即在初始化空的 vector 容器时,不能使用迭代器。
也就是说,如下初始化 vector 容器的方法是不行的:

实例5(空的 vector 容器,不能使用迭代器)

/*******************************************************************
 *   > File Name: stl-vector4.cpp
 *   > Create Time: 2021年10月10日 20:04:57
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int> values;
    int val = 1;
    for(auto first = values.begin(); first < values.end(); ++first, val ++){
        *first = val;// 无效
        cout << *first;// 无任何输出
    }

    return 0;
}

编译、运行(无任何输出):


对于空的 vector 容器来说,begin()end() 成员函数返回的迭代器是相等的,即它们指向的是同一个位置。


所以,对于空的 vector 容器来说,可以通过调用 push_back() 或者借助 resize() 成员函数实现初始化容器的目的。

vector 容器在申请更多内存的同时,容器中的所有元素可能会被复制或移动到新的内存地址,这会导致之前创建的迭代器失效。

实例6(reserve()

/*******************************************************************
 *   > File Name: stl-vector.cpp
 *   > Create Time: 2021年10月12日  0:43:34
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int> values{1, 2, 3};
    cout << "The addr of values[0]: " << values.data() << endl;
    auto first = values.begin();
    auto end = values.end();
    values.reserve(20);// 增加values的容量为20
    cout << "The addr of values[0]: " << values.data() << endl;
    //first = values.begin();
    //end = values.end();
    while(first != end){
        cout << *first;
        first ++;
    }
    cout << endl;

    return 0;
}

编译、运行(运行结果可能出错):

PS D:\study\cplusplus\day16> ./stl-vector.exe
The addr of values[0]: 0x800062170
The addr of values[0]: 0x800084640
-21441277361-2144127736

实例7(迭代器重新初始化)

values 容器在增加容量之后,首个元素的存储地址发生了改变,此时再使用先前创建的迭代器,显然是错误的。因此,为了保险起见,每当 vector 容器的容量发生变化时,我们都要对之前创建的迭代器重新初始化一遍:

/*******************************************************************
 *   > File Name: stl-vector.cpp
 *   > Create Time: 2021年10月12日  0:43:34
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int> values{1, 2, 3};
    cout << "The addr of values[0]: " << values.data() << endl;
    auto first = values.begin();
    auto end = values.end();
    values.reserve(20);// 增加values的容量为20
    cout << "The addr of values[0]: " << values.data() << endl;
    /*容量发生变化,重新申请了内存,需要重新初始化迭代器*/
    first = values.begin();
    end = values.end();
    while(first != end){
        cout << *first;//输出元素
        first ++;
    }
    cout << endl;

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day16> make stl-vector
g++ -o stl-vector stl-vector.cpp -g -Wall -std=c++11
PS D:\study\cplusplus\day16> ./stl-vector.exe
The addr of values[0]: 0x800062170
The addr of values[0]: 0x800084640
123

C++ STL vector容器访问元素的几种方式

访问vector容器中单个元素

像普通数组那样访问存储的元素,对指定下标处的元素进行修改:

实例8(使用下标[]访问)

/*******************************************************************
 *   > File Name: stl-vector1.cpp
 *   > Create Time: 2021年10月12日  1:06:05
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int> values{1, 2, 3, 4, 5};
    cout << values[0] << endl;
    values[0] = values[1] + values[2] + values[3] + values[4];
    cout << values[0] << endl;

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day16> make stl-vector1g++ -o stl-vector1 stl-vector1.cpp -g -Wall -std=c++11PS D:\study\cplusplus\day16> ./stl-vector1.exe114

注意容器名[n]这种获取元素的方式,需要确保下标 n 的值不会超过容器的容量(可以通过 capacity() 成员函数获取),否则会发生越界访问的错误。vector 容器提供了 at() 成员函数,当传给 at() 的索引会造成越界时,会抛出std::out_of_range异常。

实例9(使用at()函数访问)

/*******************************************************************
 *   > File Name: stl-vector-at.cpp
 *   > Create Time: 2021年10月14日 23:14:53
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int> values{1,2,3,4,5};

    cout << values.at(0) << endl;
    values.at(0) =  values.at(1) + values.at(2) + values.at(3) + values.at(4);
    cout << values.at(0) << endl;
    cout << values.at(6) << endl; /*抛出异常*/
    cout << "END.\n";

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day16> make stl-vector-at
g++ -o stl-vector-at stl-vector-at.cpp -g -Wall -std=c++11
PS D:\study\cplusplus\day16> .\stl-vector-at.exe
1
14
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 6) >= this->size() (which is 5)
      1 [main] stl-vector-at 164 cygwin_exception::open_stackdumpfile: Dumping stack trace to stl-vector-at.exe.stackdump

front()back()函数,分别返回 vector 容器中第一个和最后一个元素的引用,通过引用可以访问(甚至修改)容器中的首尾元素

实例10(使用front()back()函数)

/*******************************************************************
 *   > File Name: stl-vector-front.cpp
 *   > Create Time: 2021年10月14日 23:26:14
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int> values{1,2,3,4,5};
    cout << "values首元素:" << values.front() << endl;
    cout << "values尾元素:" << values.back() << endl;

    values.front() = 10;
    values.back() = 100;
    cout << "values新首元素:" << values.front() << endl;
    cout << "values新尾元素:" << values.back() << endl;

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day16> make stl-vector-frontg++ -o stl-vector-front stl-vector-front.cpp -g -Wall -std=c++11PS D:\study\cplusplus\day16> .\stl-vector-front.exevalues首元素:1values尾元素:5values新首元素:10values新尾元素:100

访问vector容器中多个元素

访问 vector 容器中多个元素,可以借助 size() 成员函数,该函数可以返回 vector 容器中实际存储的元素个数。

实例11(使用size()函数)

/******************************************************************* *   > File Name: stl-vector-size.cpp *   > Create Time: 2021年10月16日 11:41:07 ******************************************************************/#include <iostream>#include <vector>using namespace std;int main(int argc, char* argv[]){    vector<int> values{1,2,3,4,5};    for(long unsigned int i=0; i < values.size(); i++){        cout << values[i] << " ";    }    cout << endl;    return 0;}

编译、运行:

PS D:\study\cplusplus\day16> make stl-vector-sizeg++ -o stl-vector-size stl-vector-size.cpp -g -Wall -std=c++11PS D:\study\cplusplus\day16> ./stl-vector-size.exe1 2 3 4 5

注意:这里不要使用 capacity() 成员函数,因为它返回的是 vector 容器的容量,而不是实际存储元素的个数,这两者是有差别的

实例12(基于范围的循环)

/*******************************************************************
 *   > File Name: stl-vector-auto.cpp
 *   > Create Time: 2021年10月16日 12:18:52
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int> values{1,2,3,4,5};
    /*基于范围的循环*/
    for(auto&& value: values){
        cout << value << ' ';
    }
    cout << endl;

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day16> make stl-vector-auto
g++ -o stl-vector-auto stl-vector-auto.cpp -g -Wall -std=c++11
PS D:\study\cplusplus\day16> ./stl-vector-auto.exe
1 2 3 4 5

实例13(使用迭代器)

使用 vector 迭代器遍历 vector 容器。

/*******************************************************************
 *   > File Name: stl-vector-iterator.cpp
 *   > Create Time: 2021年10月16日 12:27:16
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int> values{1,2,3,4,5};
    for(auto first = values.begin(); first < values.end(); ++first){
        cout << *first << " ";
    }
    cout << endl;

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day16> make stl-vector-iterator
g++ -o stl-vector-iterator stl-vector-iterator.cpp -g -Wall -std=c++11
PS D:\study\cplusplus\day16> ./stl-vector-iterator.exe
1 2 3 4 5

C++ vector容量(capacity)和大小(size)的区别

vector 容器的容量(用 capacity 表示),指的是在不分配更多内存的情况下,容器可以保存的最多元素个数;而 vector 容器的大小(用 size 表示),指的是它实际所包含的元素个数。

实例13(capacity()和size()函数)

/*******************************************************************
 *   > File Name: stl-vector-reserve.cpp
 *   > Create Time: 2021年10月16日 17:48:17
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    std::vector<int>values{2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
    values.reserve(20);
    cout << "values容量:" << values.capacity() << endl;
    cout << "values大小:" << values.size() << endl;

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day16> make stl-vector-reserve
g++ -o stl-vector-reserve stl-vector-reserve.cpp -g -Wall -std=c++11
PS D:\study\cplusplus\day16> ./stl-vector-reserve.exe
values容量:20
values大小:15

cplusplus-stl-vector

显然,vector 容器的大小不能超出它的容量,在大小等于容量的基础上,只要增加一个元素,就必须分配更多的内存。注意:这里的“更多”并不是 1 个。换句话说,当 vector 容器的大小和容量相等时,如果再向其添加(或者插入)一个元素,vector 往往会申请多个存储空间,而不仅仅只申请 1 个。

一旦 vector 容器的内存被重新分配,则和 vector 容器中元素相关的所有引用、指针以及迭代器,都可能会失效,最稳妥的方法就是重新生成。

实例14

/*******************************************************************
 *   > File Name: stl-vector-capacity.cpp
 *   > Create Time: 2021年10月16日 21:59:15
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int>values{2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
    cout << "values容量:" << values.capacity() << endl;
    cout << "values大小:" << values.size() << endl;
    printf("values地址:0X%lX\n", values.data());
    values.push_back(53);
    cout << "values容量1:" << values.capacity() << endl;
    cout << "values大小1:" << values.size() << endl;
    printf("values地址1:0X%lX\n", values.data());
    return 0;
}

编译、运行:

PS D:\study\cplusplus\day16> ./stl-vector-capacity.exe
values容量:15
values大小:15
values地址:0X800062170
values容量1:30
values大小1:16
values地址1:0X8000846A0

可以看到,向“已满”的 vector 容器再添加一个元素,整个 value 容器的存储位置发生了改变,同时 vector 会一次性申请多个存储空间(具体多少,取决于底层算法的实现)。这样做的好处是,可以很大程度上减少 vector 申请空间的次数,当后续再添加元素时,就可以节省申请空间耗费的时间。

因此,对于 vector 容器而言,当增加新的元素时,有可能很快完成(即直接存在预留空间中);也有可能会慢一些(扩容之后再放新元素)

修改vector容器的容量和大小

实例15(resize()函数)

/*******************************************************************
 *   > File Name: stl-vector-resize.cpp
 *   > Create Time: 2021年10月16日 22:19:19
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int>values{ 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47 };
    cout << "values容量:" << values.capacity() << endl;
    cout << "values大小:" << values.size() << endl;
    for(auto&& value: values){
        cout << value << " " ;
    }
    cout << endl;

    values.reserve(20);
    cout << "values容量1:" << values.capacity() << endl;
    cout << "values大小1:" << values.size() << endl;
    for(auto&& value: values){
        cout << value << " " ;
    }
    cout << endl;

    /*将元素个数改变为 21 个,所以会增加 6 个默认初始化的元素*/
    values.resize(21);
    cout << "values容量2:" << values.capacity() << endl;
    cout << "values大小2:" << values.size() << endl;
    for(auto&& value: values){
        cout << value << " " ;
    }
    cout << endl;

    /*将元素个数改变为 25 个,所以会增加 4 个默认初始化的元素*/
    values.resize(25,99);
    cout << "values容量3:" << values.capacity() << endl;
    cout << "values大小3:" << values.size() << endl;
    for(auto&& value: values){
        cout << value << " " ;
    }
    cout << endl;

    /*减少容器的大小*/
    values.resize(10);
    cout << "values容量4:" << values.capacity() << endl;
    cout << "values大小4:" << values.size() << endl;
    for(auto&& value: values){
        cout << value << " " ;
    }
    cout << endl;

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day16> make stl-vector-resize
g++ -o stl-vector-resize stl-vector-resize.cpp -g -Wall -std=c++11
PS D:\study\cplusplus\day16> .\stl-vector-resize.exe
values容量:15
values大小:15
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
values容量1:20
values大小1:15
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
values容量2:30
values大小2:21
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 0 0 0 0 0 0
values容量3:30
values大小3:25
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 0 0 0 0 0 0 99 99 99 99
values容量4:30
values大小4:10
2 3 5 7 11 13 17 19 23 29

可以看到,仅通过 reserve() 成员函数增加 value 容器的容量,其大小并没有改变;但通过 resize() 成员函数改变 value 容器的大小,它的容量可能会发生改变。另外需要注意的是,通过 resize() 成员函数减少容器的大小(多余的元素会直接被删除),不会影响容器的容量。

vector容器容量和大小的数据类型

在实际场景中,我们可能需要将容器的容量大小保存在变量中,要知道 vector<T> 对象的容量和大小类型都是 vector<T>::size_type 类型。因此,当定义一个变量去保存这些值时,可以如下所示:

vector<int>::size_type cap = value.capacity();vector<int>::size_type size = value.size();

size_type 类型是定义在由 vector 类模板生成的 vecotr 类中的,它表示的真实类型和操作系统有关,在 32 位架构下普遍表示的是 unsigned int 类型,而在 64 位架构下普通表示 unsigned long 类型。

可以使用 auto 关键字代替 vector<int>::size_type

auto cap = value.capacity();auto size = value.size();

深度剖析C++ vector容器的底层实现机制

STL 众多容器中,vector 是最常用的容器之一,其底层所采用的数据结构非常简单,就只是一段连续的线性内存空间。

分析 vector 容器的源代码可以发现,它就是使用 3 个迭代器(可以理解成指针)来表示的:

//_Alloc 表示内存分配器,此参数几乎不需要我们关心template <class _Ty, class _Alloc = allocator<_Ty>>class vector{    ...protected:    pointer _Myfirst;    pointer _Mylast;    pointer _Myend;};

其中,_Myfirst 指向的是 vector 容器对象的起始字节位置
_Mylast 指向当前最后一个元素的末尾字节
_Myend 指向整个 vector 容器所占用内存空间的末尾字节

cplusplus-stl-vector

在此基础上,将 3 个迭代器两两结合,还可以表达不同的含义,例如:

  • _Myfirst_Mylast 可以用来表示 vector 容器中目前已被使用的内存空间;
  • _Mylast_Myend 可以用来表示 vector 容器目前空闲的内存空间;
  • _Myfirst_Myend 可以用表示 vector 容器的容量。

对于空的 vector 容器,由于没有任何元素的空间分配,因此 _Myfirst_Mylast_Myend 均为 null

通过灵活运用这 3 个迭代器,vector 容器可以轻松的实现诸如首尾标识大小容器空容器判断等几乎所有的功能,比如:

template <class _Ty, class _Alloc = allocator<_Ty>>
class vector{
public:
    iterator begin() {return _Myfirst;}
    iterator end() {return _Mylast;}
    size_type size() const {return size_type(end() - begin());}
    size_type capacity() const {return size_type(_Myend - begin());}
    bool empty() const {return begin() == end();}
    reference operator[] (size_type n) {return *(begin() + n);}
    reference front() { return *begin();}
    reference back() {return *(end()-1);}
    ...
};

vector扩大容量的本质

另外需要指明的是,当 vector 的大小和容量相等(size==capacity)也就是满载时,如果再向其添加元素,那么 vector 就需要扩容vector 容器扩容的过程需要经历以下 3 步:

  1. 完全弃用现有的内存空间,重新申请更大的内存空间;
  2. 将旧内存空间中的数据,按原有顺序移动到新的内存空间中;
  3. 最后将旧的内存空间释放。

这也就解释了,为什么 vector 容器在进行扩容后,与其相关的指针引用以及迭代器可能会失效的原因。

由此可见,vector 扩容是非常耗时的。为了降低再次分配内存空间时的成本,每次扩容时 vector 都会申请比用户需求量更多的内存空间(这也就是 vector 容量的由来,即 capacity>=size),以便后期使用。

vector 容器扩容时,不同的编译器申请更多内存空间的量是不同的。以 VS 为例,它会扩容现有容器容量的 50%

C++ STL vector添加元素(push_back()和emplace_back())详解

实例16(push_back()函数):

/*******************************************************************
 *   > File Name: stl-vector-push_back.cpp
 *   > Create Time: 2021年10月16日 23:49:05
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int>values{};
    values.push_back(10);//在尾部添加元素
    values.push_back(100);//在尾部添加元素

    for(int i = 0; i< values.size(); i++){
        cout << values[i] << " ";
    }
    cout << endl;

    return 0;
}

运行:

PS D:\study\cplusplus\day16> .\stl-vector-push_back.exe10 100

实例17(emplace_back()函数)

#include <iostream>
#include <vector>

using namespace std;
int main(){    
    vector<int> values{};
    
    values.emplace_back(1);//在尾部添加元素    
    values.emplace_back(2);//在尾部添加元素    
    for (int i = 0; i < values.size(); i++) {        
        cout << values[i] << " ";    
    }    
    
    return 0;
}

运行:

1 2

emplace_back()push_back()的区别

emplace_back()push_back() 的区别,就在于底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。

实例18

/*******************************************************************
 *   > File Name: emplace_back.cpp
 *   > Create Time: 2021年10月19日 23:41:15
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

class testDemo{
public:
    testDemo(int num):num(num){
        std::cout << "构造函数" << endl;
    }

    testDemo(const testDemo& other):num(other.num){
        std::cout << "调用拷贝构造函数" << endl;
    }
    testDemo(testDemo&& other):num(other.num){
        std::cout << "调用移动构造函数" << endl;
    }
    ~testDemo(){
        std::cout << "析构函数" << endl;
    }
private:
    int num;
};

int main(int argc, char* argv[])
{
    cout << "emplace_back:" << endl;
    std::vector<testDemo>demo1;
    demo1.emplace_back(2);

    cout << "push_back:" << endl;
    std::vector<testDemo>demo2;
    demo2.push_back(2);

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day17> make
g++ -o emplace_back emplace_back.cpp -g -Wall -std=c++11
PS D:\study\cplusplus\day17> .\emplace_back.exe
emplace_back:
构造函数
push_back:
构造函数
调用移动构造函数
析构函数
析构函数
析构函数

实例19

/*******************************************************************
 *   > File Name: emplace_back.cpp
 *   > Create Time: 2021年10月19日 23:41:15
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

class testDemo{
public:
    testDemo(int num):num(num){
        std::cout << "构造函数" << endl;
    }

    testDemo(const testDemo& other):num(other.num){
        std::cout << "调用拷贝构造函数" << endl;
    }
    // testDemo(testDemo&& other):num(other.num){
        // std::cout << "调用移动构造函数" << endl;
    // }
    ~testDemo(){
        std::cout << "析构函数" << endl;
    }
private:
    int num;
};

int main(int argc, char* argv[])
{
    cout << "emplace_back:" << endl;
    std::vector<testDemo>demo1;
    demo1.emplace_back(2);

    cout << "push_back:" << endl;
    std::vector<testDemo>demo2;
    demo2.push_back(2);

    return 0;
}

编译、运行:

PS E:\fly-prj\cplusplus\day33> make 
g++ -o emplace_back emplace_back.cpp -g -Wall -std=c++11
PS E:\fly-prj\cplusplus\day33> .\emplace_back.exe
emplace_back:
构造函数
push_back:
构造函数
调用拷贝构造函数
析构函数
析构函数
析构函数

从实例18和19中看看出,push_back()在底层实现时,会优先调用移动构造函数,如果没有才会调用拷贝构造函数。

显然完成同样的操作,push_back() 的底层实现过程比 emplace_back() 更繁琐,换句话说,emplace_back() 的执行效率比 push_back() 高。因此,在实际使用时,建议大家优先选用 emplace_back()

由于 emplace_back()C++ 11 标准新增加的,如果程序要兼顾之前的版本,还是应该使用 push_back()

C++ STL vector插入元素(insert()emplace())详解

vector容器提供了 insert()emplace() 这 2 个成员函数,用来实现在容器指定位置处插入元素。

insert()

insert() 函数的功能是在 vector 容器的指定位置插入一个或多个元素。

语法格式

用法说明

iterator insert(pos,elem)

在迭代器 pos 指定的位置之前插入一个新元素elem,并返回表示新插入元素位置的迭代器。

iterator insert(pos,n,elem)

在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。

iterator insert(pos,first,last)

在迭代器 pos 指定的位置之前,插入其他容器(不仅限于vector)中位于 [first,last] 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。

iterator insert(pos,initlist)

在迭代器 pos 指定的位置之前,插入初始化列表(用大括号{}括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。

实例20(insert()

/*******************************************************************
 *   > File Name: stl-vector-insert.cpp
 *   > Create Time: 2021年10月22日 22:32:33
 ******************************************************************/
#include <iostream>
#include <vector>
#include <array>
using namespace std;

template <typename T>
void printVector(vector<T> vec){
    for(auto&& value:vec){
        cout << value << " ";
    }
    cout << endl;
}

int main(int argc, char* argv[])
{
    vector<int>values {1,2};
    printVector(values);

    values.insert(values.begin() + 1, 3);
    printVector(values);

    values.insert(values.end(), 2, 5);
    printVector(values);

    std::array<int, 3>test{ 7, 8, 9};
    values.insert(values.end(), test.begin(), test.end());
    printVector(values);

    values.insert(values.end(), {10, 11});
    printVector(values);

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day17> make stl-vector-insert
g++ -o stl-vector-insert stl-vector-insert.cpp -g -Wall -std=c++11
PS D:\study\cplusplus\day17> .\stl-vector-insert.exe
1 2
1 3 2
1 3 2 5 5
1 3 2 5 5 7 8 9
1 3 2 5 5 7 8 9 10 11

emplace()

emplace()C++ 11 标准新增加的成员函数,用于在 vector 容器指定位置之前插入一个新的元素(只能插入一个元素)。

iterator emplace (const_iterator pos, args...);

其中,pos 为指定插入位置的迭代器;args... 表示与新插入元素的构造函数相对应的多个参数;该函数会返回表示新插入元素位置的迭代器。

实例21(emplace()成员函数

/*******************************************************************
 *   > File Name: stl-vector-emplace.cpp
 *   > Create Time: 2021年10月24日 21:50:42
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    std::vector<int> demo1{1,2};
    demo1.emplace(demo1.begin(), 3);
    for(int i = 0; i< demo1.size() ; i++){
        cout << demo1[i] << " ";
    }
    cout << endl;

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day17> make stl-vector-emplace
g++ -o stl-vector-emplace stl-vector-emplace.cpp -g -Wall -std=c++11
stl-vector-emplace.cpp: 在函数‘int main(int, char**)’中:
stl-vector-emplace.cpp:16:21: 警告:comparison of integer expressions of different signedness: ‘int’ and ‘std::vector<int>:
:size_type’ {aka ‘long unsigned int’} [-Wsign-compare]
   16 |     for(int i = 0; i< demo1.size() ; i++){
      |                    ~^~~~~~~~~~~~~~
PS D:\study\cplusplus\day17> ./stl-vector-emplace.exe
3 1 2

实例22(insert()和emplace()函数)

/*******************************************************************
 *   > File Name: stl-vector-emplace1.cpp
 *   > Create Time: 2021年10月24日 21:58:16
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

class testDemo{
public:
    testDemo(int num):num(num){
        std::cout << "调用构造函数" << endl;
    }
    testDemo(const testDemo& other):num(other.num){
        std::cout << "调用拷贝构造函数" << endl;
    }
    testDemo(testDemo&& other):num(other.num){
        std::cout << "调用移动构造函数" << endl;
    }
    testDemo& operator=(const testDemo& other);
private:
    int num;
};

testDemo& testDemo::operator=(const testDemo& other){
    this->num = other.num;
    return *this;
}

int main(int argc, char* argv[])
{
    cout << "Insert:" << endl;
    std::vector<testDemo> demo2{};
    demo2.insert(demo2.begin(), testDemo(1));

    cout << "emplace: " << endl;
    std::vector<testDemo> demo1{};
    demo1.emplace(demo1.begin(), 1);
    
    return 0;
}

编译、运行:

PS D:\study\cplusplus\day17> make stl-vector-emplace1
g++ -o stl-vector-emplace1 stl-vector-emplace1.cpp -g -Wall -std=c++11
PS D:\study\cplusplus\day17> .\stl-vector-emplace1.exe
Insert:
调用构造函数
调用移动构造函数
emplace:
调用构造函数

注意,当拷贝构造函数和移动构造函数同时存在时,insert() 会优先调用移动构造函数。

emplace()insert() 都能完成向 vector 容器中插入新元素,通过上面程序运行可知,emplace()insert()效率更高。emplace() 在插入元素时,是在容器的指定位置直接构造元素,而不是先单独生成,再将其复制(或移动)到容器中。

C++ STL vector删除元素的几种方式(超级详细)

删除 vector 容器元素的几种方式:

函数

功能

pop_back()

删除 vector 容器中最后一个元素,该容器的大小(size)会减 1,但容量(capacity)不会发生改变。

erase(pos)

删除 vector 容器中 pos 迭代器指定位置处的元素,并返回指向被删除元素下一个位置元素的迭代器。该容器的大小(size)会减 1,但容量(capacity)不会发生改变。

swap(beg)pop_back()

先调用 swap() 函数交换要删除的目标元素和容器最后一个元素的位置,然后使用 pop_back() 删除该目标元素。

erase(beg,end)

删除 vector 容器中位于迭代器 [beg,end)指定区域内的所有元素,并返回指向被删除区域下一个位置元素的迭代器。该容器的大小(size)会减小,但容量(capacity)不会发生改变。

remove()

删除容器中所有和指定元素值相等的元素,并返回指向最后一个元素下一个位置的迭代器。值得一提的是,调用该函数不会改变容器的大小和容量。

clear()

删除 vector 容器中所有的元素,使其变成空的 vector 容器。该函数会改变 vector 的大小(变为 0),但不是改变其容量。

实例23(pop_back()函数)

/*******************************************************************
 *   > File Name: vector-pop_back.cpp
 *   > Create Time: 2021年11月 1日 23:51:03
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int>demo{1, 2, 3, 4, 5};
    demo.pop_back(); /*删除最后一个元素*/

    cout << "size is :" << demo.size() << endl; /*输出容器的大小*/
    cout << "capacity is :" << demo.capacity() << endl; /*输出容器的容量*/

    for(int i=0; i< demo.size(); i++){
        cout << demo[i] << " "; /*遍历输出容器中的元素*/
    }
    cout << endl;

    return 0;
}

编译、运行:

PS D:\study\cplusplus\day18> make
g++ -o vector-pop_back vector-pop_back.cpp -g -Wall -std=c++11
PS D:\study\cplusplus\day18> .\vector-pop_back.exe
size is :4
capacity is :5
1 2 3 4

实例24(erase()函数)

/*******************************************************************
 *   > File Name: vector-erase.cpp
 *   > Create Time: 2021年11月 1日 23:56:50
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int>demo {1, 2, 3, 4, 5};
    auto iter = demo.erase(demo.begin() + 1);

    cout << "size is :" << demo.size() << endl;
    cout << "capacity is :" << demo.capacity() << endl;

    for(int i=0; i< demo.size(); i++){
        cout << demo[i] << " ";
    }
    cout << endl << *iter << endl;

    return 0;
}

运行结果:

PS D:\study\cplusplus\day18> .\vector-erase.exe
size is :4
capacity is :5
1 3 4 5
3

实例25(swap()pop_back()函数)

/*******************************************************************
 *   > File Name: vector-swap.cpp
 *   > Create Time: 2021年11月 2日 22:08:06
 ******************************************************************/
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(int argc, char* argv[])
{
    vector<int>demo{1,2,3,4,5};
    swap(*(std::begin(demo)+1), *(std::end(demo)-1));/*交换元素*/

    for(int i = 0; i< demo.size(); i++){
        cout << demo[i] << " ";
    }
    cout << endl;

    demo.pop_back();/*移除容器最后一个元素*/

    cout << "size is : " << demo.size() << endl;
    cout << "capacity is : " << demo.capacity() << endl;

    for(int i = 0; i< demo.size(); i++){
        cout << demo[i] << " ";
    }
    cout << endl;

    return 0;
}

运行:

PS D:\study\cplusplus\day18> .\vector-swap.exe
1 5 3 4 2
size is : 4
capacity is : 5
1 5 3 4

实例26(erase(begin, end)函数)

/*******************************************************************
 *   > File Name: vector-erase1.cpp
 *   > Create Time: 2021年11月 2日 22:19:16
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
    std::vector<int>demo{1, 2, 3, 4, 5, 6};
    //删除2,3,4
    auto iter = demo.erase(demo.begin()+1, demo.end()-3);
    cout << "*iter is : " << *iter << endl;
    cout << "size is :" << demo.size() << endl;
    cout << "capacity is : " << demo.capacity() << endl;

    for(int i = 0; i< demo.size(); i++){
        cout << demo[i] << " ";
    }
    cout << endl;

    return 0;
}

运行:

PS D:\study\cplusplus\day18> .\vector-erase1.exe
*iter is : 4
size is :4
capacity is : 6
1 4 5 6

实例27(remove()函数)

/*******************************************************************
 *   > File Name: stl-vector-remove.cpp
 *   > Create Time: 2021年11月 7日  9:21:01
 ******************************************************************/

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

int main(void)
{
    std::vector<int>demo{1, 3, 3, 4, 3, 5};
    auto iter = std::remove(demo.begin(), demo.end() , 3);

    cout << "iter is : " << *iter << endl;
    cout << "size is : " << demo.size() << endl;
    cout << "capacity is : " << demo.capacity() << endl;

    for(auto first = demo.begin(); first < iter ; ++ first){
        cout << *first << " ";
    }
    cout << endl;

    for(int i = 0; i < demo.capacity(); i++){
        cout << demo[i] << " ";
    }
    cout << endl;
    
    return 0;
}

运行:

PS E:\MyStudy\cplusplus\day1> .\stl-vector-remove.exe
iter is : 4
size is : 6
capacity is : 6
1 4 5
1 4 5 4 3 5

remove() 的实现原理是,在遍历容器中的元素时,一旦遇到目标元素,就做上标记,然后继续遍历,直到找到一个非目标元素,即用此元素将最先做标记的位置覆盖掉,同时将此非目标元素所在的位置也做上标记,等待找到新的非目标元素将其覆盖。因此,如果将上面程序中 demo 容器的元素全部输出,得到的结果为 1 4 5 4 3 5

实例28(remove()函数)

通过实例27运行可知,remove函数删除demo中的多个指定元素,容器的大小容量都没有变化,其剩余位置还保留了之前存储的元素,使用erase函数可以删除这些元素。

/*******************************************************************
 *   > File Name: stl-vector-remove1.cpp
 *   > Create Time: 2021年11月 7日  9:49:43
 ******************************************************************/
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(void)
{
    vector<int>demo{1, 3, 3, 4, 3, 5};
    auto iter = std::remove(demo.begin(), demo.end(), 3);
    demo.erase(iter, demo.end());
    cout << "size is : " << demo.size() << endl;
    cout << "capacity is : " << demo.capacity() << endl;

    for(int i = 0; i < demo.size() ; i++){
        cout << demo[i] << " " ;
    } 
    cout << endl;

    return 0;
}

编译、运行:

PS E:\MyStudy\cplusplus\day1> .\stl-vector-remove1.exe
size is : 3
capacity is : 6
1 4 5

实例29(clear()函数)

/*******************************************************************
 *   > File Name: stl-vector.clear.cpp
 *   > Create Time: 2021年11月 7日 10:01:06
 ******************************************************************/
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(void)
{
    vector<int>demo{1, 3, 3, 4, 3, 5};
    demo.clear();

    cout << "size is : " << demo.size() << endl;
    cout << "capacity is : " << demo.capacity() << endl;

    return 0;
}

编译、运行:

PS E:\MyStudy\cplusplus\day1> .\stl-vector_clear.exe
size is : 0
capacity is : 6

如何避免vector容器进行不必要的扩容?

vector 容器扩容的整个过程,和 realloc() 函数的实现方法类似,大致分为以下 4 个步骤:

分配一块大小是当前 vector 容量几倍的新存储空间。注意,多数 STL 版本中的 vector 容器,其容器都会以 2 的倍数增长,也就是说,每次 vector 容器扩容,它们的容量都会提高到之前的 2 倍;

vector 容器存储的所有元素,依照原有次序从旧的存储空间复制到新的存储空间中;

析构掉旧存储空间中存储的所有元素;

释放旧的存储空间。

vector 容器的扩容过程是非常耗时的,并且当容器进行扩容后,之前和该容器相关的所有指针、迭代器以及引用都会失效。因此在使用 vector 容器过程中,我们应尽量避免执行不必要的扩容操作。

vector模板类中功能类似的成员方法:

成员方法

功能

size()

告诉我们当前 vector 容器中已经存有多少个元素,但仅通过此方法,无法得知 vector 容器有多少存储空间。

capacity()

告诉我们当前 vector 容器总共可以容纳多少个元素。如果想知道当前 vector 容器有多少未被使用的存储空间,可以通过 capacity()-size() 得知。注意,如果 size()capacity() 返回的值相同,则表明当前 vector 容器中没有可用存储空间了,这意味着,下一次向 vector 容器中添加新元素,将导致 vector 容器扩容。

resize(n)

强制 vector 容器必须存储 n 个元素,注意,如果 nsize() 的返回值小,则容器尾部多出的元素将会被析构(删除);如果 nsize() 大,则 vector 会借助默认构造函数创建出更多的默认值元素,并将它们存储到容器末尾;如果 ncapacity() 的返回值还要大,则 vector 会先扩增,在添加一些默认值元素。

reserve(n)

强制 vector 容器的容量至少为 n。注意,如果 n 比当前 vector 容器的容量小,则该方法什么也不会做;反之如果 n 比当前 vector 容器的容量大,则 vector 容器就会扩容。

**总结:**只要有新元素要添加到 vector 容器中而恰好此时 vector 容器的容量不足时,该容器就会自动扩容。

因此,避免 vector 容器执行不必要的扩容操作的关键在于,在使用 vector 容器初期,就要将其容量设为足够大的值。换句话说,在 vector 容器刚刚构造出来的那一刻,就应该借助 reserve() 成员方法为其扩充足够大的容量。

实例30

假设我们想创建一个包含 1~1000 的 vector<int>,通常会这样实现:

vector<int>myvector;
for (int i = 1; i <= 1000; i++) {
    myvector.push_back(i);
}

上面代码的整个循环过程中,vector 容器会进行 2~10 次自动扩容(多数的 STL 标准库版本中,vector 容器通常会扩容至当前容量的 2 倍,而这里 1000≈2 ^ 10),程序的执行效率可想而知。

实例31

使用reserve()方法尽量避免vector容器的不必要的扩容操作。

vector<int>myvector;
myvector.reserve(1000);
cout << myvector.capacity();
for (int i = 1; i <= 1000; i++) {
    myvector.push_back(i);
}

相比前面的代码实现,整段程序在运行过程中,vector 容器的容量仅扩充了 1 次,执行效率大大提高。

当然在实际场景中,我们可能并不知道 vector 容器到底要存储多少个元素。这种情况下,可以先预留出足够大的空间,当所有元素都存储到 vector 容器中之后,再去除多余的容量。

关于怎样去除 vector 容器多余的容量,可以借助该容器模板类提供的 shrink_to_fit() 成员方法。

vector swap()成员方法还可以这样用!

实例32

在使用 vector 容器的过程中,其容器会根据需要自行扩增。比如,使用 push_back()insert()emplace() 等成员方法向 vector 容器中添加新元素时,如果当前容器已满(即 size() == capacity()),则它会自行扩容以满足添加新元素的需求。当然,还可以调用 reserve() 成员方法来手动提升当前 vector 容器的容量。

/*******************************************************************
 *   > File Name: stl-myvector.cpp
 *   > Create Time: 2021年11月 7日 11:17:40
 ******************************************************************/
#include <iostream>
#include <vector>
using namespace std;

int main(void)
{
    vector<int> myvector;
    cout << "1.myvector size is " << myvector.size() << " , ";
    cout << "capacity is " << myvector.capacity() << endl;

    for(int i = 1; i <= 10; i++){
        myvector.push_back(i);
    }

    cout << "2.myvector size is " << myvector.size() << " , ";
    cout << "capacity is " << myvector.capacity() << endl;

    myvector.reserve(1000);

    cout << "3.myvector size is " << myvector.size() << " , ";
    cout << "capacity is " << myvector.capacity() << endl;

    return 0;
}

运行:

g++ -o stl-vector_clear stl-vector_clear.cpp -g -Wall -std=c++11
PS E:\MyStudy\cplusplus\day1> .\stl-myvector.exe
1.myvector size is 0 , capacity is 0
2.myvector size is 10 , capacity is 16
3.myvector size is 10 , capacity is 1000