手撕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();
}
相关推荐
十八旬7 小时前
RuoYi-Vue3项目定制修改全攻略
java·windows
任风雨7 小时前
3.1.1.Java基础知识
java·开发语言
froginwe117 小时前
CSS3 框大小:深入解析与优化技巧
开发语言
兜兜风d'7 小时前
RabbitMQ死信队列详解
c++·rabbitmq·java-rabbitmq
脸大是真的好~7 小时前
黑马JAVA+AI 加强03-集合-Collection-List和Set集合-迭代器(Iterator)遍历-并发修改异常
java
C_Liu_7 小时前
12.C++:模版进阶
开发语言·c++
cj6341181508 小时前
DBeaver连接本地MySQL、创建数据库表的基础操作
java·后端
sali-tec8 小时前
C# 基于halcon的视觉工作流-章54-N点标定
开发语言·图像处理·算法·计算机视觉·c#
娇娇yyyyyy8 小时前
C++11新特性基础知识点汇总
开发语言·c++·算法