【C++】vector常用接口的使用
欢迎拜访:Madison-No7个人主页
文章主题: 探秘string的底层实现
隶属专栏:我的 C++ 成长日志
写作日期:2025年10月12日
目录
一、什么是vector
二、vector常见接口的使用
2.1 构造函数
🌸无参构造:vector()
🌸有参构造:
🌸拷贝构造:
🌸 使用迭代器进行初始化构造:
2.2 vector iterator的使用:
🎯begin+end:
2.3 vecror空间增长的相关接口:
🎯size:获取数据个数
🎯capacity:获取容量大小
🎯empty:判断是否为空
🎯resize:改变vector的size
🎯reserve:改变vector的capacity
2.4 vector的增删查改:
🌊 push_back:尾插
🌊 pop_back:尾删
🌊 insert:在position之前插入val
🌟问题:vector 可以替代 string 嘛?
🌊 erase:删除position位置的数据
🌊 swap:交换两个vector的数据空间
🌊 operator[] :像数组一样访问
三、使用vector需注意的点:
一、什么是vector
-
vector 是一种序列容器,用于表示大小可动态变化的数组。
-
与普通数组类似,vector 的元素也存储在连续的内存空间中。这意味着可以通过元素的常规指针偏移量来访问 vector 中的元素,其访问效率与普通数组完全一致。但与普通数组不同的是,vector 的大小可以动态改变,其内存空间由容器自动管理。
-
在内部实现上,vector 会使用动态分配的数组来存储元素。当插入新元素导致 vector 需要扩容时,容器可能会重新分配内存 —— 即分配一块新的数组空间,并将所有元素移动到新空间中。这种操作的时间开销相对较大,因此 vector 并不会在每次添加元素时都执行重新分配。
-
相反,vector 容器会预先分配额外的内存空间以应对可能的扩容需求。因此,容器的实际容量(capacity)可能会大于存储当前元素所需的内存空间(即容器的大小 size)。不同的标准库实现可能会采用不同的扩容策略,以在内存占用和重新分配频率之间取得平衡。但无论采用何种策略,重新分配都只会在大小以对数级增长的间隔时发生,从而确保在 vector 末尾插入单个元素的操作能达到均摊常数时间复杂度。
-
因此,与普通数组相比,vector 会消耗更多内存,但换来的是更便捷的内存管理能力和高效的动态扩容能力。
-
与其他动态序列容器(
deque
双端队列、list
双向链表、forward_list
单向链表)相比:
- vector 在元素访问上效率极高(与普通数组一致);
- 在末尾添加或删除元素的操作上效率也相对较高;
- 但在非末尾位置插入或删除元素时,效率远低于其他容器;
- 此外,其迭代器和引用的稳定性也不如
list
和forward_list
(元素移动可能导致迭代器 / 引用失效)。
二、vector常见接口的使用
2.1 构造函数
🌸无参构造:vector()
表示创建一个空的 vector 容器,即不包含任何元素,仅初始化容器自身的基础结构。
🌸有参构造:
表示构造并初始化n个val,适用于需要批量初始化相同值元素的场景。
// T:vector存储的元素类型;n:元素数量(非负整数);val:初始化值(T类型)
std::vector<T> 容器名(n, val);
🌸拷贝构造:
🌸 使用迭代器进行初始化构造:
2.2 vector iterator的使用:
🎯begin+end:
表示获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator
遍历vector:
2.3 vecror空间增长的相关接口:
🎯size:获取数据个数

🎯capacity:获取容量大小
capacity底层扩容机制:
void TestVectorExpand()
{size_t sz;vector<int> v;sz = v.capacity();cout << "making v grow:\n";for (int i = 0; i < 100; ++i){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}
}
Vs2022下运行:
Linux下的结果:
🌟综上:capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2 倍增长的。
🎯empty:判断是否为空

注意:如果容器大小为0,则返回True,否则返回false。
🎯resize:改变vector的size

说明:该函数的第二个参数value_type val=value_type();其中value_type表示第一个模板参数(T),,对于vector<int>,T就是int。对于value_type()表示我们不传入第二个参数的时候,才会调用该类型的默认构造函数,对于内置类型,value_type()会生成值为0的临时对象,为什么会是0呢?而不是其他值,这是因为确保了内置类型在这种场景下的初始化行为是可预期的,也就是初始化的值是多少(0)是明确的,而不是随机内存垃圾(随机值)。对于自定义类型,会调用其默认构造函数
resize在开空间的同时还会进行初始化,影响size。
- 如果n小于当前容器的大小(size),则内容将被减少到前n个元素,并删除超出的元素(并销毁它们)。
- 如果n大于当前容器的大小(size),则通过在末尾插入所需的元素来扩展内容,以达到n的大小。如果指定了val,则将新元素初始化为val的副本,否则将初始化为0。
- 如果n大于当前容器容量(capacity),则会扩容。然后通过在末尾插入所需的元素来扩展内容,以达到n的大小。如果指定了val,则将新元素初始化为val的副本,否则将初始化为0。
void Print(const vector<int>& v)
{cout <<"size:" << v.size() << endl;cout << "capacity" << v.capacity() << endl;cout << "遍历数组:";vector<int>::const_iterator it = v.cbegin();while (it != v.cend()){cout << *it << " ";it++;}cout << endl;
}
void test02()
{vector<int> v2(10, 1);Print(v2);cout << "n为5:" << endl;v2.resize(5);Print(v2);cout << "n为15:" << endl;//指定了valv2.resize(15,3);Print(v2);cout << "n为20:" << endl;//没指定valv2.resize(20);Print(v2);
}
🎯reserve:改变vector的capacity
请求更改容量,要求vector容器容量至少足以容纳n个元素。
如果n大于当前的vector容量,则该函数使容器重新分配其存储空间,将其容量增加到n(或更大)。
在所有其他情况下,函数调用不会导致重新分配,向量容量也不会受到影响。
这个函数对vector的大小没有影响,也不能改变vector的元素。
🌟总结:
如果n>capacity大,就扩容(VS下要多少给多少空间),n<capacity,就不用管,也不会缩容。reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
比如:
如果已经确定 vector 中要存储元素的大概个数,可以reserve()提前将空间开好,这样就可以避免免一遍插入一遍扩容而导致效率低下的问题了。
void TestVectorExpand()
{size_t sz;vector<int> v;v.reserve(100);sz = v.capacity();cout << "making v grow:\n";for (int i = 0; i < 100; ++i){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}
}

2.4 vector的增删查改:
🌊 push_back:尾插
注意:value_type指的是第一个模板参数(T)。
🌊 pop_back:尾删
🌊 insert:在position之前插入val
void Print_arr(const vector<int>& v)
{vector<int>::const_iterator it = v.cbegin();while (it != v.cend()){cout << *it << " ";it++;}cout << endl;
}
void test03()
{vector<int> v(10,0);//在下标为5的位置前插入1v.insert(v.begin()+5, 1);Print_arr(v);//在下标为7的位置插入5个2v.insert(v.begin()+7,5,2);Print_arr(v);//在下标为9的位置插入一个迭代器区间的内容v.insert(v.begin()+9,v.begin(),v.end());Print_arr(v);
}
🌟问题:vector<char> 可以替代 string 嘛?
答案是不可以,虽然他们俩的底层本质上都是动态增长的数组,但是 string 字符串的结尾默认有 \0,可以更好的兼容 C 接口,而 vector<char> 的结尾默认是没有 \0 的,需要我们自己插入。
🌊 erase:删除position位置的数据
🌊 swap:交换两个vector的数据空间
void Print_arr(const vector<int>& v,const int& n)
{cout << "v"<<n<<":";vector<int>::const_iterator it = v.cbegin();while (it != v.cend()){cout << *it << " ";it++;}cout << endl;
}
void test04()
{vector<int> v1(10,1);vector<int> v2(5,0);cout << "交换前 :" << endl;Print_arr(v1,1);Print_arr(v2, 2);v1.swap(v2);cout << "交换后:" << endl;Print_arr(v1, 1);Print_arr(v2, 2);
}
🌊 operator[] :像数组一样访问

void test05()
{vector<int> myvector(10,1); for (int i=0;i< myvector.size();i++){myvector[i] = 2;cout << myvector[i] << " ";}cout << endl;const vector<int> myvector2(5, 5);for (int i = 0; i < myvector2.size(); i++){//只读,不可修改//myvector2[i] = 2;cout << myvector2[i] << " ";}
}
三、使用vector需注意的点:
vector里面可以存int、double、char这些自定义类型外,还可以存自定义类型。
比如:
在vector里面存string类型:
void test06()
{string s1="hello world";vector<string> v1;v1.push_back(s1);//尾插字符串,会走隐式类型转换v1.push_back("have a nice day");//底层原理是通过迭代器实现编译器会获取 v1.begin() 和 v1.end() 迭代器,//循环中通过*it解引用获取元素,拷贝给 s(每次循环都会调用string的拷贝构造)。//auto 自动推导 s 的类型为stringfor (auto s:v1){//调用 string 的输出运算符重载,打印字符串内容。cout << s << " ";}//每次循环都会调用string的拷贝构造严重影响效率//所以我们可以加上引用,减少拷贝,提高效率for (auto& s : v1){cout << s << " ";}
}
🌟小Tips:auto后面加不加&,就想s是不是自定义类型且该自定义类型还涉及到申请空间,如果是就要加&,减少深拷贝,提高效率,所以最好还是加上&。
在vector里面存vector类型:
void test08()
{//二维数组//5行10列vector<int> v (10,1);vector<vector<int>> vv(5,v);vv[2][5] = 2;//与vv[2][5] = 2等价:vv.operator[](2).operator[](5) = 2;//遍历二维数组for (int i=0;i<vv.size();i++){for (int j=0;j<v.size();j++){cout << vv[i][j] << " ";}cout << endl;}
}
🌟注意:vector不支持流插入和流提取,因为vector的格式不固定,输出的时候每个数之间有,吗?有空格吗?我们可以自己设置打印的格式,也就是从侧面实现vector的输出。
完。
今天的分享就到这里,感谢各位大佬的关注,大家互相学习,共同进步呀!