行行宜行行

热爱风光、热爱新技术、踏出每一步,过好每一天!

Std::Vector详解与实现

std::vector是一种动态数组容器,是存放在连续的内存空间,并且可以自动扩容,下面详细介绍一下std::vector的底层原理

核心机制

std::vector的核心机制主要有以下四个,分别是

  1. 连续存储

std::vector内部会维护一块连续的动态内存空间,将元素存储在其中,可以像数组一样通过下标快速访问元素(时间复杂度为O(1))

  1. 数组内部容量与大小分离

    数组内部有两个表示大小的成员变量分别是sizecapacity,其中size表示当前实际存储的元素数量,capacity表示当前分配的内存可以容纳的最大元素数量,当两者相等时,说明当前内存空间已满,会触发重新分配内存空间,即扩容

  2. 拥有一个动态扩容机制

    当需要插入元素而空间不足时,会自动分配一块更大的内存(通常是当前容量的 1.5 倍或 2 倍),具体实现原理后面章节会详细介绍

  3. 利用指针管理内存

内部通过三个指针管理内存,分别是:
start_:指向内存起始位置的指针
end_:指向当前最后一个元素的后一个位置的指针
end_of_storage_:指向内存块的末尾指针

array的对比

特性 数组(Array) 向量(Vector)
大小 固定大小(编译时或运行时) 动态可变大小
内存管理 手动管理(需要预留足够空间) 自动管理(自动扩展或收缩)
支持的操作 限制较多 丰富的成员函数和操作
安全性 较低(易发生缓冲区溢出) 较高(通过成员函数进行边界检查)
与 STL 算法的兼容性

底层原理与实现

类型别名与成员变量

 template<typename T, typename Alloc = MyAlloc<T>>
    class vector {
    public:
    	using value_type = T;
        using allocator_type = Alloc;
        using size_type = size_t;
        using difference_type = ptrdiff_t;
        using reference = value_type &;
        using const_reference = const value_type &;
        using pointer = typename Alloc::pointer;
        using const_pointer = typename Alloc::const_pointer;
        using iterator = pointer;
        using const_iterator = const_pointer;
        using reverse_iterator = my::reverse_iterator<iterator>;
        using const_reverse_iterator = my::reverse_iterator<const_iterator>;

        /// 定义成员变量
        pointer start_;         /// 指向开头
        pointer finish_;        /// 指向最后一个有效元素的下一位
        pointer end_of_storage_;    /// 指向分配空间的最后
        Alloc alloc_;
    }

首先定义了完整的类型别名以及所需要的三个指针,以此来管理分配的内存地址,内存分配器可以使用自己自定义的内存分配器,但是我个人觉得一般情况下使用标准的内存分配器效果更好,看个人考量吧。

构造函数与析构

我们需要提供几种典型的构造情形,比如默认构造、单参构造、通过初始化列表来构造、迭代器范围构造以及拷贝构造和移动构造等,下面一一给出这几种典型的情况,并且解释其原理

默认构造

默认构造函数,就是最简单的方式,利用初始化列表的方式给三个指针初始化为nullptr就行

vector() : start_(nullptr), finish_(nullptr), end_of_storage_(nullptr) {}

单参构造

为了防止隐式类型转换,我们通常在单参构造时添加 explicit关键字,必须显示指定其具体类型,如下所示。

 explicit vector(size_type n) {
            create_storage_(n);
            default_initialize_(n);
        }

这里在单参构造大小为n的数组时,起始是调用了底层的私有接口函数,分别开辟了内存地址空间以及在空间上构造对象,这里介绍这两个私有接口

create_storage_

这个接口的主要作用就是分配相应的内存空间,更新三个成员变量的值

void create_storage_(size_type n) {
            /// 分配空间
            start_ = alloc_.allocate(n);   /// 这是底层内存分配器完成的工作
            finish_ = start_;
            end_of_storage_ = start_ + n;
        }

这里仅是分配了对应的内存空间,并未构造对象

default_initialize_

由于底层构造对象的函数都是可以复用的,所以这里调用了uninitalized_default_n_a,在first开始的内存空间中,构造对象

void default_initialize_(size_type n) {
      finish_ = uninitalized_default_n_a(start_, n, alloc_);
}

uninitalized_default_n_a

使用默认构造函数构造 n 个对象到 first 开始的内存空间, 构造过程中失败,就销毁 [first, cur) 区间—>保证异常安全

template <typename ForwardIterator, typename Size, typename Alloc>
ForwardIterator uninitalized_default_n_a(ForwardIterator first, Size n, Alloc& alloc) {
    ForwardIterator cur = first;    /// 记录构造进度
    try{
        for(; n > 0; --n, ++cur) {
            alloc.construct(std::addressof(*cur));
        }
        return cur;     /// 返回下一个未初始化的位置
    } catch (...) {
        destroy_(first, cur, alloc);
        throw;
    }
}

这里在构造失败之后,销毁已经构造的对象函数 destroy_如下

destroy_

对 [first, last) 区间的对象调用 destroy,对 [first, last) 区间的对象调用 destroy

template<typename InputIterator, typename Alloc>
void destroy_(InputIterator first, InputIterator last, Alloc &alloc) {
    for (; first != last; ++first) {
        std::allocator_traits<Alloc>::destroy(alloc, std::addressof(*first));   
    }
}

构造n个value

其实这些都是提供给外部的接口,内部实现都是去调用底层的私有实现方法

vector(size_type n, const T &value) : vector() {
            /// 都去调用底层实现
            create_storage_(n);
            fill_initialize_(n, value);
        }

fill_initialize_

构造n个value值的对象

void fill_initialize_(size_type n, const T &value) {
            finish_ = uninitialized_fill_n_a(start_, n, value, alloc_); /// 调用底层的分配方法
        }

uninitialized_fill_n_a

[first, first + n)范围内,用value 拷贝构造 n 个对象(未初始化空间),使用自定义分配器 alloc -> 返回最后一个构造后的位置(cur),如果构造失败,则析构刚刚创建过的对象,并且释放内存、抛出异常

template<typename ForwardIterator, typename Size, typename T, typename Alloc>
ForwardIterator uninitialized_fill_n_a(ForwardIterator first, Size n, const T &value, Alloc &alloc) {
    ForwardIterator cur = first;  /// 记录当前构造进度位置,用于异常时回滚
    try {
        for (;n > 0; --n, ++cur) {
            /// 调用分配构造函数,用指定值构造对象
            alloc.construct(std::addressof(*cur), value);
        }
        return cur;  /// 返回下一个未初始化的位置
    } catch (...) {
        /// 析构已经成功构造的 [first, cur) 区间
        destroy_(first, cur, alloc);
        throw;
    }
}

使用 initializer_list 构造

std::vector还提供了初始化列表的构造方式,用户在定义时可以通过以下方式构造vector

std::vector<int> vec = {1, 2, 3, 5};

所以这里添加这种方式构造的实现

 vector(std::initializer_list<T> ilist) {
            create_storage_(ilist.size());
            finish_ = uninitialized_copy_a(ilist.begin(), ilist.end(), start_, alloc_);
        }

第一步分配内存空间就不多说了,都是一样的,这里介绍一下uninitialized_copy_a

uninitialized_copy_a

将 [first, last) 区间元素拷贝到 result 开始的未初始化区域==>等价于 std::uninitialized_copy, 在 vector::insert()realloc() 时,将旧数据复制到新内存中就会用到这个函数

template <typename InputIterator, typename ForwordIterator, typename Alloc>
ForwordIterator uninitialized_copy_a(InputIterator first, InputIterator last, ForwordIterator result, Alloc& alloc) {
    ForwordIterator cur = result;
    try {
        for (; first != last; ++first, ++cur) {
            alloc.construct(std::addressof(*cur), *first);
        }
        return cur;
    } catch(...) {
        destroy_(result, cur, alloc);
        throw ;
    }
}

initializer_list 赋值

vector &operator=(std::initializer_list<T> ilist) {
            assign(ilist.begin(), ilist.end());
            return *this;
        }

assign

void assign(size_type n, const value_type &value) {
            fill_assign_(n, value);
        }

填充n个value

fill_assign_

这里涉及到将容器中的内容替换为n个value的拷贝,所以需要考虑三种不同的情况

  1. 如果n大于当前容器的容量大小,创建一个新容器并且交换内容
  2. 如果n大于当前大小,但是小于容量,会填充现有元素并添加新元素
  3. 如果n小于当前大小,缩小容器的大小
void fill_assign_(size_type n, const value_type &value) {
            if (n > capacity()) {
                vector temp(n, value);
                this->swap(temp);
            } else if (n > size()) {
                std::fill(start_, finish_, value);
                finish_ = uninitialized_fill_n_a(finish_, n - size(), value, alloc_);
            } else if (n < size()) {
                std::fill(start_, start_ + n, value);
                erase_at_end_(start_ + n);

            } else {
                erase_at_end_(start_ + n);
            }
        }

那在这里接着介绍一下其中用到的各种接口

swap

即交换两个空间,相信大家一看代码就懂

void swap(vector &other) {
            my::swap(start_, other.start_);
            my::swap(finish_, other.finish_);
            my::swap(end_of_storage_, other.end_of_storage_);
        }
erase_at_end_

删除指定位置之后的元素

void erase_at_end_(pointer pos) {
            if (finish_ > pos) {
                destroy_(pos, finish_, alloc_);
                finish_ = pos;
            }
        }

通过迭代器范围构造

输入迭代器范围区间[first, last),利用这个迭代器这个范围内的元素构造数组

template<typename InputIt>
        vector(InputIt first, InputIt last) {
            size_type n = std::distance(first, last);
            create_storage_(n);
            finish_ = uninitialized_copy_a(first, last, start_, alloc_);
        }

原理都是一样的,先分配对应大小的内存空间,然后构造对象,只是不同的构造函数构造对象的方式些许不同。

拷贝构造

vector(const vector &other) {
            create_storage_(other.size());
            finish_ = uninitialized_copy_a(other.start_, other.finish_, start_, alloc_);
        }

移动构造

将另一个vector资源移动到当前vector之后,需要将另一个vector置空

vector(vector &&other) noexcept
                : start_(other.start_), finish_(other.finish_), end_of_storage_(other.end_of_storage_) {
            other.start_ = other.finish_ = other.end_of_storage_ = nullptr;
        }

赋值运算符

拷贝赋值

两个数组进行赋值操作时,最核心的一点就是检查两个数组的空间容量以及大小是否普配,需要更新相关的参数

如果用来赋值的数组的数量大于当前被赋值数组的长度,需要新开辟空间;否则就需要删除多余的元素,减少空间

	vector &operator=(const vector &other) {
            /// 自赋值检查
            if (this != &other) {
                const size_type other_size = other.size();
                if (other_size > capacity()) {
                    /// 用来赋值的数组的数量大于当前被赋值数组的长度,需要新开辟空间
                    pointer temp = allocate_and_copy_(other_size, other.start_, other.finish_);
                    destroy_(start_, finish_, alloc_);
                    destroy_storage_();
                    start_ = temp;
                    end_of_storage_ = start_ + other_size;
                } else if (size() >= other.size()) {
                    /// 将赋值的数组拷贝到被赋值的数组的对应位置
                    pointer new_finish = std::copy(other.start_, other.finish_, start_);
                    /// 析构对于的对象
                    destroy_(new_finish, finish_, alloc_);
                } else {
                    std::copy(other.start_, other.start_ + size(), start_);
                    uninitialized_copy_a(other.start_ + size(), other.finish_, finish_, alloc_);
                }
                finish_ = start_ + other_size;
            }
            return *this;
        }

移动赋值

 	vector &operator=(vector &&other) noexcept {
            if (this != &other) {
                destroy_(start_, finish_, alloc_);
                destroy_storage_();

                /// 更新成员变量
                start_ = other.start_;
                finish_ = other.finish_;
                end_of_storage_ = other.end_of_storage_;
                /// 将other的成员变量都置为nullptr
                other.start_ = other.finish_ = other.end_of_storage_ = nullptr;
            }
            return *this;
        }

析构

析构需要析构所有的对象,并且释放内存

~vector() {
            destroy_(start_, finish_, alloc_);
            destroy_storage_();
        }
        
void destroy_storage_() {
            /// 释放空间
            alloc_.deallocate(start_, end_of_storage_ - start_);
            /// 将指针全部置为nullptr
            start_ = finish_ = end_of_storage_ = nullptr;
        }

迭代器相关函数

获取开始迭代器与尾后迭代器

非常量版本

        iterator begin() {
                return start_;
        }

         iterator end() {
                 return finish_;
        }

const版本

        const_iterator begin() const {
            return start_;
        }

        const_iterator cbegin() const {
            return const_iterator(start_);
        }

        const_iterator end() const {
            return finish_;
        }

        const_iterator cend() const {
            return const_iterator(finish_);
        }

反向迭代器

		reverse_iterator rbegin() {
            return reverse_iterator(finish_);
        }

        reverse_iterator rend() {
            return reverse_iterator(start_);
        }

        const_reverse_iterator rbegin() const {
            return reverse_iterator(finish_);
        }

        const_reverse_iterator rend() const {
            return reverse_iterator(start_);
        }

接口方法

获取大小

分别提供了获取当前实际的元素大小与容量大小

		size_type size() const { return finish_ - start_; }

        size_type capacity() const { return end_of_storage_ - start_; }

        bool empty() const { return start_ == finish_; }

        size_type max_size() const { return alloc_.max_size(); }

[]与at()

[]不带边界检查的,需要用户自己处理越界的情况,at()带边界检查

		reference operator[](size_type n) {
            return *(start_ + n);
        }

        const_reference operator[](size_type n) const {
            return *(start_ + n);
        }

        /// at()带边界检查
        reference at(size_type n) {
            if (n >= size()) {
                throw my::exception("my::vector<T> out of range");
            }
            return *(start_ + n);
        }

        const_reference at(size_type n) const {
            if (n >= size()) {
                throw my::exception("my::vector<T> out of range");
            }
            return *(start_ + n);
        }

获取元素

分别获取数组前面和后面的元素

		reference front() {
            return *start_;
        }

        const_reference front() const {
            return *start_;
        }

        reference back() {
            if (finish_ == start_) {
                throw my::exception("my::vector<T> empty");
            }
            return *(finish_ - 1);
        }

        const_reference back() const {
            if (finish_ == start_) {
                throw my::exception("my::vector<T> empty");
            }
            return *(finish_ - 1);
        }

预分配内存空间

使用reverse预分配内存空间,可以减少底层频繁的扩容导致的性能开销.

分配了一个新的内存空间并且拷贝旧数据之后,需要删除旧地址空间,并且更新成员变量参数值。

其实底层在做数据的操作时,也完全可以使用移动的方式,性能更好,但是我这里全部使用的拷贝。

 void reserve(size_type n) {
            /// 边界检查
            if (n > max_size()) {
                throw my::exception("my::vector<T> too long");
            }

            if (n > capacity()) {
                size_type old_size = size();
                pointer temp = allocate_and_copy_(n, start_, finish_);
                destroy_(start_, finish_, alloc_);      /// 析构旧的对象
                destroy_storage_();                                 /// 释放旧的空间
                start_ = temp;
                finish_ = start_ + old_size;
                end_of_storage_ = start_ + n;
            }
        }

在预分配一个更大的空间时需要分配空间并且拷贝旧地址的数据

allocate_and_copy_

都是调用底层的私有函数,前面已经介绍过了uninitialized_copy_a

 		template<typename ForwordIterator>
        pointer allocate_and_copy_(size_type n, ForwordIterator first, ForwordIterator last) {
            pointer result = alloc_.allocate(n);
            try {
                uninitialized_copy_a(first, last, result, alloc_);
                return result;
            } catch (...) {
                alloc_.deallocate(result, n);
                throw;
            }
        }

重新分配数组大小 resize()

给数组设定一个新大小,然后将数组的大小改为这个大小,如果新大小大于原始大小,说明需要扩容,并且尾部使用传入的值进行填充,如果小于当前大小,则需要将尾部多余的元素析构。

void resize(size_type new_size, const value_type &value) {
            /// 判断new_size大小
            if (new_size > size()) {
                fill_insert_(end(), new_size - size(), value);
            } else if (new_size < size()) {
                /// 将末尾size() - new_size个元素删除
                erase_at_end_(start_ + new_size);
            }
        }

void resize(size_type n) {
            resize(n, T());
        }

fill_insert_

这里使用了fill_insert_,在指定范围内插入,涉及到元素的移动或者拷贝以及空间够不够用的问题,主要逻辑都在代码中,这里给出具体代码

void fill_insert_(iterator pos, size_type n, const value_type &value) {
            if (n == 0) {
                return;
            }

            if (size_type(end_of_storage_ - finish_) > n) {
                /// 后面还有足够的空间容纳n个value
                const size_type elems_after = finish_ - pos;
                pointer old_finish = finish_;
                if (elems_after > n) {
                    /// 如果pos后的元素比n多,则往后挪n个位置,给pos处开始留n个空间value
                    finish_ = uninitialized_copy_a(finish_ - n, finish_, finish_, alloc_);
                    std::fill_n(pos, n, value);
                } else {
                    finish_ = uninitialized_fill_n_a(finish_, n - elems_after, value, alloc_);
                    /// 现在是使用的拷贝,后期优化成move
                    finish_ = uninitialized_copy_a(pos, old_finish, finish_, alloc_);
                    std::fill(pos, old_finish, value);
                }
            } else {
                /// 如果后面的空间不够了,需要开辟新的空间
                const size_type old_size = size();
                const size_type elems_before = pos - begin();
                const size_type new_size = old_size + my::max(old_size, n);
                pointer new_start = alloc_.allocate(new_size);
                pointer new_finish = new_start;

                try {
                    uninitialized_fill_n_a(new_start + elems_before, n, value, alloc_);
                    /// 这里先使用copy后期优化成move()
                    new_finish = uninitialized_copy_a(start_, pos, new_start, alloc_);
                    new_finish += n;
                    new_finish = uninitialized_copy_a(pos, finish_, new_finish, alloc_);
                } catch (...) {
                    if (!new_finish) {
                        destroy_(new_start + elems_before, new_start + elems_before + n, alloc_);
                    } else {
                        destroy_(new_start, new_finish, alloc_);
                    }
                    alloc_.deallocate(new_start, new_size);
                    throw;
                }

                destroy_(start_, finish_, alloc_);
                destroy_storage_();
                start_ = new_start;
                finish_ = new_finish;
                end_of_storage_ = start_ + new_size;
            }
        }

shrink_to_fit

根据实际大小调整数组容量

	 void shrink_to_fit() {
            if (size() < capacity()) {
                size_type old_size = size();
                pointer temp = allocate_and_copy_(old_size, start_, finish_);
                destroy_(start_, finish_, alloc_);
                destroy_storage_();
                start_ = temp;
                finish_ = start_ + old_size;
                end_of_storage_ = finish_;
            }
        }

元素插入删除

insert

可以在一个位置插入value,在一个位置插入n个value,这两种方式比较简单,使用了底层的fill_insert_区别就在于插入的元素数量

void insert(iterator pos, const value_type &value) {
            fill_insert_(pos, 1, value);    /// 在指定位置插入一个value
        }

void insert(iterator pos, size_type n, const value_type &value) {
            fill_insert_(pos, n, value);    /// 在指定位置插入n个value
        }

使用迭代器范围插入

template<typename InputIt>
void insert(iterator pos, InputIt first, InputIt last) {
            size_type n = my::distance(first, last);
            if (n == 0) {
                return;
            }

            if (size_type(end_of_storage_ - finish_) >= n) {
                pointer old_finish = finish_;
                finish_ = uninitialized_copy_a(old_finish - n, old_finish, old_finish, alloc_);
                std::copy_backward(pos, old_finish, finish_);
                std::copy(first, last, pos);
            } else {
                size_type old_size = size();
                size_type elems_before = pos - begin();
                size_type new_size = old_size + my::max(old_size, n);
                pointer new_start = alloc_.allocate(new_size);
                pointer new_finish = new_start;

                try {
                    new_finish = uninitialized_copy_a(start_, pos, new_start, alloc_);
                    new_finish = uninitialized_copy_a(first, last, new_finish, alloc_);
                    new_finish = uninitialized_copy_a(pos, finish_, new_finish, alloc_);
                } catch (...) {
                    destroy_(new_start, new_finish, alloc_);
                    alloc_.deallocate(new_start, new_size);
                    throw;
                }
                destroy_(start_, finish_, alloc_);
                destroy_storage_();
                start_ = new_start;
                finish_ = new_finish;
                end_of_storage_ = new_start + new_size;
            }
        }

push_back

push_back复用insert的逻辑

void push_back(const value_type &value) {
            insert(end(), value);
        }

emplace_back

emplace_back直接在数组的内存空间中构造,避免了先构造再拷贝到数组中去,减少了开销

如果以前分配的空间还没有用完的情况下,可以直接在后面构造,如果以前分配的空间已经不够了,就需要重新分配空间并且插入数据

		template<typename ...Args>
		void emplace_back(Args &&...args) {
            if (finish_ != end_of_storage_) {
                /// 分配的空间还没有用完,直接进行构造
                std::allocator_traits<Alloc>::construct(alloc_, finish_, my::forward<Args>(args)...);
                ++finish_;
            } else {
                realloc_insert_(end(), my::forward<Args>(args)...);
            }
        }

        /// 支持 emplace 插入
        template<typename... Args>
        iterator emplace(const_iterator pos, Args &&... args) {
            size_type index = pos - cbegin();
            emplace_impl_(begin() + index, std::forward<Args>(args)...);
            return begin() + index;
        }

其中在空间不够用的情况下,使用了realloc_insert_

realloc_insert_

简单来说,这个函数的作用就是分配一个更大的空间,然后将需要构造的元素,在对应的位置构造进去,涉及到的元素的移动,其实看代码就可以发现,就没有画图,所以直接给出代码

  		template <typename... Args>
        void realloc_insert_(iterator pos, Args&&... args) {
            const size_type old_size = size();
            const size_type elems_before = pos - start_;
            const size_type new_capacity = old_size + my::max(old_size, size_type(1));  // grow exponentially

            pointer new_start = alloc_.allocate(new_capacity);
            pointer new_finish = new_start;

            try {
                new_finish = uninitialized_copy_a(start_, pos, new_start, alloc_);

                std::allocator_traits<Alloc>::construct(alloc_, new_start + elems_before, std::forward<Args>(args)...);

                new_finish = new_start + elems_before + 1;

                new_finish = uninitialized_copy_a(pos, finish_, new_finish, alloc_);
            } catch (...) {
                destroy_(new_start, new_finish, alloc_);
                alloc_.deallocate(new_start, new_capacity);
                throw;
            }

            destroy_(start_, finish_, alloc_);
            destroy_storage_();

            start_ = new_start;
            finish_ = new_finish;
            end_of_storage_ = new_start + new_capacity;
        }

emplace

数组也支持了emplace插入

		template<typename... Args>
        iterator emplace(const_iterator pos, Args &&... args) {
            size_type index = pos - cbegin();
            emplace_impl_(begin() + index, std::forward<Args>(args)...);
            return begin() + index;
        }

emplace_iml_

		template<typename... Args>
        void emplace_impl_(iterator pos, Args &&... args) {
            if (finish_ != end_of_storage_) {
                std::move_backward(pos, finish_, finish_ + 1);
                std::allocator_traits<Alloc>::construct(alloc_, pos, std::forward<Args>(args)...);
                ++finish_;
            } else {
                realloc_emplace_(pos, std::forward<Args>(args)...);
            }
        }
		
		// 空间不足的情况下的处理方案
 		template<typename... Args>
        void realloc_emplace_(iterator pos, Args &&... args) {
            size_type old_size = size();
            size_type elems_before = pos - start_;
            size_type new_size = old_size + my::max(old_size, size_type(1));
            pointer new_start = alloc_.allocate(new_size);
            pointer new_finish = new_start;

            try {
                new_finish = uninitialized_move_a(start_, pos, new_start, alloc_);
                std::allocator_traits<Alloc>::construct(alloc_, new_start + elems_before, my::forward<Args>(args)...);
                ++new_finish;
                new_finish = uninitialized_move_a(pos, finish_, new_start + elems_before + 1, alloc_);
            } catch (...) {
                destroy_(new_start, new_finish, alloc_);
                alloc_.deallocate(new_start, new_size);
                throw;
            }

            destroy_(start_, finish_, alloc_);
            destroy_storage_();
            start_ = new_start;
            finish_ = new_finish;
            end_of_storage_ = new_start + new_size;
        }

删除元素

pop_back

在删除元素前,我们都需要判断数组中是否为空,否则对空数组进行删除会导致未定义的行为

		void pop_back() {
            if (start_ == finish_) {
                throw my::exception("my::vector<T> empty");
            }
            --finish_;
            alloc_.destroy(finish_);
        }

erase

	iterator erase(iterator pos) {
            if (pos + 1 != finish_) {
                /// 将pos后的元素都往前挪
                std::copy(pos + 1, finish_, pos);
            }
            --finish_;
            alloc_.destroy(finish_);
            return pos;
        }

clear

	void clear() {
            erase_at_end_(start_);
        }

重载 ==

如果要相等必须其中的每一个元素都要相等,所以需要循环比较,这是关键

	bool operator==(const vector &other) const {
            if (size() != other.size()) {
                return false;
            }

            /// 如果要相等必须其中的每一个元素都要相等,所以需要循环比较
            for (size_type i = 0; i < size(); ++i) {
                if (start_[i] != other.start_[i]) {
                    return false;
                }
            }
            return true;
        }

	// 复用==
	bool operator!=(const vector &other) const {
            return !(*this == other);
        }

 

 

 

 

 

 

 

 

发表回复

Your email address will not be published. Required fields are marked *.

*
*

近期评论

您尚未收到任何评论。

conviction

要想看到璀璨的日出时刻,就要有跋山涉水的决心和坚毅