vector 是动态连续数组,底层基于原生指针实现,是 STL 中最常用的序列式容器,核心特性:

  1. 内存连续分配,支持随机访问([]/at 访问效率 O (1));
  2. 迭代器本质是原生指针(T*),指针天然满足 STL 迭代器的所有要求;
  3. 核心成员是三个原生指针,所有操作都围绕这三个指针展开,这是 vector 源码的核心骨架;
  4. 存在容量 (capacity)大小 (size) 分离的设计,当插入元素导致 size == capacity 时触发扩容,扩容是 vector 性能损耗的核心点;
  5. SGI STL(工业界主流实现,gcc/libstdc++ 采用)的核心设计思想:内存分配 和 对象构造分离内存释放 和 对象析构分离,这是所有 STL 容器的通用内存管理范式。

三个版本

  • 版本一:原生朴素版(三指针裸版) - 无任何优化,纯基础实现,vector 的本源,也是你之前看过的核心版本;
  • 版本二:写时拷贝版(COW 版,Copy-On-Write) - 基于版本一的性能优化,引用计数 + 共享内存,90 年代~gcc5.0 的主流版本;
  • 版本三:小对象优化版(SBO 版,Small Buffer Optimization) - 工业级终极版本,当前 gcc/clang/msvc 默认实现,无任何致命缺陷,兼顾极致性能 + 安全性,生产环境唯一使用版本。

版本一:原生朴素版(三指针裸版 / 基础版)

核心特征

  • 底层纯 3 个原生指针,无任何额外成员变量,vector 的最基础形态;
  • 严格深拷贝:拷贝构造 / 赋值运算符都会完整拷贝堆内存的所有元素,新对象独占内存;
  • 无任何优化,逻辑极简,无坑点,是理解 vector 的基石;
  • 所有操作都是「直来直去」,迭代器失效规则最简单。

完整可编译源码

#include <memory>
#include <algorithm>
#include <stdexcept>

template <typename T, typename Alloc = std::allocator<T>>
class vector_v1 { // v1: 原生朴素版
public:
    // STL标准类型别名
    using value_type      = T;
    using pointer         = T*;
    using const_pointer   = const T*;
    using reference       = T&;
    using const_reference = const T&;
    using iterator        = T*;
    using const_iterator  = const T*;
    using size_type       = size_t;
    using difference_type = ptrdiff_t;

protected:
    // ========== 核心成员:纯三指针,无任何多余变量 ==========
    iterator start;        // 指向内存中第一个有效元素
    iterator finish;       // 指向内存中最后一个有效元素的下一位
    iterator end_of_storage;//指向内存容量的最后一个位置的下一位
    using data_allocator = typename Alloc::template rebind<T>::other;

    // 内存工具:分配/释放/构造/析构 分离
    pointer allocate(size_type n) { return n ? data_allocator::allocate(n) : nullptr; }
    void deallocate(pointer p, size_type n) { if (p) data_allocator::deallocate(p, n); }
    void construct(pointer p, const T& val) { data_allocator::construct(p, val); }
    void destroy(pointer p) { data_allocator::destroy(p); }
    void destroy(iterator first, iterator last) { for (; first != last; ++first) destroy(first); }

public:
    // 构造函数
    vector_v1() noexcept : start(nullptr), finish(nullptr), end_of_storage(nullptr) {}
    explicit vector_v1(size_type n, const T& val = T{}) {
        start = allocate(n);
        end_of_storage = start + n;
        finish = std::uninitialized_fill_n(start, n, val);
    }
    template <typename InputIter>
    vector_v1(InputIter first, InputIter last) {
        size_type n = std::distance(first, last);
        start = allocate(n);
        end_of_storage = start + n;
        finish = std::uninitialized_copy(first, last, start);
    }
    // 深拷贝构造
    vector_v1(const vector_v1& rhs) {
        start = allocate(rhs.capacity());
        end_of_storage = start + rhs.capacity();
        finish = std::uninitialized_copy(rhs.start, rhs.finish, start);
    }
    // 深拷贝赋值
    vector_v1& operator=(const vector_v1& rhs) {
        if (this != &rhs) {
            destroy(start, finish);
            deallocate(start, capacity());
            start = allocate(rhs.capacity());
            end_of_storage = start + rhs.capacity();
            finish = std::uninitialized_copy(rhs.start, rhs.finish, start);
        }
        return *this;
    }
    // 析构函数
    ~vector_v1() { destroy(start, finish); deallocate(start, capacity()); }

    // 迭代器
    iterator begin() noexcept { return start; }
    const_iterator begin() const noexcept { return start; }
    iterator end() noexcept { return finish; }
    const_iterator end() const noexcept { return finish; }

    // 容量/大小
    size_type size() const noexcept { return finish - start; }
    size_type capacity() const noexcept { return end_of_storage - start; }
    bool empty() const noexcept { return start == finish; }

    // 扩容:仅扩容容量,不改变元素个数
    void reserve(size_type n) {
        if (n > capacity()) {
            size_type old_size = size();
            pointer tmp = allocate(n);
            finish = std::uninitialized_move(start, finish, tmp);
            destroy(start, finish);
            deallocate(start, capacity());
            start = tmp;
            end_of_storage = tmp + n;
        }
    }

    // 元素访问
    reference operator[](size_type n) noexcept { return start[n]; }
    const_reference operator[](size_type n) const noexcept { return start[n]; }
    reference at(size_type n) {
        if (n >= size()) throw std::out_of_range("vector_v1::at out of range");
        return start[n];
    }
    reference front() noexcept { return *start; }
    const_reference front() const noexcept { return *start; }
    reference back() noexcept { return *(finish - 1); }
    const_reference back() const noexcept { return *(finish - 1); }

    // 核心操作
    void push_back(const T& val) {
        if (finish == end_of_storage) reserve(capacity() ? 2 * capacity() : 1);
        construct(finish++, val);
    }
    void pop_back() noexcept { if (!empty()) destroy(--finish); }
    iterator erase(iterator pos) noexcept {
        if (pos + 1 != end()) std::copy(pos+1, finish, pos);
        destroy(--finish);
        return pos;
    }
    void clear() noexcept { destroy(start, finish); finish = start; }
};

版本二:写时拷贝版(COW 版 / 引用计数版,Copy-On-Write)

核心特征

  • 基于版本一的三指针模型改造,核心优化:共享内存 + 引用计数
  • 核心思想:读共享、写拷贝,拷贝构造 / 赋值运算符是「浅拷贝」,只拷贝指针,引用计数 + 1,时间复杂度 O (1),解决版本一深拷贝的性能痛点;
  • 引用计数存储在堆内存的最起始位置(所有共享对象共用同一个引用计数);
  • 所有写操作(push_back/pop_back/erase/operator [] 修改)前必须做 unshare() 检查:如果引用计数 > 1,说明有其他对象共享内存,此时触发「深拷贝」,独占内存后再写;读操作无拷贝;
  • 析构逻辑:引用计数 - 1,只有计数减到 0 时,才真正释放堆内存
  • ✘ 致命缺陷:线程不安全、写操作有隐性延迟拷贝开销、迭代器失效规则复杂 → C++11 废弃,gcc5.0 后彻底移除

完整可编译源码

#include <memory>
#include <algorithm>
#include <stdexcept>
#include <atomic>

template <typename T, typename Alloc = std::allocator<T>>
class vector_v2 { // v2: 写时拷贝版 COW (Copy-On-Write)
public:
    using value_type      = T;
    using pointer         = T*;
    using const_pointer   = const T*;
    using reference       = T&;
    using const_reference = const T&;
    using iterator        = T*;
    using const_iterator  = const T*;
    using size_type       = size_t;
    using difference_type = ptrdiff_t;

protected:
    // ========== COW核心改造:堆内存首地址存【引用计数】,后接数据区 ==========
    // 内存布局:[ refcount (引用计数) | start -> 数据区 | finish | end_of_storage ]
    iterator start;
    iterator finish;
    iterator end_of_storage;
    using data_allocator = typename Alloc::template rebind<T>::other;
    using refcount_alloc = typename Alloc::template rebind<size_type>::other;

    // 【核心】获取引用计数的指针(堆内存首地址)
    size_type* get_refcount() const noexcept { return reinterpret_cast<size_type*>(start) - 1; }
    // 分配内存:先分配1个引用计数的空间,再分配n个元素的空间
    pointer allocate_with_ref(size_type n) {
        if (n == 0) return nullptr;
        size_type* ref = refcount_alloc::allocate(1);
        *ref = 1; // 初始引用计数=1
        return reinterpret_cast<pointer>(ref + 1); // 跳过引用计数,返回数据区首地址
    }
    // 释放内存:先释放引用计数,再释放数据区
    void deallocate_with_ref(pointer p, size_type n) {
        if (p) {
            size_type* ref = get_refcount();
            refcount_alloc::deallocate(ref, 1);
            data_allocator::deallocate(p, n);
        }
    }
    void construct(pointer p, const T& val) { data_allocator::construct(p, val); }
    void destroy(pointer p) { data_allocator::destroy(p); }
    void destroy(iterator first, iterator last) { for (; first != last; ++first) destroy(first); }

    // ========== COW核心函数:unshare 写时分离 ==========
    // 写操作前必须调用,确保当前对象独占内存
    void unshare(size_type need = 0) {
        size_type* ref = get_refcount();
        // 引用计数>1 → 有其他对象共享,需要深拷贝独占内存
        if (*ref > 1) {
            size_type old_size = size();
            size_type new_cap = std::max(need, capacity());
            pointer new_start = allocate_with_ref(new_cap);
            std::uninitialized_move(start, finish, new_start);
            
            // 原内存的引用计数-1,若为0则释放
            if (--(*ref) == 0) deallocate_with_ref(start, capacity());
            
            // 更新指针,指向新的独占内存
            start = new_start;
            finish = new_start + old_size;
            end_of_storage = new_start + new_cap;
        }
    }

public:
    // 构造函数
    vector_v2() noexcept : start(nullptr), finish(nullptr), end_of_storage(nullptr) {}
    explicit vector_v2(size_type n, const T& val = T{}) {
        start = allocate_with_ref(n);
        end_of_storage = start + n;
        finish = std::uninitialized_fill_n(start, n, val);
    }
    template <typename InputIter>
    vector_v2(InputIter first, InputIter last) {
        size_type n = std::distance(first, last);
        start = allocate_with_ref(n);
        end_of_storage = start + n;
        finish = std::uninitialized_copy(first, last, start);
    }
    // 【核心】COW拷贝构造:浅拷贝指针,引用计数+1 → O(1)时间
    vector_v2(const vector_v2& rhs) noexcept {
        if (rhs.empty()) { start = finish = end_of_storage = nullptr; return; }
        start = rhs.start;
        finish = rhs.finish;
        end_of_storage = rhs.end_of_storage;
        ++(*get_refcount()); // 引用计数+1
    }
    // 【核心】COW赋值运算符:浅拷贝指针,引用计数+1 → O(1)时间
    vector_v2& operator=(const vector_v2& rhs) noexcept {
        if (this != &rhs) {
            // 释放当前对象的引用
            if (!empty() && --(*get_refcount()) == 0) deallocate_with_ref(start, capacity());
            // 浅拷贝
            start = rhs.start;
            finish = rhs.finish;
            end_of_storage = rhs.end_of_storage;
            if (!empty()) ++(*get_refcount());
        }
        return *this;
    }
    // 析构函数:引用计数-1,只有0时才释放内存
    ~vector_v2() {
        if (!empty() && --(*get_refcount()) == 0) deallocate_with_ref(start, capacity());
    }

    // 迭代器:const迭代器只读,无需unshare;非const迭代器可能写,需unshare
    const_iterator begin() const noexcept { return start; }
    const_iterator end() const noexcept { return finish; }
    iterator begin() noexcept { unshare(); return start; }
    iterator end() noexcept { unshare(); return finish; }

    // 容量/大小:只读操作,无需unshare
    size_type size() const noexcept { return finish - start; }
    size_type capacity() const noexcept { return end_of_storage - start; }
    bool empty() const noexcept { return start == finish; }

    // 元素访问:const是读操作,无拷贝;非const是写操作,必须unshare
    const_reference operator[](size_type n) const noexcept { return start[n]; }
    reference operator[](size_type n) noexcept { unshare(); return start[n]; }
    const_reference front() const noexcept { return *start; }
    reference front() noexcept { unshare(); return *start; }
    const_reference back() const noexcept { return *(finish - 1); }
    reference back() noexcept { unshare(); return *(finish - 1); }

    // ========== 所有写操作:必须先unshare ==========
    void push_back(const T& val) {
        if (empty()) reserve(1);
        if (finish == end_of_storage) unshare(2 * capacity());
        construct(finish++, val);
    }
    void pop_back() noexcept {
        if (!empty()) { unshare(); destroy(--finish); }
    }
    iterator erase(iterator pos) noexcept {
        if (empty() || pos >= finish) return finish;
        unshare();
        if (pos + 1 != end()) std::copy(pos+1, finish, pos);
        destroy(--finish);
        return pos;
    }
    void clear() noexcept {
        if (!empty()) { unshare(); destroy(start, finish); finish = start; }
    }
    void reserve(size_type n) {
        if (n > capacity()) unshare(n);
    }
};

版本三:小对象优化版(SBO 版,Small Buffer Optimization)

核心特征

  • 当前所有编译器(gcc/clang/msvc)的默认实现,生产环境唯一使用版本,无任何致命缺陷;
  • 核心优化:栈上内置缓冲区 + 堆内存动态切换,完美解决版本一的「小对象堆分配开销」和版本二的「线程安全坑」;
  • 内存布局:用联合体 (union) 做内存复用(栈 / 堆模式互斥),无内存浪费,极致紧凑;
  • 核心原则:小数据走栈、大数据走堆:当元素个数 ≤ 内置缓冲区大小(工业级标准16),直接存在对象自身的栈内存中,无 malloc/free 开销,极致快;超过则自动切换为版本一的堆内存模式,完全兼容;
  • 回归值语义 + 深拷贝,无引用计数,天然线程安全,迭代器失效规则和版本一一致,逻辑简单;
  • 对外接口和版本一完全一致,调用无感知,性能碾压版本一 / 二,是 vector 的最优解。

补充:string 的该版本叫 SSO (Small String Optimization),本质和 SBO 是同一个东西,只是命名不同。

完整可编译源码

#include <memory>
#include <algorithm>
#include <stdexcept>
#include <cstring>

template <typename T, typename Alloc = std::allocator<T>, size_type SBO_SIZE = 16>
class vector_v3 { // v3: 小对象优化版 SBO (Small Buffer Optimization) 【工业级终极版】
public:
    using value_type      = T;
    using pointer         = T*;
    using const_pointer   = const T*;
    using reference       = T&;
    using const_reference = const T&;
    using iterator        = T*;
    using const_iterator  = const T*;
    using size_type       = size_t;
    using difference_type = ptrdiff_t;
    static constexpr size_type SBO_BUFFER_SIZE = SBO_SIZE; // 工业级标准:16个元素

protected:
    // ========== SBO核心:联合体 内存复用(栈/堆模式互斥) ==========
    // 状态标记:栈模式(small) / 堆模式(large)
    enum class Mode { Small, Large };
    Mode mode;

    // 堆模式:复用版本一的三指针
    struct HeapData {
        iterator start;
        iterator finish;
        iterator end_of_storage;
    };

    // 栈模式:内置缓冲区,栈内存存储,无堆分配
    struct StackData {
        T buf[SBO_BUFFER_SIZE]; // 栈上缓冲区,存小对象
        size_type size_;         // 栈模式下的元素个数
    };

    // 联合体:栈/堆模式二选一,内存复用,无浪费
    union Data {
        HeapData heap;
        StackData stack;
        Data() {}
        ~Data() {} // 联合体析构手动控制
    } data;

    using data_allocator = typename Alloc::template rebind<T>::other;
    // 内存工具:分配/释放/构造/析构 分离
    pointer allocate(size_type n) { return n ? data_allocator::allocate(n) : nullptr; }
    void deallocate(pointer p, size_type n) { if (p) data_allocator::deallocate(p, n); }
    void construct(pointer p, const T& val) { data_allocator::construct(p, val); }
    void destroy(pointer p) { data_allocator::destroy(p); }
    void destroy(iterator first, iterator last) { for (; first != last; ++first) destroy(first); }

    // 核心:判断当前模式
    bool is_small() const noexcept { return mode == Mode::Small; }
    bool is_large() const noexcept { return mode == Mode::Large; }

    // 统一的指针访问接口:对上层透明,不用关心栈/堆
    iterator get_start() noexcept {
        return is_small() ? data.stack.buf : data.heap.start;
    }
    iterator get_finish() noexcept {
        return is_small() ? data.stack.buf + data.stack.size_ : data.heap.finish;
    }
    iterator get_end_storage() noexcept {
        return is_small() ? data.stack.buf + SBO_BUFFER_SIZE : data.heap.end_of_storage;
    }
    const_iterator get_start() const noexcept {
        return is_small() ? data.stack.buf : data.heap.start;
    }
    const_iterator get_finish() const noexcept {
        return is_small() ? data.stack.buf + data.stack.size_ : data.heap.finish;
    }
    const_iterator get_end_storage() const noexcept {
        return is_small() ? data.stack.buf + SBO_BUFFER_SIZE : data.heap.end_of_storage;
    }

    // 扩容核心:栈→堆 切换 / 堆扩容
    void reallocate(size_type new_cap) {
        size_type old_size = size();
        T* old_start = get_start();
        // 分配新内存(堆)
        T* new_start = allocate(new_cap);
        T* new_finish = std::uninitialized_move(old_start, old_start + old_size, new_start);
        // 清理旧内存
        if (is_large()) {
            destroy(data.heap.start, data.heap.finish);
            deallocate(data.heap.start, data.heap.end_of_storage - data.heap.start);
        } else {
            destroy(data.stack.buf, data.stack.buf + old_size);
        }
        // 切换为堆模式,更新指针
        mode = Mode::Large;
        data.heap.start = new_start;
        data.heap.finish = new_finish;
        data.heap.end_of_storage = new_start + new_cap;
    }

public:
    // 构造函数:默认初始化为栈模式,空容器
    vector_v3() noexcept : mode(Mode::Small) { data.stack.size_ = 0; }
    explicit vector_v3(size_type n, const T& val = T{}) : mode(Mode::Small) {
        if (n <= SBO_BUFFER_SIZE) { // 小对象:栈模式
            data.stack.size_ = n;
            std::uninitialized_fill_n(data.stack.buf, n, val);
        } else { // 大对象:堆模式
            reallocate(n);
            std::uninitialized_fill_n(data.heap.start, n, val);
            data.heap.finish = data.heap.start + n;
        }
    }
    template <typename InputIter>
    vector_v3(InputIter first, InputIter last) : mode(Mode::Small) {
        size_type n = std::distance(first, last);
        if (n <= SBO_BUFFER_SIZE) {
            data.stack.size_ = n;
            std::uninitialized_copy(first, last, data.stack.buf);
        } else {
            reallocate(n);
            std::uninitialized_copy(first, last, data.heap.start);
            data.heap.finish = data.heap.start + n;
        }
    }
    // 深拷贝构造:栈/堆模式都完整拷贝,值语义
    vector_v3(const vector_v3& rhs) : mode(rhs.mode) {
        if (rhs.is_small()) {
            data.stack.size_ = rhs.data.stack.size_;
            std::uninitialized_copy(rhs.data.stack.buf, rhs.data.stack.buf + rhs.size(), data.stack.buf);
        } else {
            data.heap.start = allocate(rhs.capacity());
            data.heap.end_of_storage = data.heap.start + rhs.capacity();
            data.heap.finish = std::uninitialized_copy(rhs.data.heap.start, rhs.data.heap.finish, data.heap.start);
        }
    }
    // 深拷贝赋值
    vector_v3& operator=(const vector_v3& rhs) {
        if (this != &rhs) {
            clear();
            if (rhs.is_small()) {
                mode = Mode::Small;
                data.stack.size_ = rhs.size();
                std::uninitialized_copy(rhs.data.stack.buf, rhs.data.stack.buf + rhs.size(), data.stack.buf);
            } else {
                reallocate(rhs.capacity());
                std::uninitialized_copy(rhs.data.heap.start, rhs.data.heap.finish, data.heap.start);
                data.heap.finish = data.heap.start + rhs.size();
            }
        }
        return *this;
    }
    // 析构函数:手动控制栈/堆的析构逻辑
    ~vector_v3() { clear(); }

    // 迭代器:统一接口,透明访问
    iterator begin() noexcept { return get_start(); }
    const_iterator begin() const noexcept { return get_start(); }
    iterator end() noexcept { return get_finish(); }
    const_iterator end() const noexcept { return get_finish(); }

    // 容量/大小:统一计算,无感知
    size_type size() const noexcept { return get_finish() - get_start(); }
    size_type capacity() const noexcept { return get_end_storage() - get_start(); }
    bool empty() const noexcept { return size() == 0; }

    // 元素访问:统一接口,极致性能
    reference operator[](size_type n) noexcept { return get_start()[n]; }
    const_reference operator[](size_type n) const noexcept { return get_start()[n]; }
    reference front() noexcept { return *get_start(); }
    const_reference front() const noexcept { return *get_start(); }
    reference back() noexcept { return *(get_finish() - 1); }
    const_reference back() const noexcept { return *(get_finish() - 1); }

    // 核心操作:栈/堆自动切换,性能拉满
    void push_back(const T& val) {
        if (size() >= capacity()) reallocate(capacity() ? 2 * capacity() : 1);
        construct(get_finish(), val);
        if (is_small()) data.stack.size_++;
        else data.heap.finish++;
    }
    void pop_back() noexcept {
        if (!empty()) {
            if (is_small()) destroy(data.stack.buf + --data.stack.size_);
            else destroy(--data.heap.finish);
        }
    }
    iterator erase(iterator pos) noexcept {
        if (empty() || pos >= end()) return end();
        if (pos + 1 != end()) std::copy(pos+1, end(), pos);
        pop_back();
        return pos;
    }
    void clear() noexcept {
        if (is_small()) {
            destroy(data.stack.buf, data.stack.buf + data.stack.size_);
            data.stack.size_ = 0;
        } else {
            destroy(data.heap.start, data.heap.finish);
            deallocate(data.heap.start, data.heap.end_of_storage - data.heap.start);
            mode = Mode::Small;
            data.stack.size_ = 0;
        }
    }
    void reserve(size_type n) { if (n > capacity()) reallocate(n); }
};
  • vector 的三个版本演进,本质是 「性能与安全性的权衡」:版本一追求安全但性能差,版本二追求性能但牺牲安全,版本三兼顾性能与安全,是终极解;
  • string 和 vector 完全同源,三个版本的实现逻辑、演进过程、优缺点完全一致,只是 string 的 SBO 叫 SSO,缓冲区存 char;
  • 三个版本的对外接口完全一致,替换使用时无需修改任何调用代码,这是 STL 的封装精髓。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐