手撕STL Vector容器:深入理解动态数组的实现

前言

在C++标准模板库(STL)中,vector是最常用且功能强大的容器之一。它提供了动态数组的功能,能够自动管理内存,支持随机访问,并在尾部高效地插入和删除元素。本文将深入探讨如何手动实现一个简化版的vector容器,并分析其中的关键技术和设计思路。

Vector的基本结构

cpp

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; // 分配内存的末尾
};

Vector使用三个指针来管理动态数组:

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

  • _finish: 指向最后一个元素的下一个位置(即当前元素数量)

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

构造函数与析构函数

默认构造函数

cpp

vector() = default;  // C++11特性,使用编译器生成的默认构造函数

迭代器范围构造函数

cpp

template<class InputIterator>
vector(InputIterator first, InputIterator end)
{
    while (first != end)
    {
        push_back(*first);
        first++;
    }
}

这个模板构造函数允许从任何迭代器范围初始化vector,提供了极大的灵活性。

特定数量元素构造函数

cpp

vector(size_t n, const T& val = T())
{
    reserve(n);
    for (size_t i = 0; i < n; i++)
    {
        push_back(val);
    }
}

为了避免与迭代器构造函数冲突,需要为不同整数类型提供重载版本(int、long、long long等)。

拷贝构造函数与赋值运算符

cpp

vector(const vector<T>& v)
{
    reserve(v.size());
    for (auto& e : v)
    {
        push_back(e);
    }
}

vector<T>& operator=(const vector<T>& v)
{
    if (this != &v)  // 防止自赋值
    {
        clear();
        reserve(v.size());
        for (auto& e : v)
        {
            push_back(e);
        }
    }
    return *this;
}

析构函数

cpp

~vector()
{
    delete[] _start;
    _start = _finish = _end_of_storage = nullptr;
}

核心功能实现

内存管理:reserve()

cpp

void reserve(size_t n)
{
    if (n > capacity())
    {
        T* temp = new T[n];
        const size_t old_size = size();
        
        // 深拷贝元素
        for (size_t i = 0; i < old_size; i++)
        {
            temp[i] = _start[i];
        }
        
        delete[] _start;
        _start = temp;
        _finish = temp + old_size;
        _end_of_storage = temp + n;
    }
}

关键点:不能使用memcpy进行浅拷贝,必须对每个元素进行深拷贝,特别是对于自定义类型(如string)。

元素访问

cpp

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

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

迭代器

cpp

iterator begin() { return _start; }
iterator end() { return _finish; }
const_iterator begin() const { return _start; }
const_iterator end() const { return _finish; }

插入操作

cpp

iterator insert(iterator pos, const T& x)
{
    assert(pos >= _start);
    assert(pos <= _finish);
    
    if (_finish == _end_of_storage)
    {
        size_t len = pos - _start;  // 保存相对位置
        reserve(capacity() == 0 ? 4 : 2 * capacity());
        pos = _start + len;         // 更新pos位置
    }
    
    // 向后移动元素
    iterator end = _finish - 1;
    while (end >= pos)
    {
        *(end + 1) = *end;
        end--;
    }
    
    *pos = x;
    _finish++;
    return pos;
}

void push_back(const T& v)
{
    if (_finish == _end_of_storage)
    {
        reserve(capacity() == 0 ? 4 : 2 * capacity());
    }
    *_finish = v;
    _finish++;
}

删除操作

cpp

void erase(iterator pos)
{
    assert(pos < _finish);
    assert(pos >= _start);
    
    iterator it = pos + 1;
    while (it != end())
    {
        *(it - 1) = *it;
        it++;
    }
    _finish--;
}

void pop_back()
{
    assert(!empty());
    _finish--;
}

大小调整:resize()

cpp

void resize(size_t n, const T& t = T())
{
    if (n < size())
    {
        _finish = _start + n;  // 截断
    }
    else
    {
        reserve(n);
        while (_finish != _start + n)
        {
            *(_finish++) = t;  // 填充新元素
        }
    }
}

关键技术与注意事项

  1. 模板实现:模板类必须在头文件中完整实现,不能将声明与定义分离,否则会导致链接错误。

  2. 深拷贝问题:在reserve()中必须使用元素级别的拷贝而非memcpy,确保自定义类型正确拷贝。

  3. 迭代器失效:插入或删除操作可能导致迭代器失效,需要在实现中特别注意。

  4. 异常安全:在实际生产代码中需要考虑异常安全性,确保资源不会泄漏。

  5. 类型萃取:标准库实现中使用类型萃取技术优化拷贝操作,对POD类型使用memcpy。

测试示例

cpp

void test1()
{
    vector<string> v1;
    v1.push_back("1");
    v1.push_back("2");
    v1.push_back("3");
    v1.push_back("4");
    v1.push_back("5");
    Print_Vector(v1);
}

总结

通过手动实现vector容器,我们深入理解了动态数组的内部工作机制,包括:

  • 内存管理与自动扩容

  • 迭代器设计与实现

  • 元素访问与修改

  • 插入删除操作的时间复杂度分析

  • 深拷贝与浅拷贝的区别

这种实现虽然简化,但涵盖了vector的核心功能,对于理解STL容器的设计思想和内部机制非常有帮助。在实际开发中,建议直接使用标准库的vector,它经过充分优化和测试,具有更好的性能和稳定性。

理解底层实现的价值在于能够更有效地使用标准库,并在需要时能够扩展或定制容器行为。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值