【爱上C++】vector模拟实现

07-16 544阅读

文章目录

  • 前言
  • 一:基本框架
    • 1.结构的定义
    • 2.构造函数
      • ①.详解 const T& val = T()
      • ②.为什么要多加一个int类的带参构造?】
      • 3.析构函数
      • 4.size()和capacity()
      • 5.push_back尾插
      • 6.operator[]
        • operator[]的返回类型为T&有以下几个原因:
        • 二:迭代器的实现
            • 1.begin()和end()
            • 2.迭代器区间构造
            • 三:reserve 引发的相关问题
              • 1.内部迭代器失效
              • 2.外部迭代器失效
              • 3.正确的实现
                  • _start 指针的作用
                  • _start[i] 的含义
                  • 4.memcpy带来的问题
                  • 四:insert 引发的相关问题
                    • 1.内部迭代器失效
                    • 2.外部迭代器失效
                    • 五:erase 引发的相关问题
                        • 修改后的执行过程
                        • 六:其他操作
                          • resize
                          • 赋值运算符重载
                            • 现代写法的思路
                            • 为什么现代写法传参数时不能传引用?
                            • 现代写法传参为什么不能传引用的具体原因:
                            • 七:完整代码

                              前言

                              上一节我们讲了vector的基本使用,现在我们讲解vector的模拟实现,其中有三大重难点:
                              1.vector是如何进行设计与封装的
                              2.迭代器失效问题
                              3.memcpy,memmove导致的浅拷贝问题


                              一:基本框架

                              1.结构的定义

                              #include 
                              #include 
                              using namespace std;
                              namespace myvector
                              {
                              	template
                              	class vector {
                              	public:
                              		// Vector的迭代器是一个原生指针
                              		typedef T* iterator;
                              		typedef const T* const_iterator;
                              	private:
                              		iterator _start;        // 开始位置
                              		iterator _finish;		// 结束位置
                              		iterator _endofstorage;	// end of storage
                              	};
                              }
                              

                              在 vector 类中,我们通常会使用_指针_来表示迭代器,因为指针天然支持指针算术运算和解引用操作,可以方便地遍历和访问元素。使用 typedef 定义迭代器类型可以使代码更加灵活和可维护。

                              使用 iterator 类型有以下几个原因:

                              1. 可读性和维护性: 使用 iterator 使得代码更具可读性。例如,当看到 iterator 类型时,很容易理解这是一个指向容器元素的指针,而不是其他类型的指针。
                              2. 灵活性: 如果将来需要更改迭代器的实现方式,只需要修改 typedef 定义,而不需要修改所有使用迭代器的代码。例如,如果将来决定使用自定义的迭代器类,而不是原始指针,只需要修改 typedef 语句。
                              3. 与 STL 接口一致: 这使得 vector 类的接口与标准模板库(STL)容器的接口一致,便于用户使用和理解。例如,STL 容器如 std::vector 也使用迭代器来遍历和操作元素。

                              _start:

                              • 这是一个指针,指向分配的内存空间中的第一个元素。
                              • 在 vector 类中,_start 指向当前存储的第一个元素的位置。

                                _finish:

                                • 这是一个指针,指向当前存储的最后一个元素之后的位置。
                                • 在 vector 类中,_finish 指向最后一个元素的下一个位置。即,有效元素的范围是从 _start 到 _finish-1。

                                  _endofstorage:

                                  • 这是一个指针,指向分配的内存空间的末尾位置。
                                  • 在 vector 类中,_endofstorage 指向当前分配的内存空间的末尾,即容器可以存储元素的最大位置。

                                    【爱上C++】vector模拟实现
                                    所以由图我们可以知道:
                                    _size=_finish-_start
                                    _capacity=_endofstorage-_start

                                    2.构造函数

                                    构造函数有三类:无参构造函数,带参构造,迭代器区间构造放在 二:迭代器的实现 中讲述。

                                    //无参默认构造
                                    vector() 
                                    :_start(nullptr)
                                    , _finish(nullptr)
                                    , _endofstorage(nullptr)
                                    {
                                    }
                                    // 带参构造函数,创建包含 n 个 val 值的 vector
                                    vector(size_t n, const T& val = T())
                                    {
                                        resize(n, val);
                                    }
                                    //带参构造(int)
                                    vector(int n, const T& val = T())
                                    {
                                        resize(n, val);
                                    }
                                    

                                    ①.详解 const T& val = T()

                                    关于 const T& value = T() 作为默认参数的详细解释,我们需要理解以下几个概念:默认参数、匿名对象、以及如何结合使用它们。
                                    默认参数
                                    默认参数允许函数在调用时省略某些参数。编译器会使用提供的默认值来填补这些被省略的参数。对于构造函数来说,默认参数提供了一种灵活的初始化方式。
                                    匿名对象
                                    匿名对象是指没有绑定到任何变量的临时对象。在 C++ 中,可以通过直接调用构造函数来创建匿名对象。例如,T() 就是一个创建类型为 T 的默认构造对象的表达式。
                                    const T& value = T()
                                    现在,让我们将这些概念结合起来看 const T& value = T() 是如何工作的。

                                    1. 默认参数的使用
                                      在构造函数定义中:
                                      vector(int n, const T& value = T())
                                      value 是一个默认参数,其默认值是一个匿名对象 T()。
                                    2. 匿名对象的创建
                                      T() 表达式创建了一个类型为 T 的匿名对象。对于基础类型 int,T() 等价于 0。对于用户定义的类型 T,它调用 T 的默认构造函数创建一个默认初始化的对象。
                                    3. 引用绑定
                                      const T& value = T() 意味着默认参数 value 是一个对匿名对象的常量引用。这里引用了临时对象,但因为是常量引用,所以该临时对象在整个函数调用过程中是有效的(临时对象的生命周期延长至引用的生命周期结束)。

                                    ②.为什么要多加一个int类的带参构造?】

                                    思考一下,如果不加vector(int n, const T& val = T()),下面这个代码是否有问题?

                                    vector v1(5, "1111");
                                    for (auto e : v1)
                                        {
                                            cout 
                                            cout 
                                        if (_start)
                                        {
                                            delete[] _start;
                                            _start = _finish = _endofstorage = nullptr;
                                        }
                                    }
                                    
                                        return _finish - _start;
                                    }
                                    size_t capacity() const
                                    {
                                        return _endofstorage - _start;
                                    }
                                    
                                        if (_finish == _endofstorage)//如果满了则要扩容
                                        {
                                            size_t  newcapacity = capacity() == 0 ? 4 : capacity() * 2;
                                            //如果 capacity() == 0,则让 capacity()=4,
                                            //如果 capacity() !=0 ,则让 capacity()=capacity()*2
                                            reserve(newcapacity);
                                        }
                                        *_finish = x;
                                        ++_finish;
                                    }
                                    void pop_back()
                                    {
                                        assert(size()0);
                                        --_finish;
                                    }
                                    
                                        assert(pos 
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]