【C++】Vector核心实现:类设计到迭代器陷阱

vector 模拟实现代码的核心

下面从类设计、核心接口、内存安全、常见陷阱、测试场景5 个维度,提炼需重点掌握的知识点,覆盖面试高频考点与实践易错点:

一、类结构与成员变量(基础框架)

vector 的核心是通过三个迭代器(指针) 管理动态内存,这是理解所有接口的前提:

  • _start:指向动态数组的起始位置(首元素)

  • _finish:指向动态数组中已使用元素的下一个位置(size = _finish - _start

  • _end_of_storage:指向动态数组的末尾位置(capacity = _end_of_storage - _start

  • 成员变量用C++11 类内初始值(= nullptr)初始化,因此默认构造可空实现(vector() {}),简化代码。

二、构造函数设计(重载与模板陷阱)

构造函数是 vector 的 "入口",需重点关注重载匹配问题与通用性:

  1. 避免模板构造函数的匹配陷阱

代码中提供了两个n, val构造的重载:

cpp 复制代码
vector(size_t n, const T& val = T()); // 1. size_t版本
vector(int n, const T& val = T());    // 2. int版本
为什么需要int版本?(没懂)

若只写size_t n版本,当用户传入int类型的n(如vector<int> v(10, 5),10 是int),编译器会优先匹配模板迭代器构造(vector(InputIterator first, InputIterator last))------ 因为int可被推导为InputIterator类型,导致将105当作 "迭代器范围",而int不是迭代器,直接编译报错。

注:

  • 在 C++ 中,像 105 这样的整数字面量,默认类型是 int(有符号整型),而不是 size_t(无符号整型,通常是 unsigned intunsigned long long,取决于平台)。

  • 只有显式写 10u(加 u 表示无符号)或 size_t(10),才会被视为 size_t 类型。

  • 若只有 size_t 版本,编译器会优先选择模板迭代器构造函数(精确匹配),但 int 不是迭代器,导致报错;

  • 加个 int 版本后,它会作为 "非模板的精确匹配" 优先被选中,确保调用的是 "n 个 val 初始化" 的正确逻辑。

结论:必须重载int n版本,避免模板构造函数 "抢错" 匹配。

  1. 模板迭代器构造(通用性关键)
cpp 复制代码
template <class InputIterator>
vector(InputIterator first, InputIterator last);
  • 作用:支持从任意 "迭代器范围"([first, last))初始化,如string的迭代器、原生数组指针、其他容器的迭代器。

  • 示例:

cpp 复制代码
std::string s1("hello");
vector<int> v3(s1.begin(), s1.end()); // char转int,存储ASCII值
int a[] = {100,10,2};
vector<int> v4(a, a+3); // 原生数组指针作迭代器
  • 注意:迭代器类型需支持*解引用和++自增(符合 InputIterator 概念)。
  1. 拷贝构造(深拷贝的正确实现)

代码中拷贝构造的核心是避免浅拷贝:

cpp 复制代码
vector(const vector<T>& v) {
    _start = new T[v.capacity()]; // 开和原对象capacity相同的空间
    // 循环赋值(深拷贝),而非memcpy(浅拷贝)
    for (size_t i = 0; i < v.size(); ++i) {
        _start[i] = v._start[i]; 
    }
    _finish = _start + v.size();
    _end_of_storage = _start + v.capacity();
}
关键对比:为什么不用memcpy
  • memcpy是字节级拷贝(浅拷贝),仅适用于 POD 类型(如intdouble);

  • T是复杂类型(如string、自定义类),memcpy会导致两个对象的成员指针指向同一块内存,析构时重复释放(崩溃);

  • 循环赋值会调用T的赋值运算符(如string::operator=),完成深拷贝(为新string开辟独立内存)。

结论:动态数组存储非 POD 类型时,必须用循环赋值实现深拷贝。

三、核心接口实现(易错点与设计思路)

vector 的核心接口(reserve/resize/insert/erase)是面试高频考点,需关注功能差异、迭代器失效、扩容策略。

  1. reserve vs resize(最易混淆的接口)

两者均可能扩容,但核心作用完全不同:

|----------------|----------------|----------------------------|-----------------------------|------------------------------|
| 接口 | 作用 | 对 size 的影响 | 对 capacity 的影响 | 元素初始化 |
| reserve(n) | 仅保证 capacity≥n | 不改变 | 若 n > 当前 capacity 则扩容,否则不做 | 不初始化新元素 |
| resize(n, val) | 保证 size=n | 改变(n<size 截断,n>size 补元素) | 若 n > 当前 capacity 则扩容,否则不做 | n>size 时,新增元素用val初始化(默认T()) |

示例:

cpp 复制代码
v1.resize(10); // size从5→10,新增5个元素(int()=0),capacity若不足则扩容
v1.resize(3);  // size从10→3,仅移动_finish(_start+3),不释放内存
  1. insert(迭代器失效的典型场景)

insert(pos, val)的核心是解决扩容导致的 pos 失效:

cpp 复制代码
iterator insert(iterator pos, const T& val) {
    if (_finish == _end_of_storage) {
        size_t len = pos - _start; // 记录pos到起始的距离(关键)
        reserve(/*扩容*/);
        pos = _start + len; // 扩容后更新pos(旧pos指向已释放内存)
    }
    // 元素后移(从后往前)
    iterator end = _finish - 1;
    while (end >= pos) {
        *(end + 1) = *end;
        --end;
    }
    *pos = val;
    ++_finish;
    return pos; // 返回更新后的pos,方便用户后续使用
}
关键注意点:
  • 扩容后必须重新计算 pos:因为扩容会分配新内存,旧pos指向的旧内存已被释放(野指针);

  • 返回新pos:避免用户使用失效的迭代器(如 test_vector3 中pos = v1.insert(pos, 30)后,可安全修改*pos)。

  1. erase(循环删除的正确写法)

erase(pos)的核心是迭代器失效范围(仅pos之后的迭代器失效,pos本身被返回的新迭代器替代):

cpp 复制代码
iterator erase(iterator pos) {
    // 元素前移(从pos+1开始)
    iterator start = pos + 1;
    while (start != _finish) {
        *(start - 1) = *start;
        ++start;
    }
    --_finish;
    return pos; // 返回指向"原pos下一个元素"的迭代器
}
典型场景:循环删除满足条件的元素

错误写法(会导致迭代器失效,漏删或越界):'

cpp 复制代码
for (auto it = v1.begin(); it != v1.end(); ++it) {
    if (*it % 2 == 0) {
        v1.erase(it); // erase后it失效,++it操作非法
    }
}

正确写法(用 erase 的返回值更新迭代器):

cpp 复制代码
auto it = v1.begin();
while (it != v1.end()) {
    if (*it % 2 == 0) {
        it = v1.erase(it); // 用返回值更新it,指向删除后的下一个元素
    } else {
        ++it; // 不删除则正常后移
    }
}
  1. push_back(扩容策略)
cpp 复制代码
void push_back(const T& x) {
    if (_finish == _end_of_storage) {
        // 扩容策略:初始capacity=0→4,否则扩为2倍
        reserve(capacity() == 0 ? 4 : capacity() * 2);
    }
    *_finish = x;
    ++_finish;
}
扩容策略的意义:
  • 避免每次push_back都扩容(减少内存分配次数,提高效率);

  • 2 倍扩容是平衡 "内存利用率" 和 "分配次数" 的经典方案(1.5 倍扩容更优,但 2 倍实现简单)。

四、const 正确性(规范设计)

代码严格遵循 "const 对象只能调用 const 接口",这是 C++ 的规范设计,需关注:

  1. const 迭代器:提供const_iterator begin() constconst_iterator end() const,支持 const 对象遍历(如func(const vector<int>& v)中使用v.begin());

  2. const 版本 operator []:

cpp 复制代码
const T& operator[](size_t pos) const {
    assert(pos < size());
    return _start[pos];
}
  1. 确保 const 对象只能读取元素,不能修改(如funcv[i]无法赋值)。

五、测试用例中的典型场景(实践巩固)

代码中的测试用例覆盖了 vector 的核心使用场景,复习时需结合案例理解:

  1. test_vector5(循环删除):掌握erase后迭代器更新的正确写法;

  2. test_vector7(嵌套 vector):vector<vector<int>>实现杨辉三角,理解二维动态数组的管理(外层 vector 存储内层 vector,内层 vector 各自管理自己的内存);

  3. test_vector7(复杂类型拷贝):vector<string> v4(v3)验证深拷贝正确性(若用memcpyv3v4string会共享内存,析构崩溃);

  4. test_vector6(sort 排序):sort(v1.begin(), v1.end())说明 vector 的迭代器是随机访问迭代器(支持sort的要求),而 list 的迭代器不支持。

六、总结

  1. 三个迭代器的作用与size()/capacity()的计算方式;

  2. 拷贝构造为什么不能用memcpy(深拷贝 vs 浅拷贝);

  3. reserveresize的功能差异(是否改变 size、是否初始化元素);

  4. inserterase的迭代器失效问题(如何避免、返回值的作用);

  5. 循环删除元素的正确写法(结合erase的返回值);

  6. 模板构造函数的匹配陷阱(为什么需要int n的重载)。

cpp 复制代码
#pragma once
#include<assert.h>

namespace bit
{
        template<class T>
        class vector
        {
        public:
                typedef T* iterator;
                typedef const T* const_iterator;

                //vector()
                //        :_start(nullptr)
                //        , _finish(nullptr)
                //        , _end_of_storage(nullptr)
                //{}

                //vector(size_t n, const T& val = T())
                //        : _start(nullptr)
                //        , _finish(nullptr)
                //        , _end_of_storage(nullptr)
                //{
                //        reserve(n);
                //        for (size_t i = 0; i < n; ++i)
                //        {
                //                push_back(val);
                //        }
                //}

                //// [first, last)
                //template <class InputIterator>
                //vector(InputIterator first, InputIterator last)
                //        : _start(nullptr)
                //        , _finish(nullptr)
                //        , _end_of_storage(nullptr)
                //{
                //        while (first != last)
                //        {
                //                push_back(*first);
                //                ++first;
                //        }
                //}

                vector()
                {}

                //vector<int> v(10, 5);
                vector(size_t n, const T& val = T())
                {
                        reserve(n);
                        for (size_t i = 0; i < n; ++i)
                        {
                                push_back(val);
                        }
                }

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

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

                vector(const vector<T>& v)
                {
                        _start = new T[v.capacity()];
                        //memcpy(_start, v._start, sizeof(T)*v.size());
                        for (size_t i = 0; i < v.size(); ++i)
                        {
                                _start[i] = v._start[i];
                        }

                        _finish = _start + v.size();
                        _end_of_storage = _start + v.capacity();
                }

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

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

                iterator begin()
                {
                        return _start;
                }

                iterator end()
                {
                        return _finish;
                }

                const_iterator begin() const
                {
                        return _start;
                }

                const_iterator end() const
                {
                        return _finish;
                }

                void resize(size_t n, T val = T())
                {
                        if (n < size())
                        {
                                // 删除数据
                                _finish = _start + n;
                        }
                        else
                        {
                                if (n > capacity())
                                        reserve(n);

                                while (_finish != _start+n)
                                {
                                        *_finish = val;
                                        ++_finish;
                                }
                        }
                }

                void reserve(size_t n)
                {
                        if (n > capacity())
                        {
                                size_t sz = size();
                                T* tmp = new T[n];
                                if (_start)
                                {
                                        //memcpy(tmp, _start, sizeof(T)*size());
                                        //深拷贝
                                        for (size_t i = 0; i < sz; ++i)
                                        {
                                                tmp[i] = _start[i];
                                        }
                                        delete[] _start;
                                }

                                _start = tmp;
                                _finish = _start + sz;
                                _end_of_storage = _start + n;
                        }
                }

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

                        *_finish = x;
                        ++_finish;
                }

                void pop_back()
                {
                        assert(!empty());

                        --_finish;
                }

                iterator insert(iterator pos, const T& val)
                {
                        assert(pos >= _start);
                        assert(pos <= _finish);

                        if (_finish == _end_of_storage)
                        {
                                size_t len = pos - _start;
                                reserve(capacity() == 0 ? 4 : capacity() * 2);

                                // 扩容后更新pos,解决pos失效的问题
                                pos = _start + len;
                        }

                        iterator end = _finish-1;
                        while (end >= pos)
                        {
                                *(end + 1) = *end;
                                --end;
                        }

                        *pos = val;
                        ++_finish;

                        return pos;
                }

                iterator erase(iterator pos)
                {
                        assert(pos >= _start);
                        assert(pos < _finish);

                        iterator start = pos + 1;
                        while (start != _finish)
                        {
                                *(start - 1) = *start;
                                ++start;
                        }

                        --_finish;

                        return pos;
                }

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

                size_t size() const
                {
                        return _finish - _start;
                }

                bool empty()
                {
                        return _start == _finish;
                }

                T& operator[](size_t pos)
                {
                        assert(pos < size());

                        return _start[pos];
                }

                const T& operator[](size_t pos) const
                {
                        assert(pos < size());

                        return _start[pos];
                }

        private:
                iterator _start = nullptr;
                iterator _finish = nullptr;
                iterator _end_of_storage = nullptr;
        };

        void func(const vector<int>& v)
        {
                for (size_t i = 0; i < v.size(); ++i)
                {
                        cout << v[i] << " ";
                }
                cout << endl;

                vector<int>::const_iterator it = v.begin();
                while (it != v.end())
                {
                        cout << *it << " ";
                        ++it;
                }
                cout << endl << endl;
        }

        void test_vector1()
        {
                vector<int> v1;
                v1.push_back(1);
                v1.push_back(2);
                v1.push_back(3);
                v1.push_back(4);
                v1.push_back(5);

                func(v1);

                for (size_t i = 0; i < v1.size(); ++i)
                {
                        cout << v1[i] << " ";
                }
                cout << endl;

                v1.pop_back();
                v1.pop_back();

                vector<int>::iterator it = v1.begin();
                while (it != v1.end())
                {
                        cout << *it << " ";
                        ++it;
                }
                cout << endl;

                v1.pop_back();
                v1.pop_back();
                v1.pop_back();
                //v1.pop_back();

                for (auto e : v1)
                {
                        cout << e << " ";
                }
                cout << endl;

                //func(v1);
        }

        //template<class T>
        //void f()
        //{
        //        T x = T();
        //        cout << x << endl;
        //}

        //void test_vector2()
        //{
        //        // 内置类型有没有构造函数
        ///*        int i = int();
        //        int j = int(1);*/

        //        f<int>();
        //        f<int*>();
        //        f<double>();
        //}

        void test_vector2()
        {
                vector<int> v1;
                v1.push_back(1);
                v1.push_back(2);
                v1.push_back(3);
                v1.push_back(4);
                v1.push_back(5);

                cout << v1.size() << endl;
                cout << v1.capacity() << endl;

                v1.resize(10);

                cout << v1.size() << endl;
                cout << v1.capacity() << endl;

                func(v1);

                v1.resize(3);

                func(v1);
        }

        void test_vector3()
        {
                std::vector<int> v1;
                v1.push_back(1);
                v1.push_back(2);
                v1.push_back(3);
                v1.push_back(4);
                //v1.push_back(5);
                for (auto e : v1)
                {
                        cout << e << " ";
                }
                cout << endl;

                /*v1.insert(v1.begin(), 0);
                for (auto e : v1)
                {
                cout << e << " ";
                }
                cout << endl;*/

                auto pos = find(v1.begin(), v1.end(), 3);
                if (pos != v1.end())
                {
                        //v1.insert(pos, 30);
                        pos = v1.insert(pos, 30);
                }

                for (auto e : v1)
                {
                        cout << e << " ";
                }
                cout << endl;

                // insert以后我们认为pos失效了,不能再使用
                (*pos)++;

                for (auto e : v1)
                {
                        cout << e << " ";
                }
                cout << endl;
        }

        void test_vector4()
        {
                std::vector<int> v1;
                v1.push_back(1);
                v1.push_back(2);
                v1.push_back(3);
                v1.push_back(4);
                for (auto e : v1)
                {
                        cout << e << " ";
                }
                cout << endl;

                //auto pos = find(v1.begin(), v1.end(), 2);
                auto pos = find(v1.begin(), v1.end(), 4);
                if (pos != v1.end())
                {
                        v1.erase(pos);
                }

                (*pos)++;

                for (auto e : v1)
                {
                        cout << e << " ";
                }
                cout << endl;
        }

        void test_vector5()
        {
                bit::vector<int> v1;
                v1.push_back(10);
                v1.push_back(2);
                v1.push_back(3);
                v1.push_back(4);
                v1.push_back(5);
                v1.push_back(50);

                bit::vector<int>::iterator it = v1.begin();
                while (it != v1.end())
                {
                        if (*it % 2 == 0)
                        {
                                it = v1.erase(it);
                        }
                        else
                        {
                                ++it;
                        }
                }

                for (auto e : v1)
                {
                        cout << e << " ";
                }
                cout << endl;

                for (auto e : v1)
                {
                        cout << e << " ";
                }
                cout << endl;
        }


        void test_vector6()
        {
                //vector<int> v(10u, 5);
                vector<int> v1(10, 5);
                for (auto e : v1)
                {
                        cout << e << " ";
                }
                cout << endl;

                vector<int> v2(v1.begin()+1, v1.end()-1);
                for (auto e : v2)
                {
                        cout << e << " ";
                }
                cout << endl;

                std::string s1("hello");
                vector<int> v3(s1.begin(), s1.end());
                for (auto e : v3)
                {
                        cout << e << " ";
                }
                cout << endl;

                int a[] = { 100, 10, 2, 20, 30 };
                vector<int> v4(a, a+3);
                for (auto e : v4)
                {
                        cout << e << " ";
                }
                cout << endl;

                v1.insert(v1.begin(), 10);
                for (auto e : v1)
                {
                        cout << e << " ";
                }
                cout << endl;

                sort(v1.begin(), v1.end());

                for (auto e : v1)
                {
                        cout << e << " ";
                }
                cout << endl;


                for (auto e : a)
                {
                        cout << e << " ";
                }
                cout << endl;

                //sort(a, a+sizeof(a)/sizeof(int));
        /*        greater<int> g;
                sort(a, a + sizeof(a) / sizeof(int), g);*/

                sort(a, a + sizeof(a) / sizeof(int), greater<int>());

                for (auto e : a)
                {
                        cout << e << " ";
                }
                cout << endl;
        }

        class Solution {
        public:
                vector<vector<int>> generate(int numRows) {
                        vector<vector<int>> vv;
                        vv.resize(numRows, vector<int>());
                        for (size_t i = 0; i < vv.size(); ++i)
                        {
                                vv[i].resize(i + 1, 0);
                                vv[i][0] = vv[i][vv[i].size() - 1] = 1;
                        }

                        for (size_t i = 0; i < vv.size(); ++i)
                        {
                                for (size_t j = 0; j < vv[i].size(); ++j)
                                {
                                        if (vv[i][j] == 0)
                                        {
                                                vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
                                        }
                                }
                        }

                        return vv;
                }
        };

        void test_vector7()
        {
                vector<int> v1(10, 5);
                for (auto e : v1)
                {
                        cout << e << " ";
                }
                cout << endl;

                vector<int> v2(v1);
                for (auto e : v2)
                {
                        cout << e << " ";
                }
                cout << endl;

                vector<std::string> v3(3, "111111111111111111111");
                for (auto e : v3)
                {
                        cout << e << " ";
                }
                cout << endl;

                vector<std::string> v4(v3);
                for (auto e : v4)
                {
                        cout << e << " ";
                }
                cout << endl;

                v4.push_back("2222222222222222222");
                v4.push_back("2222222222222222222");
                v4.push_back("2222222222222222222");
                for (auto e : v4)
                {
                        cout << e << " ";
                }
                cout << endl;

                vector<vector<int>> ret = Solution().generate(5);
                for (size_t i = 0; i < ret.size(); ++i)
                {
                        for (size_t j = 0; j < ret[i].size(); ++j)
                        {
                                cout << ret[i][j] << " ";
                        }
                        cout << endl;
                }
                cout << endl;
        }
}
相关推荐
学生小羊3 小时前
C++小游戏
开发语言·c++·游戏
Go_Zezhou3 小时前
在线性代数里聊聊word embedding
线性代数·算法·机器学习·nlp
Christo33 小时前
TFS-2005《A Possibilistic Fuzzy c-Means Clustering Algorithm》
人工智能·算法·机器学习
aiden:)3 小时前
Selenium WebUI 自动化“避坑”指南——从常用 API 到 10 大高频问题
开发语言·前端·javascript·python·selenium
企鹅chi月饼3 小时前
面试问题:c++的内存管理方式,delete的使用,vector的resize和reverse,容量拓展
c++·面试
hansang_IR3 小时前
【题解】洛谷P1776 宝物筛选 [单调队列优化多重背包]
c++·算法·动态规划·题解·背包·多重背包·单调队列
淬炼之火3 小时前
笔记:深层卷积神经网络(CNN)中的有效感受野简单推导
笔记·cnn·卷积神经网络
jndingxin3 小时前
c++多线程(1)------创建和管理线程td::thread
开发语言·c++·算法
SuperCandyXu3 小时前
洛谷 P3128 [USACO15DEC] Max Flow P -普及+/提高
c++·算法·图论·洛谷