​ Vector容器的底层其实就是一个动态数组,内部设有几个数据成员,其中三个指针分别指向元素存储区的起始位置、最后一个有效元素之后的位置、内存即总容量的末尾,还会设有一个成员用于分配和释放对象。

​ 完整的vector容器较为庞大,包含构造/析构、赋值、容量管理、元素访问、迭代器、内存管理等一系列操作。鉴于苯人的水平,决定循序渐进,先写一个最小可用的试试看。

​ 主要先实现最基础的push_back与pop_back吧,这里需要注意的是vector的扩容机制,当你push一个元素进入vector时容量已满,它会以倍数的形式进行扩容,一般是2倍,这里苯人想起来push_back和emplace_back的区别但是暂时用不到就先不说了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#pragma once
#include <cstddef> // std::size_t
#include <stdexcept> // std::out_of_range
#include <iostream> // optional, for debug

namespace kiri { //声明命名空间

template<typename T>
class vector {
public:
//默认构造函数,初始化为空容器
vector()
: data_(nullptr), size_(0), capacity_(0) {}

//析构函数,释放分配的内存
~vector() {
delete[] data_; //释放动态数组
}
//向容器尾部添加一个元素
void push_back(const T& value) {
//容量已满则进行扩容
if (size_ == capacity_) {
reserve(capacity_ == 0 ? 1 : capacity_ * 2);
}
//加入新元素
data_[size_++] = value;
}
//移除尾部的一个元素(不回收内存)
void pop_back() {
if (size_ > 0) --size_;
}
//通过下标获取元素引用,由于不进行越界检查,非安全访问
T& operator[](std::size_t i) {
return data_[i];
}
//常量版本的非安全访问
const T& operator[](std::size_t i) const {
return data_[i];
}
//获取当前元素个数
std::size_t size() const {
return size_;
}
//获取当前容量的分配
std::size_t capacity() const {
return capacity_;
}
//清空所有元素但未释放内存
void clear() {
size_ = 0;
}

private:
//扩容函数:重新分配更大的空间并复制原有数据
void reserve(std::size_t new_cap) {
//分配新空间
T* new_data = new T[new_cap];
//拷贝旧数据
for (std::size_t i = 0; i < size_; ++i) {
new_data[i] = data_[i];
}
delete[] data_;
data_ = new_data;
capacity_ = new_cap;
}

private:
T* data_;//动态分配的数组
std::size_t size_;//当前元素个数
std::size_t capacity_;//当前容量
};

} // namespace kiri

✅ 正确扩容

✅ 存储泛型类型

✅ 支持 push_back/pop_back

✅ 满足绝大多数基本测试

测试代码,结果如下:

vector一代测试