在上一篇中,我分析了nginx中的动态数组, 作为对比这里再分析一下SGI STL
中的动态数组(vector)。
在开篇之前想借此文大家一个问题,STL在大家的工程中用得多吗?本人现在的
公司C++的代码都是用MFC开发的,公司项目也算比较多的,但是在开发过程中用
上标准库的真的不太多!?
STL中的vector也可以认为是对array的包装和升级。
- vector 在其内部保持着一个原始的数组(一块连续的内存区域),但是提供了计算size,capacity,以及
动态扩展的能力。
- vector 可以认为是一种全新的数据类型,所以必须重载=, []等运算符
- 提供了push_back, pop_back,clear,erase, resize等操作元素的能力
- 提供empty , size,capacity等判断内部元素的能力
- 支持迭代
在vector中定义了三个成员变量,代码如下:
namespace SLD { template <class T> class vector { private: T* m_start;//使用空间的开头 T* m_finish;//使用空间的末尾 T* m_end_of_storage//可用空间的末尾 }; }
其中 m_finish – m_start 就等于这个vector的size,
m_end_of_storage – m_start就等于这个vector的capacity。
vector支持五种类型的初始化化, 所以它就必须有5个构造函数:
explicit vector():m_start(0), m_finish(0), m_end_of_storage(0) {} vector(size_t n, const T& value) { Base(n); m_finish = uninitialized_fill_n(m_start, n, value); } explicit vector(size_t n) { Base(n); m_finish = uninitialized_fill_n(m_start, n, T()); } template <class p> vector(const SLD::vector<p>& x) { Base(x.size()); uninitialized_copy(x.begin(), x.end(), m_start); } vector(T* first, T* last) { Base(last - first); uninitialized_copy(first, last, m_start); }
在这里, 我为了代码的简单, 去掉了STL中从alloc分配内存的动作, 而直接用new delete,
这样管理内存,性能当然很差了, 但是用来说明vector却足够了, 所以uninitialized_copy
和uninitialized_fill_n 函数 我也仿造STL中从新写了一个, 不然的话光是内存分配的动作就能
拉出一大堆的代码。下面是我仿造的几个函数:
private: //为了简便我在这里重新模拟了uninitialized_fill_n //在SLT中它是在stl_uninitialized.h中单独实现的 T* uninitialized_fill_n(T* first, size_t n, const T& value) { assert(first); assert(n >= 0); for (int i = 0; i < n; ++i) *(first + i) = value; return first + n;//[m_start, m_finish) } void uninitialized_copy(T* source_begin, T* source_end, T* dest) { for (int i = 0; i < (source_end - source_begin); ++i) { *(dest + i) = *(source_begin + i); } } void Base (size_t n) { //m_start = _M_allocate(__n);原本是从alloc中分配的,这里为了简单用malloc代替 m_start = new T[n*2];//这个是我写的, 用来代替原来的从alloc中分配的, //这样做的原因是为了得到最少的能编译通过的代码 m_finish = m_start; m_end_of_storage = m_start + 2*n; }
这样我们就可以写下如下的测试代码了:
SLD::vector<int> test0; SLD::vector<int> test1(10,4); SLD::vector<int> test2(10); SLD::vector<int> test3(test1); SLD::vector<int> test4(test1.begin(), test1.end());
vector必须支持迭代功能,说到迭代功能,不得不说下STL种的迭代器。
迭代器中STL中扮演着很重要的角色. STL的核心思想就是:将数据结构和算法分离。
而迭代器就是作为数据结构和算法的粘合层而存在的。在STL中,每一个容器而有一个自己
的迭代器, 而vector的迭代器就是一个普通的指针。所以在vector中实现迭代功能就非常
的简单了。代码如下:
T* begin(){return m_start;} T* end(){return m_finish;}
然后调用的使用只需:
for (int* p = test1.begin(); p != test1.end(); ++p) { std::cout<<*p<<std::endl; }
vector 提供了计算大小, 容量, 是不是空 等一系列的判断,实现起来也非常简单:
size_t size(){return size_t(m_finish - m_start);} size_t capacity() const { return size_t(m_end_of_storage - m_start); } bool empty()const {return begin() == end()}
在使用vector的时候,可以吧它认为是一种全新的数据类型, 所以[], ==, != , <= 等运算符
的重载是必须的。首先我们看下它是如何进行[]和= 的:
T& operator[](size_t n) {return *(begin() + n);} template <class T1> vector<T1>& operator=(const vector<T1>& x) { if (&x != this) { const size_t xlen = x.size(); if (xlen > capacity()) { //从新分配内存 delete [] m_start; Base[xlen]; } else (size() >= xlen ) { T1*p = copy(x.begin(), x.end(), begin()); delete [] p; //删除多余的 } else { copy(x.begin(), x.begin() + size(), m_start); uninitialized_copy(x.begin() + size(), x.end(), m_finish); } m_finish = m_start + xlen; } return *this; }
有了实现我们就可以这样使用它了:
test0 = test1;
std::cout<<test0[2]<<endl;
然后我们在来看下几个比较运算符的实现:
template <class T> inline bool operator==(const vector<T>& x, const vector<T>& y) { return (x.size() == y.size()) && equal(x.begin(), x.end(), y.begin(), y.end()); //这里直接用了algorithm中的equal算法 } template <class T> inline bool operator< (const vector<T>& x, const vector<T>& y) { return lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); //还是借用了algorithm中的算法 } template <class T> inline bool operator!= (const vector<T>& x, const vector<T>& y) { return !(x == y); } template <class T> inline bool operator>= (const vector<T>& x, const vector<T>& y) { return !(x <y); }
实现起来非常简单了。
vector的元素的操作非常多, 这里我就选取其中典型的几个来分析下:
void push_back(const T& x)//在末尾增加一个 { if (m_finish != m_end_of_storage) { construct(m_finish, x); ++m_finish; } //else // _M_insert_aux(end(), __x);重新分配内存,这里就不继续了 } void pop_back()//在末尾减去一个 { --m_finish; destroy(m_finish); } T* erase(T* position) //将 postition 位置上的元素移除,返回下一个元素 { if (position + 1 != end()) copy(position + 1, m_finish, position); --m_finish; destroy(m_finish); return position; } T* erase(T* first, T* last)//移除区间[first)中的元素, 返回下一个元素 { T* i = copy(last, m_finish, first); destroy(i, m_finish); m_finish = m_finish - (last - first); return first; } void clear() { erase(begin(), end()); } //然后这里是在class vector 之外了, 定义了连个函数: template<class _T1,class _T2> inline void construct(_T1 _FARQ *_Ptr, const _T2& _Val) { // construct object at _Ptr with value _Val void _FARQ *_Vptr = _Ptr; ::new (_Vptr) _T1(_Val); } template<class _Ty> inline void destroy(_Ty _FARQ *_Ptr) { // destroy object at _Ptr _DESTRUCTOR(_Ty, _Ptr); }
这几个函数的实现也比较简单, 以 push_back 为例子, 首先它会判断此容器的容量是不是满了, 如果慢了
则需要重新申请内存了, 如果还没有满 则增加一个值吗, 然后调整m_finish的位置。而pop_back, 则刚好
相反,减少一个值,然后改变m_finish的位置。
vector的原理简单, 但是实现起来就复杂了。复杂度和功能是成正比的, STL的野心是很庞大的,导致了
内部实现的代码就复杂了。 不过话说回来,C 语言的哲学理念是:Kiss(keep it simple stupid)(武断,错误,
或者应该更正为simple,但是这里不改了)。 但是C++ 的 哲学理念是什么呢?Perfect?Complex?All-purpose?