vector 模拟实现代码的核心
下面从类设计、核心接口、内存安全、常见陷阱、测试场景5 个维度,提炼需重点掌握的知识点,覆盖面试高频考点与实践易错点:
一、类结构与成员变量(基础框架)
vector 的核心是通过三个迭代器(指针) 管理动态内存,这是理解所有接口的前提:
-
_start
:指向动态数组的起始位置(首元素) -
_finish
:指向动态数组中已使用元素的下一个位置(size =_finish - _start
) -
_end_of_storage
:指向动态数组的末尾位置(capacity =_end_of_storage - _start
) -
成员变量用C++11 类内初始值(
= nullptr
)初始化,因此默认构造可空实现(vector() {}
),简化代码。
二、构造函数设计(重载与模板陷阱)
构造函数是 vector 的 "入口",需重点关注重载匹配问题与通用性:
- 避免模板构造函数的匹配陷阱
代码中提供了两个n, val
构造的重载:
cpp
vector(size_t n, const T& val = T()); // 1. size_t版本
vector(int n, const T& val = T()); // 2. int版本
为什么需要int
版本?(没懂)
若只写size_t n
版本,当用户传入int
类型的n
(如vector<int> v(10, 5)
,10 是int
),编译器会优先匹配模板迭代器构造(vector(InputIterator first, InputIterator last)
)------ 因为int
可被推导为InputIterator
类型,导致将10
和5
当作 "迭代器范围",而int
不是迭代器,直接编译报错。
注:
-
在 C++ 中,像
10
、5
这样的整数字面量,默认类型是int
(有符号整型),而不是size_t
(无符号整型,通常是unsigned int
或unsigned long long
,取决于平台)。 -
只有显式写
10u
(加u
表示无符号)或size_t(10)
,才会被视为size_t
类型。 -
若只有
size_t
版本,编译器会优先选择模板迭代器构造函数(精确匹配),但int
不是迭代器,导致报错; -
加个
int
版本后,它会作为 "非模板的精确匹配" 优先被选中,确保调用的是 "n 个 val 初始化" 的正确逻辑。
结论:必须重载int n
版本,避免模板构造函数 "抢错" 匹配。
- 模板迭代器构造(通用性关键)
cpp
template <class InputIterator>
vector(InputIterator first, InputIterator last);
-
作用:支持从任意 "迭代器范围"(
[first, last)
)初始化,如string
的迭代器、原生数组指针、其他容器的迭代器。 -
示例:
cpp
std::string s1("hello");
vector<int> v3(s1.begin(), s1.end()); // char转int,存储ASCII值
int a[] = {100,10,2};
vector<int> v4(a, a+3); // 原生数组指针作迭代器
- 注意:迭代器类型需支持
*
解引用和++
自增(符合 InputIterator 概念)。
- 拷贝构造(深拷贝的正确实现)
代码中拷贝构造的核心是避免浅拷贝:
cpp
vector(const vector<T>& v) {
_start = new T[v.capacity()]; // 开和原对象capacity相同的空间
// 循环赋值(深拷贝),而非memcpy(浅拷贝)
for (size_t i = 0; i < v.size(); ++i) {
_start[i] = v._start[i];
}
_finish = _start + v.size();
_end_of_storage = _start + v.capacity();
}
关键对比:为什么不用memcpy
?
-
memcpy
是字节级拷贝(浅拷贝),仅适用于 POD 类型(如int
、double
); -
若
T
是复杂类型(如string
、自定义类),memcpy
会导致两个对象的成员指针指向同一块内存,析构时重复释放(崩溃); -
循环赋值会调用
T
的赋值运算符(如string::operator=
),完成深拷贝(为新string
开辟独立内存)。
结论:动态数组存储非 POD 类型时,必须用循环赋值实现深拷贝。
三、核心接口实现(易错点与设计思路)
vector 的核心接口(reserve
/resize
/insert
/erase
)是面试高频考点,需关注功能差异、迭代器失效、扩容策略。
- reserve vs resize(最易混淆的接口)
两者均可能扩容,但核心作用完全不同:
|----------------|----------------|----------------------------|-----------------------------|------------------------------|
| 接口 | 作用 | 对 size 的影响 | 对 capacity 的影响 | 元素初始化 |
| reserve(n) | 仅保证 capacity≥n | 不改变 | 若 n > 当前 capacity 则扩容,否则不做 | 不初始化新元素 |
| resize(n, val) | 保证 size=n | 改变(n<size 截断,n>size 补元素) | 若 n > 当前 capacity 则扩容,否则不做 | n>size 时,新增元素用val初始化(默认T()) |
示例:
cpp
v1.resize(10); // size从5→10,新增5个元素(int()=0),capacity若不足则扩容
v1.resize(3); // size从10→3,仅移动_finish(_start+3),不释放内存
- insert(迭代器失效的典型场景)
insert(pos, val)
的核心是解决扩容导致的 pos 失效:
cpp
iterator insert(iterator pos, const T& val) {
if (_finish == _end_of_storage) {
size_t len = pos - _start; // 记录pos到起始的距离(关键)
reserve(/*扩容*/);
pos = _start + len; // 扩容后更新pos(旧pos指向已释放内存)
}
// 元素后移(从后往前)
iterator end = _finish - 1;
while (end >= pos) {
*(end + 1) = *end;
--end;
}
*pos = val;
++_finish;
return pos; // 返回更新后的pos,方便用户后续使用
}
关键注意点:
-
扩容后必须重新计算 pos:因为扩容会分配新内存,旧
pos
指向的旧内存已被释放(野指针); -
返回新
pos
:避免用户使用失效的迭代器(如 test_vector3 中pos = v1.insert(pos, 30)
后,可安全修改*pos
)。
- erase(循环删除的正确写法)
erase(pos)
的核心是迭代器失效范围(仅pos
之后的迭代器失效,pos
本身被返回的新迭代器替代):
cpp
iterator erase(iterator pos) {
// 元素前移(从pos+1开始)
iterator start = pos + 1;
while (start != _finish) {
*(start - 1) = *start;
++start;
}
--_finish;
return pos; // 返回指向"原pos下一个元素"的迭代器
}
典型场景:循环删除满足条件的元素
错误写法(会导致迭代器失效,漏删或越界):'
cpp
for (auto it = v1.begin(); it != v1.end(); ++it) {
if (*it % 2 == 0) {
v1.erase(it); // erase后it失效,++it操作非法
}
}
正确写法(用 erase 的返回值更新迭代器):
cpp
auto it = v1.begin();
while (it != v1.end()) {
if (*it % 2 == 0) {
it = v1.erase(it); // 用返回值更新it,指向删除后的下一个元素
} else {
++it; // 不删除则正常后移
}
}
- push_back(扩容策略)
cpp
void push_back(const T& x) {
if (_finish == _end_of_storage) {
// 扩容策略:初始capacity=0→4,否则扩为2倍
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
++_finish;
}
扩容策略的意义:
-
避免每次
push_back
都扩容(减少内存分配次数,提高效率); -
2 倍扩容是平衡 "内存利用率" 和 "分配次数" 的经典方案(1.5 倍扩容更优,但 2 倍实现简单)。
四、const 正确性(规范设计)
代码严格遵循 "const 对象只能调用 const 接口",这是 C++ 的规范设计,需关注:
-
const 迭代器:提供
const_iterator begin() const
和const_iterator end() const
,支持 const 对象遍历(如func(const vector<int>& v)
中使用v.begin()
); -
const 版本 operator []:
cpp
const T& operator[](size_t pos) const {
assert(pos < size());
return _start[pos];
}
- 确保 const 对象只能读取元素,不能修改(如
func
中v[i]
无法赋值)。
五、测试用例中的典型场景(实践巩固)
代码中的测试用例覆盖了 vector 的核心使用场景,复习时需结合案例理解:
-
test_vector5(循环删除):掌握
erase
后迭代器更新的正确写法; -
test_vector7(嵌套 vector):
vector<vector<int>>
实现杨辉三角,理解二维动态数组的管理(外层 vector 存储内层 vector,内层 vector 各自管理自己的内存); -
test_vector7(复杂类型拷贝):
vector<string> v4(v3)
验证深拷贝正确性(若用memcpy
,v3
和v4
的string
会共享内存,析构崩溃); -
test_vector6(sort 排序):
sort(v1.begin(), v1.end())
说明 vector 的迭代器是随机访问迭代器(支持sort
的要求),而 list 的迭代器不支持。
六、总结
-
三个迭代器的作用与
size()
/capacity()
的计算方式; -
拷贝构造为什么不能用
memcpy
(深拷贝 vs 浅拷贝); -
reserve
与resize
的功能差异(是否改变 size、是否初始化元素); -
insert
和erase
的迭代器失效问题(如何避免、返回值的作用); -
循环删除元素的正确写法(结合
erase
的返回值); -
模板构造函数的匹配陷阱(为什么需要
int n
的重载)。
cpp
#pragma once
#include<assert.h>
namespace bit
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
//vector()
// :_start(nullptr)
// , _finish(nullptr)
// , _end_of_storage(nullptr)
//{}
//vector(size_t n, const T& val = T())
// : _start(nullptr)
// , _finish(nullptr)
// , _end_of_storage(nullptr)
//{
// reserve(n);
// for (size_t i = 0; i < n; ++i)
// {
// push_back(val);
// }
//}
//// [first, last)
//template <class InputIterator>
//vector(InputIterator first, InputIterator last)
// : _start(nullptr)
// , _finish(nullptr)
// , _end_of_storage(nullptr)
//{
// while (first != last)
// {
// push_back(*first);
// ++first;
// }
//}
vector()
{}
//vector<int> v(10, 5);
vector(size_t n, const T& val = T())
{
reserve(n);
for (size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
vector(int n, const T& val = T())
{
reserve(n);
for (int i = 0; i < n; ++i)
{
push_back(val);
}
}
/*vector(const vector<T>& v)
{
reserve(v.capacity());
for (auto e : v)
{
push_back(e);
}
}*/
vector(const vector<T>& v)
{
_start = new T[v.capacity()];
//memcpy(_start, v._start, sizeof(T)*v.size());
for (size_t i = 0; i < v.size(); ++i)
{
_start[i] = v._start[i];
}
_finish = _start + v.size();
_end_of_storage = _start + v.capacity();
}
// [first, last)
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
~vector()
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
void resize(size_t n, T val = T())
{
if (n < size())
{
// 删除数据
_finish = _start + n;
}
else
{
if (n > capacity())
reserve(n);
while (_finish != _start+n)
{
*_finish = val;
++_finish;
}
}
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//memcpy(tmp, _start, sizeof(T)*size());
//深拷贝
for (size_t i = 0; i < sz; ++i)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_end_of_storage = _start + n;
}
}
void push_back(const T& x)
{
if (_finish == _end_of_storage)
{
reserve(capacity() == 0 ? 4 : capacity()*2);
}
*_finish = x;
++_finish;
}
void pop_back()
{
assert(!empty());
--_finish;
}
iterator insert(iterator pos, const T& val)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _end_of_storage)
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
// 扩容后更新pos,解决pos失效的问题
pos = _start + len;
}
iterator end = _finish-1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = val;
++_finish;
return pos;
}
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator start = pos + 1;
while (start != _finish)
{
*(start - 1) = *start;
++start;
}
--_finish;
return pos;
}
size_t capacity() const
{
return _end_of_storage - _start;
}
size_t size() const
{
return _finish - _start;
}
bool empty()
{
return _start == _finish;
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;
};
void func(const vector<int>& v)
{
for (size_t i = 0; i < v.size(); ++i)
{
cout << v[i] << " ";
}
cout << endl;
vector<int>::const_iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl << endl;
}
void test_vector1()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
func(v1);
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
v1.pop_back();
v1.pop_back();
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
v1.pop_back();
v1.pop_back();
v1.pop_back();
//v1.pop_back();
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
//func(v1);
}
//template<class T>
//void f()
//{
// T x = T();
// cout << x << endl;
//}
//void test_vector2()
//{
// // 内置类型有没有构造函数
///* int i = int();
// int j = int(1);*/
// f<int>();
// f<int*>();
// f<double>();
//}
void test_vector2()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
cout << v1.size() << endl;
cout << v1.capacity() << endl;
v1.resize(10);
cout << v1.size() << endl;
cout << v1.capacity() << endl;
func(v1);
v1.resize(3);
func(v1);
}
void test_vector3()
{
std::vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
//v1.push_back(5);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
/*v1.insert(v1.begin(), 0);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;*/
auto pos = find(v1.begin(), v1.end(), 3);
if (pos != v1.end())
{
//v1.insert(pos, 30);
pos = v1.insert(pos, 30);
}
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
// insert以后我们认为pos失效了,不能再使用
(*pos)++;
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
void test_vector4()
{
std::vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
//auto pos = find(v1.begin(), v1.end(), 2);
auto pos = find(v1.begin(), v1.end(), 4);
if (pos != v1.end())
{
v1.erase(pos);
}
(*pos)++;
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
void test_vector5()
{
bit::vector<int> v1;
v1.push_back(10);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
v1.push_back(50);
bit::vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
if (*it % 2 == 0)
{
it = v1.erase(it);
}
else
{
++it;
}
}
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
void test_vector6()
{
//vector<int> v(10u, 5);
vector<int> v1(10, 5);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
vector<int> v2(v1.begin()+1, v1.end()-1);
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
std::string s1("hello");
vector<int> v3(s1.begin(), s1.end());
for (auto e : v3)
{
cout << e << " ";
}
cout << endl;
int a[] = { 100, 10, 2, 20, 30 };
vector<int> v4(a, a+3);
for (auto e : v4)
{
cout << e << " ";
}
cout << endl;
v1.insert(v1.begin(), 10);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
sort(v1.begin(), v1.end());
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
for (auto e : a)
{
cout << e << " ";
}
cout << endl;
//sort(a, a+sizeof(a)/sizeof(int));
/* greater<int> g;
sort(a, a + sizeof(a) / sizeof(int), g);*/
sort(a, a + sizeof(a) / sizeof(int), greater<int>());
for (auto e : a)
{
cout << e << " ";
}
cout << endl;
}
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> vv;
vv.resize(numRows, vector<int>());
for (size_t i = 0; i < vv.size(); ++i)
{
vv[i].resize(i + 1, 0);
vv[i][0] = vv[i][vv[i].size() - 1] = 1;
}
for (size_t i = 0; i < vv.size(); ++i)
{
for (size_t j = 0; j < vv[i].size(); ++j)
{
if (vv[i][j] == 0)
{
vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
}
}
}
return vv;
}
};
void test_vector7()
{
vector<int> v1(10, 5);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
vector<int> v2(v1);
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
vector<std::string> v3(3, "111111111111111111111");
for (auto e : v3)
{
cout << e << " ";
}
cout << endl;
vector<std::string> v4(v3);
for (auto e : v4)
{
cout << e << " ";
}
cout << endl;
v4.push_back("2222222222222222222");
v4.push_back("2222222222222222222");
v4.push_back("2222222222222222222");
for (auto e : v4)
{
cout << e << " ";
}
cout << endl;
vector<vector<int>> ret = Solution().generate(5);
for (size_t i = 0; i < ret.size(); ++i)
{
for (size_t j = 0; j < ret[i].size(); ++j)
{
cout << ret[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
}