STL_vector
¶1. 内部结构
将 stl_vector.h
分为了四个部分:
- 头文件
- 类型别名声明
- 数据成员
- 成员函数
¶1.1 头文件
1 |
¶1.2 类型别名声明
1 | public: // alias declarartions |
¶1.3 数据成员
1 | private: // data member |
¶1.4 成员函数
成员函数分为两种。更多的细节见下文。
对外可调用的接口函数( public ):
- 构造函数,析构函数
- 拷贝赋值运算符,移动赋值运算符
- getter ,setter
- 修改 size 和 capacity 的接口
- 比较运算符
- push 和 pop 操作
- erase,insert,assign 操作
私有成员函数,实现接口函数功能( private )
¶2. 成员函数
¶2.1 构造函数,析构函数
函数声明或定义如下:
1 | public: // ctor && dtor |
注意:
行: 5
,传入参数为 value_type()
是一个对象,有默认值。
行: 5,6
,调用 fill_initialize()
函数初始化,其内部使用 allocate_and_fill()
函数。
行: 8-11
,传入参数是两个迭代器,会调用 initialize_aux()
,会利用萃取机获得第三个参数。
行: 12-13
,都调用了 allocate_and_copy()
函数,包含分配内存和 copy 操作。
行: 15
,使用了浅拷贝,并将传入对象指针置空。
¶fill_initialize()
1 | void fill_initialize(size_type n, const value_type &value) { |
¶allocate_and_fill()
1 | iterator allocate_and_fill(size_type n, const value_type &value) { |
¶initialize_aux()
_true_type 和 _false_type 见 Iterator
1 | // _true_type |
¶allocate_and_copy()
1 | template <class InputIterator> /* 分配内存并拷贝对应分为内的值,返回头迭代器 */ |
注意:
_true_type 代表了接受的为 int,_false_type 代表了接受的是迭代器。内部分别对应了使用 uninitialized_fill_n()
还是 uninitialized_copy()
。
¶vector(const vector &)
1 | template <class T, class Alloc> /* 拷贝构造函数 */ |
¶vector(std::initializer_list<T>)
1 | template <class T, class Alloc> /* 拷贝构造函数(初始化链表)*/ |
¶vector(vector &&) noexcept
1 | template <class T, class Alloc> /* 移动构造函数 */ |
¶2.2 拷贝赋值运算符,移动赋值运算符
¶vector &operator=(const vector &)
1 | template <class T, class Alloc> /* 拷贝赋值运算符 */ |
¶vector &operator=(vector &&) noexcept
1 | template <class T, class Alloc> /* 移动赋值运算符 */ |
注意:移动赋值运算符需要考虑自赋值!
¶2.3 getter
主要用来获取 vector 的一些数据,皆是 const 类型成员函数。
1 | public: // getter |
¶2.4 setter
与 getter 相呼应,提供一些用户可以获取并修改参数的接口。
1 | public: // setter |
¶2.5 修改 size 和 capacity 的接口
1 | public: // interface for size and capacity |
注意:
行: 3
,利用 value_type()
调用 resize(n, val)
版本重载。
行: 5-9
,shrink_to_fit()
主要利用拷贝构造函数来实现。
¶void resize(size_type, const value_type &)
1 | template <class T, class Alloc> |
注意:内部可能调用 erase()
,fill_insert()
,见下文。
¶void reserve(size_type)
1 | template <class T, class Alloc> |
注意:如果修改成更小值,什么都不操作,否则重新分配内存,并拷贝过去,释放就对象。
¶2.6 比较运算符
1 | public: // compare operator(member function) |
注意:行: 3-5
,可以发现不等于操作是基于等于操作实现的。
¶bool operator==(const vector &) const noexcept
1 | template <class T, class Alloc> |
¶2.7 push_back() 和 pop_back()
1 | public: // push && pop |
注意:行: 2-6
,pop_back()
操作先 finish--
,才可以对 finish
析构。
¶void push_back(const value_type &)
1 | template <class T, class Alloc> |
注意:内部可能调用 insert_aux()
,见下文。
¶2.8 erase 操作
1 | public: // erase |
注意:行: 3-5,6
,内部调用 iterator erase(iterator, iterator)
¶iterator erase(iterator, iterator)
1 | template <class T, class Alloc> |
更直观见下图所示:
《STL源码剖析》 – P123 - P124
¶2.9 insert 操作
1 | public: // insert |
注意:
行: 2
,内部调用 iterator insert(iterator, const value_type &);
行: 5-8
,传入参数是三个迭代器,内部调用 insert_dispatch()
,会利用萃取机获得第四个参数。
¶iterator insert(iterator, const value_type &)
1 | template <class T, class Alloc> |
¶void insert_aux(iterator, const value_type &)
1 | template <class T, class Alloc> |
该函数实现,插入一个数。具体的实现见注释。
¶insert_dispatch()
1 | /* _true_type 型别,使用 fill_insert */ |
与前文相同,_true_type 主要针对 int,_false_type 主要针对迭代器,分别采用 fill_insert()
和 range_insert()
。而后者会利用到萃取机推导第四个参数。
fill_insert()
1 | template <class T, class Alloc> |
将 position 作为哨兵,可以将以上过程分为三种情况:
备用空间足够
- ① 插入点后的元素个数 elems_after 大于新增元素个数
- ② 插入点后的元素个数 elems_after 小于等于新增元素个数
③ 备用空间不足
具体的操作方式见注释,更形象的表示如下图所示:
注意:其中的内存移动利用copy
,插入使用 fill
《STL源码剖析》 – P125 - P128
¶range_insert()
range_insert()
的第四个参数由萃取机推导获得,有两种情况;
- input_iterator_tag
- forward_iterator_tag
也就对应了两种重载函数:
1 | /* 将指定范围内的元素,插入到指定位置*/ |
两个的区别在于,input_iterator_tag 情况下没有 distance()
操作,只能单步执行 insert 操作来实现。
1 | template <class T, class Alloc> |
forward_iterator_tag 情况下有distance()
操作,可以提前知道插入多少个元素。采用更高效的插入方法,具体的插入方法与 fill_insert()
完全相同,仅仅是插入的方式不同。具体的操作方式见 fill_insert()
。
注意:
fill_insert()
:其中的内存移动利用copy
,插入使用fill
range_insert()
:其中的内存移动利用copy
,插入使用copy
1 | template <class T, class Alloc> |
为什么STL的很多container的range version insert函数实现都有input iterator和forward iterator两个重载?
¶2.10 assign 操作
1 | public: // assign |
注意:
行: 2
,内部调用 fill_assign()
。
行: 4-7
,传入参数是两个迭代器,内部调用 assign_dispatch()
,会利用萃取机获得第三个参数。
行: 12-15
,内部调用 void assign(std::initializer_list<value_type> ils)
重载函数。
行: 8-10
,内部调用 void assign(InputIterator first, InputIterator last)
重载函数。
¶fill_assign()
1 | template <class T, class Alloc> |
具体的操作方式见注释。
¶assign_dispatch
1 | // _true_type 内部调用 fill_assign() |
1 | // _false_type 内部调用 assign_aux() |
与前文相同,_true_type 主要针对 int,_false_type 主要针对迭代器,分别采用 fill_assign()
和 assign_aux()
。而后者会利用到萃取机推导第三个参数。
¶assign_aux()
assign_aux()
的第三个参数由萃取机推导获得,有两种情况;
- input_iterator_tag
- forward_iterator_tag
也就对应了两种重载函数:
1 | template <class InputIterator> |
两个的区别在于,input_iterator_tag 情况下没有 distance()
操作,只能单步执行 assign 操作来实现。
1 | template <class T, class Alloc> |
forward_iterator_tag 情况下有distance()
操作,可以提前知道赋值多少个元素。采用更高效的赋值方法,具体的插入方法与 fill_assign
完全相同,仅仅是插入的方式不同。具体的操作方式见注释。
1 | template <class T, class Alloc> |
¶2.11 补充
¶交换操作
1 | template <class T, class Alloc> /* 交换操作 */ |
¶比较运算符(不是成员函数)
1 | template <class T, class Alloc> /* A == B */ |
¶3 参考文献
《STL源码剖析》
为什么STL的很多container的range version insert函数实现都有input iterator和forward iterator两个重载?