手撕vector:从零实现一个C++动态数组

前言

在C++标准模板库(STL)中,vector是最常用的容器之一,它是一个动态数组,能够自动管理内存,支持随机访问。今天我们就来手把手实现一个简化版的vector,帮助初学者理解其内部原理。

基本结构

首先我们定义vector类的基本框架:

复制代码
namespace yzq {
    template<class T>
    class vector {
    public:
        typedef T* iterator; // 定义迭代器
        typedef const T* const_iterator;  // 常量迭代器
        
    private:
        iterator _start = nullptr;
        iterator _finish = nullptr;
        iterator _end_of_storage = nullptr;
    };
}

这里我们使用三个指针来管理动态数组:

  • _start:指向数组的起始位置

  • _finish:指向最后一个元素的下一个位置

  • _end_of_storage:指向分配内存的末尾

迭代器

迭代器让我们能够像使用指针一样遍历容器:

复制代码
iterator begin() {
    return _start;
}

iterator end() {
    return _finish;
}

const_iterator begin() const{
    return _start;
}

const_iterator end() const{
    return _finish;
}

内存管理

reserve函数

reserve函数用于预分配内存:

复制代码
// 开内存
void reserve(size_t n) {
    if (n > capacity()) {
        size_t old_size = size();
        T* tmp = new T[n];
        for (size_t i = 0; i < old_size; i++) {
            tmp[i] = _start[i];
        }
        delete[] _start;
        _start = tmp;
        _finish = tmp + old_size;
        _end_of_storage = tmp + n;
    }
}

构造函数

我们的vector支持多种构造方式:

默认构造

复制代码
// C++11支持的前置生成默认构造
vector() = default;

初始化构造

复制代码
// 初始化构造
vector(size_t n, const T& value = T()) 
    :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {
    reserve(n);
    for (size_t i = 0; i < n; i++) {
        _start[i] = value;
    }
    _finish = _start + n;
}

// 为整数类型提供特别的初始化构造,避免与通用区间构造混淆
vector(int n, int value = 0)
    :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {
    reserve(n);
    for (int i = 0; i < n; i++) {
        _start[i] = value;
    }
    _finish = _start + n;
}

区间构造

复制代码
// 区间构造(针对vector类型)
vector(const_iterator first, const_iterator last)
    :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {
    size_t n = last - first;
    reserve(n);
    for (size_t i = 0; i < n; i++) {
        _start[i] = first[i];
    }
    _finish = _start + n;
}

// 区间构造(通用版)
template<class InputIterator>
vector(InputIterator first, InputIterator last) 
    :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {
    while (first != last) {
        push_back(*first);
        first++;
    }
}

拷贝构造

复制代码
// 拷贝构造(深拷贝)
vector(const vector<T>& v)
    :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {
    reserve(v.capacity());
    size_t i;
    for (i = 0; i < v.size(); i++) {
        push_back(v[i]);
    }
}

基本操作

容量相关

复制代码
size_t size() const {
    return _finish - _start;
}

size_t capacity() const {
    return _end_of_storage - _start;
}

元素访问

复制代码
T& operator[](size_t i) {
    assert(i < size());
    return _start[i];
}

const T& operator[](size_t i) const {
    assert(i < size());
    return _start[i];
}

修改操作

尾插和尾删
复制代码
// 尾插
void push_back(const T& x) {
    // 满了先扩容
    if (_finish == _end_of_storage) {
        reserve(capacity() == 0 ? 4 : 2 * capacity()); // 2倍扩
    }
    *_finish = x;
    _finish++;
}

// 尾删
void pop_back() {
    assert(size() > 0);
    _finish--;
}
插入和删除
复制代码
// 插入
iterator insert(iterator pos, const T& x) {
    assert(pos >= _start && pos <= _finish);
    // 满了先扩容
    if (_finish == _end_of_storage) {
        size_t len = pos - _start; // 先计算原来的位置
        reserve(capacity() == 0 ? 4 : 2 * capacity()); // 2倍扩
        pos = _start + len; // 更新pos,避免迭代器失效
    }
    // 挪动数据
    iterator end = _finish;
    while (end > pos) {
        *(end) = *(end - 1);
        end--;
    }
    *pos = x;
    _finish++;
    return pos;
}

// 指定位置删除
iterator erase(iterator pos) {
    assert(pos >= _start && pos < _finish);
    // 挪动数据
    iterator start = pos;
    while (start < _finish - 1) {
        *(start) = *(start + 1);
        start++;
    }
    _finish--;
    return pos;
}
调整大小
复制代码
void resize(size_t n, const T& value = T()) {
    // 小于当前元素个数即为删除元素,大于则添加元素
    if (n < size()) {
        _finish = _start + n;
    }
    else {
        reserve(n);
        while (_finish < _start + n) {
            *_finish = value;
            _finish++;
        }
    }
}

其他操作

复制代码
// 清除数据
void clear() {
    _finish = _start;
}

// 交换
void swap(vector<T>& v) {
    std::swap(_start, v._start);
    std::swap(_finish, v._finish);
    std::swap(_end_of_storage,v._end_of_storage);
}

// 现代写法(注意一定得是传值构造)
vector<T>& operator=(vector<T> v) {
    swap(v);
    return *this;
}

工具函数

复制代码
// 打印vector
void print_vector() const {
    auto it = begin();
    while (it != end()) {
        std::cout << *it << " ";
        it++;
    }
    std::cout << std::endl;
}

析构函数

复制代码
// 析构
~vector() {
    delete[] _start;
}

测试代码

复制代码
void TestVector() {
    yzq::vector<int> a;
    a.push_back(1);
    a.push_back(2);
    a.push_back(3);
    a.push_back(4);
    a.push_back(5);
    a.print_vector();
    
    for (int i = 0; i < a.size(); i++) {
        cout << a[i] << " ";
    }
    cout << endl;
    
    yzq::vector<int> b(a.begin()+1, a.end());  // 区间拷贝
    b.print_vector();
    
    yzq::vector<int> c(b); // 深度拷贝
    c.print_vector();
    
    a.resize(1);
    a.print_vector();
    a.resize(10, 2);
    a.print_vector();
}

int main() {
    TestVector();
}
相关推荐
好好研究2 分钟前
SpringMVC框架 - 异常处理
java·开发语言·spring·mvc
只会写代码2 分钟前
JDK8 Lambda 加持:打造优雅通用的对象构建器
java
songroom11 分钟前
Rust: 量化策略回测与简易线程池构建(MPMC)
开发语言·后端·rust
CS_浮鱼12 分钟前
【Linux编程】线程同步与互斥
linux·网络·c++
摇滚侠17 分钟前
Vue 项目实战《尚医通》,完成确定挂号业务,笔记46
java·开发语言·javascript·vue.js·笔记
正在走向自律25 分钟前
豆包编程模型Doubao-Seed-Code深度体验,从零开始构建全栈项目的完整指南
java·服务器·数据库·doubao·claude code·火山方舟
钱多多_qdd29 分钟前
基础篇:IoC(九):应用上下文ApplicationContext
java·spring
十五年专注C++开发32 分钟前
libdatrie: 一个高效的 基于双数组字典树(Double-Array Trie)的C语言函数库
c语言·开发语言·trie
q***558942 分钟前
SpringSecurity 实现token 认证
java
合作小小程序员小小店42 分钟前
web网页开发,在线%医院诊断管理%系统,基于Idea,html,css,jQuery,java,jsp,ssh,mysql。
java·前端·css·数据库·jdk·html·intellij-idea