7、【C++】STL容器:vector的用法及模拟实现
7、【C++】STL容器:vector的用法及模拟实现
目录
- 一、vector概述
- 二、vector的基本用法
- 三、vector迭代器失效问题
- 四、模拟实现构造函数调用不明确问题
- 五、reserve中的深浅拷贝问题
- 六、模拟实现vector的核心接口
- 七、模拟实现的vector整体代码
- 八、vector使用注意事项与最佳实践
一、vector概述
1. vector的定义与特性
vector是C++标准库提供的动态数组容器,定义在<vector>头文件中,属于序列式容器(元素按线性顺序排列)。它能够自动管理内存,根据元素数量动态调整存储空间大小,支持快速随机访问。
核心特性:
- 动态扩容:当元素数量超过当前容量时,自动分配更大的内存空间(通常为原容量的2倍),并拷贝元素。
- 随机访问:通过下标(
operator[])或迭代器可在O(1)时间内访问任意元素。 - 连续存储:元素在内存中连续存放,与数组布局兼容,可通过指针偏移访问元素。
- 尾部操作高效:
push_back(尾插)和pop_back(尾删)操作在大多数情况下为O(1)时间复杂度。
2. vector与数组的区别
| 特性 | vector | 数组(C-style array) |
|---|---|---|
| 大小管理 | 动态调整,无需手动分配/释放内存 | 固定大小,编译期确定,不可更改 |
| 内存安全 | 自动检查越界(部分实现) | 无越界检查,易导致缓冲区溢出 |
| 接口支持 | 提供丰富成员函数(如push_back) | 仅支持基本指针操作,无内置接口 |
| 迭代器 | 支持迭代器遍历 | 需通过指针模拟迭代器功能 |
| 初始化 | 支持多种初始化方式(列表初始化) | 仅支持静态初始化或手动赋值 |
3. vector的迭代器类型
vector提供四种迭代器类型,满足不同访问需求:
iterator:正向迭代器,可读写元素。const_iterator:正向迭代器,只读元素(const对象使用)。reverse_iterator:反向迭代器,可读写元素(从后向前遍历)。const_reverse_iterator:反向迭代器,只读元素。
迭代器本质:vector的迭代器是原生指针的封装(T*),因此支持指针的所有操作(++、--、+n、-n等)。
示例:
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// 正向迭代器(读写)
std::vector<int>::iterator it = v.begin();
for (; it != v.end(); ++it) {
*it *= 2; // 修改元素
}
// const正向迭代器(只读)
std::vector<int>::const_iterator cit = v.begin();
for (; cit != v.end(); ++cit) {
std::cout << *cit << " "; // 输出:2 4 6 8 10
}
// 反向迭代器
std::vector<int>::reverse_iterator rit = v.rbegin();
for (; rit != v.rend(); ++rit) {
std::cout << *rit << " "; // 输出:10 8 6 4 2
}
return 0;
}
二、vector的基本用法
1. 构造函数与初始化
vector提供多种构造方式,适应不同初始化需求:
| 构造函数原型 | 功能描述 |
|---|---|
vector() | 默认构造,创建空vector |
vector(size_t n, const T& val = T()) | 创建包含n个val的vector |
vector(const vector& v) | 拷贝构造,复制v的内容 |
vector(InputIt first, InputIt last) | 范围构造,复制[first, last)区间的元素 |
vector(std::initializer_list<T> il) | 列表初始化(C++11),如vector<int> v{1,2,3} |
示例:
#include <vector>
#include <array>
int main() {
// 默认构造
std::vector<int> v1;
// 包含5个3的vector
std::vector<int> v2(5, 3); // {3,3,3,3,3}
// 拷贝构造
std::vector<int> v3(v2);
// 范围构造(从数组拷贝)
int arr[] = {1,2,3};
std::vector<int> v4(arr, arr + 3); // {1,2,3}
// 列表初始化(C++11)
std::vector<int> v5{1,2,3,4}; // {1,2,3,4}
return 0;
}
2. 元素访问
vector提供多种元素访问方式,兼顾效率和安全性:
operator[]:随机访问,无越界检查(高效)。at(size_t pos):随机访问,越界时抛出std::out_of_range异常(安全)。front():访问第一个元素。back():访问最后一个元素。data():返回指向底层数组的指针(C++11)。
示例:
std::vector<int> v = {1,2,3,4,5};
std::cout << v[0] << std::endl; // 1(无越界检查)
std::cout << v.at(2) << std::endl; // 3(有越界检查)
std::cout << v.front() << std::endl; // 1
std::cout << v.back() << std::endl; // 5
int* p = v.data(); // 指向v[0]的指针
3. 容量与大小管理
vector的容量(capacity)和大小(size)是两个核心概念:
- size:当前元素个数。
- capacity:当前分配的内存可容纳的最大元素个数(不重新分配内存的情况下)。
常用接口:
size():返回size。capacity():返回capacity。empty():判断是否为空(size == 0)。reserve(size_t new_cap):调整capacity至new_cap(仅增大,不改变size)。resize(size_t n, T val = T()):调整size至n,新增元素用val填充。shrink_to_fit():将capacity缩减至size(C++11,释放未使用内存)。
示例:
std::vector<int> v;
v.reserve(10); // capacity=10,size=0
v.resize(5, 1); // size=5,capacity=10,元素为{1,1,1,1,1}
v.push_back(2); // size=6,capacity=10
v.shrink_to_fit(); // capacity=6(释放多余内存)
4. 修改操作
vector提供丰富的元素修改接口:
push_back(const T& val):尾插元素。pop_back():尾删元素。insert(iterator pos, const T& val):在pos位置插入val。erase(iterator pos):删除pos位置元素。swap(vector& v):交换两个vector的内容(O(1)时间)。clear():清空所有元素(size=0,capacity不变)。
示例:
std::vector<int> v = {1,2,3};
v.push_back(4); // {1,2,3,4}
v.pop_back(); // {1,2,3}
auto it = v.begin() + 1;
v.insert(it, 5); // {1,5,2,3}
v.erase(it); // {1,2,3}(it指向5,删除后it失效)
v.clear(); // size=0,capacity=4(假设初始capacity为4)
三、vector迭代器失效问题
1. 迭代器失效的场景
迭代器本质是指向vector底层数组的指针,当数组内存发生变化或元素位置改变时,迭代器会失效(指向无效内存)。常见失效场景:
(1)扩容导致失效
当push_back或reserve触发扩容时,vector会分配新内存并拷贝元素,原迭代器指向旧内存,导致失效。
std::vector<int> v = {1,2,3};
int* p = &v[0]; // 指向旧内存
v.reserve(100); // 触发扩容,分配新内存
std::cout << *p << std::endl; // 未定义行为(旧内存已释放)
(2)插入元素导致失效
- 尾部插入(未扩容):迭代器有效(
end()失效)。 - 中间插入:插入位置之后的迭代器失效(元素后移导致位置改变)。
std::vector<int> v = {1,2,3,4};
auto it = v.begin() + 2; // 指向3
v.insert(it, 5); // 插入后元素为{1,2,5,3,4}
// it现在指向5的下一个位置(原3的位置),但插入后it已失效
(3)删除元素导致失效
- 尾部删除:仅
end()失效,其他迭代器有效。 - 中间删除:删除位置之后的迭代器失效(元素前移导致位置改变)。
std::vector<int> v = {1,2,3,4};
auto it = v.begin() + 1; // 指向2
v.erase(it); // 删除后元素为{1,3,4},it失效(指向原2的位置,现为3)
2. Visual Studio和g++对迭代器失效的表现差异
不同编译器对迭代器失效的处理策略不同,导致表现差异:
(1)Visual Studio(MSVC)
- 迭代器校验:Debug模式下,迭代器包含额外校验信息(如版本号),失效后访问会直接崩溃(抛出
std::out_of_range或断言失败)。 - 示例:
std::vector<int> v = {1,2,3}; auto it = v.begin(); v.push_back(4); // 若触发扩容,it失效 *it = 5; // Debug模式下崩溃:迭代器已失效
(2)G++(GNU libstdc++)
- 无校验机制:迭代器本质是裸指针,失效后访问不会立即崩溃,但行为未定义(可能修改错误内存或程序异常)。
- 示例:
std::vector<int> v = {1,2,3}; auto it = v.begin(); v.push_back(4); // 若未触发扩容,it仍有效;若扩容,it指向旧内存 *it = 5; // 未扩容时正常,扩容后修改无效内存(未定义行为)
3. 解决迭代器失效的方法
(1)使用成员函数返回值更新迭代器
insert和erase返回新的有效迭代器,可用于更新:
std::vector<int> v = {1,2,3,4};
auto it = v.begin() + 1; // 指向2
// insert返回新插入元素的迭代器
it = v.insert(it, 5); // it现在指向5
++it; // 指向2(原位置元素)
// erase返回删除元素的下一个迭代器
it = v.erase(it); // 删除2,it指向3
(2)避免在遍历中修改容量
遍历前reserve足够容量,避免扩容:
std::vector<int> v;
v.reserve(100); // 预分配容量
for (int i = 0; i < 100; ++i) {
v.push_back(i); // 无扩容,迭代器有效
}
(3)使用索引而非迭代器
遍历中使用索引访问,不受迭代器失效影响:
std::vector<int> v = {1,2,3,4};
for (size_t i = 0; i < v.size(); ++i) {
if (v[i] == 2) {
v.erase(v.begin() + i); // 删除后索引自动调整
--i; // 修正索引(元素前移)
}
}
(4)使用范围for时避免修改容器
范围for基于迭代器实现,修改容器(如插入/删除)会导致迭代器失效:
// 错误示例:范围for中删除元素
for (auto x : v) {
if (x == 2) v.erase(...); // 迭代器失效,范围for内部迭代器未更新
}
// 正确示例:使用传统for循环并更新迭代器
for (auto it = v.begin(); it != v.end();) {
if (*it == 2) {
it = v.erase(it); // 更新迭代器
} else {
++it;
}
}
四、模拟实现构造函数调用不明确问题
1. 问题描述
C++11引入初始化器列表(initializer_list)后,当vector构造函数同时支持大小初始化和列表初始化时,可能导致调用歧义。
场景:
// 模拟vector构造函数
template <typename T>
class vector {
public:
// 构造函数1:n个val
vector(size_t n, const T& val = T()) {
std::cout << "vector(size_t, const T&)" << std::endl;
}
// 构造函数2:初始化器列表
vector(std::initializer_list<T> il) {
std::cout << "vector(initializer_list<T>)" << std::endl;
}
};
int main() {
vector<int> v(10, 20); // 调用构造函数1(10个20)
vector<int> v2{10, 20}; // 调用构造函数2(列表初始化,元素{10,20})
// 歧义场景:当参数为(size_t, int)时
vector<int> v3(10, 20); // 明确调用构造函数1
vector<int> v4{10, 20}; // 明确调用构造函数2
// 问题:当参数为(10)时,是构造10个默认元素还是列表初始化{10}?
vector<int> v5(10); // 调用构造函数1(10个int())
vector<int> v6{10}; // 调用构造函数2(列表初始化{10})
return 0;
}
歧义根源:当构造函数参数既能匹配size_t又能匹配initializer_list时,编译器优先选择初始化器列表构造函数(C++11规则)。
2. 解决调用不明确的方法
(1)使用explicit关键字
将单参数构造函数声明为explicit,禁止隐式类型转换,避免编译器误判:
template <typename T>
class vector {
public:
// 单参数构造函数声明为explicit
explicit vector(size_t n, const T& val = T()) { ... }
vector(std::initializer_list<T> il) { ... }
};
// 此时,列表初始化只能调用initializer_list构造函数
vector<int> v{10}; // 明确调用initializer_list版本
vector<int> v2(10); // 明确调用size_t版本
(2)使用std::initializer_list强制列表初始化
通过std::initializer_list显式指定列表初始化,消除歧义:
vector<int> v(std::initializer_list<int>{10, 20}); // 明确调用列表构造函数
(3)避免模糊参数类型
调用构造函数时,确保参数类型匹配预期构造函数:
// 传递size_t参数(避免int隐式转换为size_t导致歧义)
vector<int> v(static_cast<size_t>(10), 20); // 明确匹配(size_t, T)构造函数
五、reserve中的深浅拷贝问题
1. reserve中浅拷贝发生原因
当vector存储自定义类型对象(尤其是包含指针成员的对象)时,reserve触发扩容会执行浅拷贝(逐字节拷贝对象),导致多个对象共享同一块内存,析构时重复释放,引发崩溃。
示例场景:
class String {
public:
char* _str;
String(const char* str = "") {
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
~String() { delete[] _str; } // 析构时释放_str
};
int main() {
std::vector<String> v;
v.reserve(2); // 初始容量2
v.push_back(String("hello")); // 插入第一个元素
v.reserve(10); // 触发扩容,浅拷贝元素
// 此时v[0]._str与原临时对象的_str指向同一块内存
// 析构时临时对象先释放_str,v[0]._str成为野指针,再次释放崩溃
}
2. 浅拷贝发生的图解
扩容前内存布局:
vector对象: [ ptr -------> 堆内存: [String("hello")] (size=1, capacity=2)
| _str -------> "hello\0"
扩容后浅拷贝内存布局:
旧内存(待释放): [String("hello")]
| _str -------> "hello\0"
vector对象: [ ptr -------> 新堆内存: [String("hello")] (size=1, capacity=10)
| _str -------> "hello\0"(与旧内存共享)
析构时崩溃:旧内存的String对象析构释放_str,新内存的String对象析构时再次释放已失效的_str,导致双重释放错误。
3. 解决方法
(1)重写拷贝构造函数和赋值运算符(深拷贝)
为自定义类型添加深拷贝构造和赋值运算符,确保拷贝时复制指针指向的内容而非指针本身:
class String {
public:
char* _str;
String(const char* str = "") {
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
// 深拷贝构造函数
String(const String& s) {
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}
// 深拷贝赋值运算符
String& operator=(const String& s) {
if (this != &s) {
delete[] _str;
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}
return *this;
}
~String() { delete[] _str; }
};
(2)使用智能指针(如std::shared_ptr)
通过智能指针管理内存,自动处理引用计数,避免手动释放:
#include <memory>
class String {
public:
std::shared_ptr<char> _str; // 共享指针,自动管理引用计数
String(const char* str = "") {
_str = std::shared_ptr<char>(new char[strlen(str) + 1], [](char* p) { delete[] p; });
strcpy(_str.get(), str);
}
// 无需手动编写拷贝构造和赋值运算符(shared_ptr自动处理)
};
(3)避免在vector中存储原始指针
优先存储值类型对象或智能指针,减少手动内存管理:
// 推荐:存储值类型
std::vector<std::string> v1; // std::string内部实现深拷贝
// 推荐:存储智能指针
std::vector<std::shared_ptr<int>> v2;
// 不推荐:存储原始指针
std::vector<char*> v3; // 需手动管理指针生命周期,易导致浅拷贝问题
六、模拟实现vector的核心接口
1. 成员变量设计
vector的核心成员变量包括指向数据的指针、大小和容量:
template <typename T>
class vector {
public:
typedef T* iterator;
typedef const T* const_iterator;
private:
iterator _start; // 指向第一个元素
iterator _finish; // 指向最后一个元素的下一个位置
iterator _end_of_storage; // 指向容量末尾
};
关系:
size() = _finish - _startcapacity() = _end_of_storage - _start
2. 构造函数与析构函数
(1)默认构造函数
vector() : _start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {}
(2)带大小和初始值的构造函数
vector(size_t n, const T& val = T()) {
reserve(n); // 预分配容量
for (size_t i = 0; i < n; ++i) {
push_back(val); // 尾插元素
}
}
(3)初始化器列表构造函数(C++11)
vector(std::initializer_list<T> il) {
reserve(il.size());
for (const T& val : il) {
push_back(val);
}
}
(4)拷贝构造函数(深拷贝)
vector(const vector& v) {
reserve(v.capacity());
for (const T& val : v) {
push_back(val);
}
}
(5)析构函数
~vector() {
if (_start) {
// 销毁所有元素(调用T的析构函数)
for (iterator it = _start; it != _finish; ++it) {
it->~T();
}
// 释放内存
operator delete(_start);
_start = _finish = _end_of_storage = nullptr;
}
}
3. 迭代器实现
iterator begin() { return _start; }
const_iterator begin() const { return _start; }
iterator end() { return _finish; }
const_iterator end() const { return _finish; }
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
4. 容量管理接口
(1)reserve
void reserve(size_t new_cap) {
if (new_cap > capacity()) {
size_t old_size = size();
// 分配新内存(operator new分配原始内存,不调用构造函数)
iterator new_start = (iterator)operator new(new_cap * sizeof(T));
iterator new_finish = new_start;
// 拷贝旧元素到新内存(调用T的拷贝构造函数)
for (iterator it = _start; it != _finish; ++it) {
new (new_finish) T(*it); // 定位new:在new_finish处构造T
++new_finish;
}
// 销毁旧元素并释放旧内存
if (_start) {
for (iterator it = _start; it != _finish; ++it) {
it->~T();
}
operator delete(_start);
}
// 更新指针
_start = new_start;
_finish = new_finish;
_end_of_storage = _start + new_cap;
}
}
(2)resize
void resize(size_t n, const T& val = T()) {
if (n > size()) {
// 插入新元素
reserve(n);
for (size_t i = size(); i < n; ++i) {
push_back(val);
}
} else if (n < size()) {
// 销毁多余元素
while (_finish != _start + n) {
--_finish;
_finish->~T();
}
}
}
5. 元素访问接口
T& operator[](size_t pos) {
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const {
assert(pos < size());
return _start[pos];
}
T& front() {
assert(!empty());
return *_start;
}
const T& front() const {
assert(!empty());
return *_start;
}
T& back() {
assert(!empty());
return *(_finish - 1);
}
const T& back() const {
assert(!empty());
return *(_finish - 1);
}
6. 修改操作接口
(1)push_back
void push_back(const T& val) {
if (_finish == _end_of_storage) {
// 扩容:新容量=旧容量*2(至少1)
size_t new_cap = capacity() == 0 ? 1 : capacity() * 2;
reserve(new_cap);
}
new (_finish) T(val); // 定位new:在_finish处构造元素
++_finish;
}
(2)pop_back
void pop_back() {
assert(!empty());
--_finish;
_finish->~T(); // 销毁最后一个元素
}
(3)insert
iterator insert(iterator pos, const T& val) {
assert(pos >= _start && pos <= _finish);
size_t len = pos - _start;
if (_finish == _end_of_storage) {
size_t new_cap = capacity() == 0 ? 1 : capacity() * 2;
reserve(new_cap);
}
// 更新pos(扩容后可能失效,需重新计算)
pos = _start + len;
// 移动元素(从后向前)
for (iterator it = _finish; it != pos; --it) {
new (it) T(*(it - 1)); // 拷贝构造前一个元素
(it - 1)->~T(); // 销毁前一个元素
}
new (pos) T(val); // 插入新元素
++_finish;
return pos; // 返回新插入元素的迭代器
}
(4)erase
iterator erase(iterator pos) {
assert(pos >= _start && pos < _finish);
// 移动元素(从前向后覆盖)
for (iterator it = pos + 1; it != _finish; ++it) {
*(it - 1) = *it; // 赋值运算符(需T支持)
}
--_finish;
_finish->~T(); // 销毁最后一个元素
return pos; // 返回删除元素的下一个迭代器
}
七、模拟实现的vector整体代码
以下是完整的vector模拟实现代码,包含上述所有接口:
#include <cstddef>
#include <initializer_list>
#include <algorithm>
#include <cassert>
namespace my {
template <typename T>
class vector {
public:
// 迭代器类型
typedef T* iterator;
typedef const T* const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
// 构造函数
vector() : _start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {}
vector(size_t n, const T& val = T()) {
reserve(n);
for (size_t i = 0; i < n; ++i) {
push_back(val);
}
}
vector(std::initializer_list<T> il) {
reserve(il.size());
for (const T& val : il) {
push_back(val);
}
}
vector(const vector& v) {
reserve(v.capacity());
for (const T& val : v) {
push_back(val);
}
}
// 赋值运算符
vector& operator=(const vector& v) {
if (this != &v) {
vector tmp(v); // 拷贝构造临时对象
swap(tmp); // 交换临时对象和当前对象
}
return *this;
}
// 析构函数
~vector() {
if (_start) {
// 销毁所有元素
for (iterator it = _start; it != _finish; ++it) {
it->~T();
}
// 释放内存
operator delete(_start);
_start = _finish = _end_of_storage = nullptr;
}
}
// 迭代器接口
iterator begin() { return _start; }
const_iterator begin() const { return _start; }
iterator end() { return _finish; }
const_iterator end() const { return _finish; }
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
// 容量接口
size_t size() const { return _finish - _start; }
size_t capacity() const { return _end_of_storage - _start; }
bool empty() const { return size() == 0; }
void reserve(size_t new_cap) {
if (new_cap > capacity()) {
size_t old_size = size();
iterator new_start = (iterator)operator new(new_cap * sizeof(T));
iterator new_finish = new_start;
// 拷贝旧元素到新内存
for (iterator it = _start; it != _finish; ++it) {
new (new_finish) T(*it);
++new_finish;
}
// 销毁旧元素并释放内存
if (_start) {
for (iterator it = _start; it != _finish; ++it) {
it->~T();
}
operator delete(_start);
}
// 更新指针
_start = new_start;
_finish = new_finish;
_end_of_storage = _start + new_cap;
}
}
void resize(size_t n, const T& val = T()) {
if (n > size()) {
reserve(n);
for (size_t i = size(); i < n; ++i) {
push_back(val);
}
} else if (n < size()) {
while (_finish != _start + n) {
--_finish;
_finish->~T();
}
}
}
void shrink_to_fit() {
if (capacity() > size()) {
reserve(size()); // 缩容到size
}
}
// 元素访问接口
T& operator[](size_t pos) {
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const {
assert(pos < size());
return _start[pos];
}
T& at(size_t pos) {
if (pos >= size()) {
throw std::out_of_range("vector::at");
}
return _start[pos];
}
const T& at(size_t pos) const {
if (pos >= size()) {
throw std::out_of_range("vector::at");
}
return _start[pos];
}
T& front() {
assert(!empty());
return *_start;
}
const T& front() const {
assert(!empty());
return *_start;
}
T& back() {
assert(!empty());
return *(_finish - 1);
}
const T& back() const {
assert(!empty());
return *(_finish - 1);
}
// 修改操作接口
void push_back(const T& val) {
if (_finish == _end_of_storage) {
size_t new_cap = capacity() == 0 ? 1 : capacity() * 2;
reserve(new_cap);
}
new (_finish) T(val);
++_finish;
}
void pop_back() {
assert(!empty());
--_finish;
_finish->~T();
}
iterator insert(iterator pos, const T& val) {
assert(pos >= _start && pos <= _finish);
size_t len = pos - _start;
if (_finish == _end_of_storage) {
size_t new_cap = capacity() == 0 ? 1 : capacity() * 2;
reserve(new_cap);
}
pos = _start + len; // 重新计算pos(扩容后可能失效)
// 移动元素
for (iterator it = _finish; it != pos; --it) {
new (it) T(*(it - 1));
(it - 1)->~T();
}
new (pos) T(val);
++_finish;
return pos;
}
iterator erase(iterator pos) {
assert(pos >= _start && pos < _finish);
for (iterator it = pos + 1; it != _finish; ++it) {
*(it - 1) = *it;
}
--_finish;
_finish->~T();
return pos;
}
void swap(vector& v) {
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
void clear() {
for (iterator it = _start; it != _finish; ++it) {
it->~T();
}
_finish = _start;
}
private:
iterator _start; // 指向第一个元素
iterator _finish; // 指向最后一个元素的下一个位置
iterator _end_of_storage;// 指向容量末尾
};
} // namespace my
// 使用示例
#include <iostream>
int main() {
my::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.insert(v.begin() + 1, 4); // {1,4,2,3}
for (int x : v) {
std::cout << x << " ";
}
v.erase(v.begin() + 2); // {1,4,3}
std::cout << std::endl;
for (int x : v) {
std::cout << x << " ";
}
return 0;
}
八、vector使用注意事项与最佳实践
1. 避免频繁扩容
- 预分配容量:通过
reserve预先分配已知的最大容量,减少扩容次数(如读取文件前reserve文件大小)。 - 选择合适的初始容量:避免初始容量过小导致频繁扩容(如vector v(0); v.reserve(1000);)。
2. 迭代器失效处理
- 使用返回值更新迭代器:
insert和erase返回新迭代器,及时更新避免失效。 - 范围for中禁止修改容器:范围for内部迭代器无法更新,修改容器会导致失效。
3. 存储自定义类型时的内存管理
- 确保深拷贝:自定义类型需重写拷贝构造和赋值运算符,避免浅拷贝问题。
- 优先使用智能指针:存储指针时,使用
std::shared_ptr或std::unique_ptr管理生命周期。
4. 性能优化
- 使用
emplace_back替代push_back:C++11的emplace_back直接在容器中构造元素,避免临时对象拷贝(需编译器支持)。 - 避免不必要的拷贝:传递vector时使用引用(
const vector&),避免值传递导致的深拷贝。
5. 安全性考虑
- 使用
at()而非operator[]:at()提供越界检查,适合调试阶段;operator[]效率更高,适合发布版本。 - 清空容器后释放内存:使用
clear()后调用shrink_to_fit()释放未使用内存(C++11)。
通过以上实践,可高效、安全地使用vector,充分发挥其动态数组的优势。
更多推荐
所有评论(0)