4.STL简单容器(cghVector)的实现

         我是照着侯捷老师的《STL源码剖析》做的cghSTL,现在才看到第三章,忽然觉得这本书的编排非常适合自学。

源码下载:github

第一章讲解空间配置器,这是STL最基础的部件,没什么好说的。


第二章讲解迭代器,老师举了单向链表的例子,这非常有考究,单向链表不需要连续的存储空间,意味着使用单向链表实现的容器不需要考虑空间溢出等问题,我们可以把注意力集中在容器的迭代器上。


第三章讲解序列式容器,一来说怎么实现vector,vector需要考虑空间的连续使用和空间溢出等问题,正因为vector维护的是连续线性空间,所以不论其元素类型为何,普通指针都可以作为vector的迭代器而满足所有必要条件!这成功的避开了设计容器时不得不分散精力设计迭代器的坑!


到此,我觉得侯捷老师写书真是有水平啊!


言归正传,接下来我们看看一个vector的实现细节。


我们一共有四个文件:

1.      globalConstruct.h,构造和析构函数文件

2.      cghAlloc.h,空间配置器文件

3.      cghVector.h,容器的实现文件

4.      cghSTL.cpp,测试文件

 

先看第一个,globalConstruct.h构造函数文件

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  [email protected]
*
*  功能:全局构造和析构的实现代码
******************************************************************/
 
#include "stdafx.h"
#include<new.h>
#include<type_traits>
#ifndef_CGH_GLOBAL_CONSTRUCT_
#define_CGH_GLOBAL_CONSTRUCT_
 
 
namespace CGH
{
         #pragma region 统一的构造析构函数
         template<class T1, class  T2>
         inline void construct(T1* p, constT2& value)
         {
                   new (p)T1(value);
         }
 
         template<class T>
         inline void destroy(T* pointer)
         {
                   pointer->~T();
         }
 
         template<class ForwardIterator>
         inline void destroy(ForwardIteratorfirst, ForwardIterator last)
         {
                   // 本来在这里要使用特性萃取机(traits编程技巧)判断元素是否为non-trivial
                   // non-trivial的元素可以直接释放内存
                   // trivial的元素要做调用析构函数,然后释放内存
                   for (; first < last;++first)
                            destroy(&*first);
         }
         #pragma endregion
}
 
#endif


按照STL的接口规范,正确的顺序是先分配内存然后构造元素。构造函数的实现采用placement new的方式;为了简化起见,我直接调用析构函数来销毁元素,而在考虑效率的情况下一般会先判断元素是否为non-trivial类型,non-trivial的元素可以直接释放内存,可以理解为non-trivial全部分配在栈上,不用我们操心,由程序自动回收,trivial的元素要分配在堆上,需要手工调用析构函数来销毁,然后释放内存。

 

cghAlloc.h是空间配置器文件,空间配置器不仅要负责内存的申请和回收,也负责对象的构造和析构,故globalConstruct.h包含在cghAlloc.h中。

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  [email protected]
*
*  功能:cghAllocator空间配置器的实现代码
******************************************************************/
 
#include <new>
#include <cstddef>
#include <cstdlib>
#include <climits>
#include <iostream>
 
#ifndef_CGH_ALLOC_
#define _CGH_ALLOC_
 
 
namespace CGH
{
         #pragma region 内存分配和释放函数、元素的构造和析构函数
         // 内存分配
         template<class T>
         inline T* _allocate(ptrdiff_t size, T*)
         {
                   set_new_handler(0);
                   T* tmp = (T*)(::operator new((size_t)(size* sizeof(T))));
                   if (tmp == 0)
                   {
                            std::cerr << "outof memory" << std::endl;
                            exit(1);
                   }
                   return tmp;
         }
 
         // 内存释放
         template<class T>
         inline void _deallocate(T* buffer)
         {
                   ::operator delete(buffer);
         }
 
         // 元素构造
         template<class T1, class  T2>
         inline void _construct(T1* p, const T2&value)
         {
                   new(p)T1(value);
         }
 
         // 元素析构
         template<class T>
         inline void _destroy(T* ptr)
         {
                   ptr->~T();
         }
         #pragma endregion
 
         #pragma region cghAllocator空间配置器的实现
         template<class T>
         class cghAllocator
         {
         public:
                   typedef T                    value_type;
                   typedef T*                           pointer;
                   typedef const T*      const_pointer;
                   typedef T&                          reference;
                   typedef const T&     const_reference;
                   typedef size_t           size_type;
                   typedef ptrdiff_t      difference_type;
 
                   template<class U>
                   struct rebind
                   {
                            typedef cghAllocator<U>other;
                   };
 
                   static pointer allocate(size_typen, const void* hint = 0)
                   {
                            return _allocate((difference_type)n,(pointer)0);
                   }
 
                   static void deallocate(pointerp, size_type n)
                   {
                            _deallocate(p);
                   }
 
                   void construct(pointer p, constT& value)
                   {
                            _construct(p, value);
                   }
 
                   void destroy(pointer p)
                   {
                            _destroy(p);
                   }
 
                   pointer address(reference x)
                   {
                            return (pointer)&x;
                   }
 
                   const_pointer const_address(const_referencex)
                   {
                            return (const_pointer)&x;
                   }
 
                   size_type max_size() const
                   {
                            return size_type(UINT_MAX/ sizeof(T));
                   }
         };
         #pragma endregion
 
         #pragma region 封装STL标准的空间配置器接口
         template<class T, class Alloc = cghAllocator<T>>
         class simple_alloc
         {
         public:
                   static T* allocate(size_t n)
                   {
                            return 0 == n ? 0 : (T*)Alloc::allocate(n*sizeof(T));
                   }
 
                   static T* allocate(void)
                   {
                            return (T*)Alloc::allocate(sizeof(T));
                   }
 
                   static void deallocate(T* p, size_tn)
                   {
                            if (0 != n)Alloc::deallocate(p,n*sizeof(T));
                   }
 
                   static void deallocate(T* p)
                   {
                            Alloc::deallocate(p,sizeof(T));
                   }
         };
         #pragma endregion
}
 
#endif


class cghAllocator是空间配置器类的定义,主要的四个函数的意义如下:allocate函数分配内存,deallocate函数释放内存,construct构造元素,destroy析构元素。这四个函数最终都是通过调用_allocate、_deallocate、_construct、_destroy这四个内联函数实现功能。

我们自己写的空间配置器必须封装一层STL的标准接口,

template<classT, class Alloc = cghAllocator<T>>
class simple_alloc


 

vector的实现代码如下:

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  [email protected]
*
*  功能:cghVector容器的实现代码
******************************************************************/
 
#include "globalConstruct.h"
#include "cghAlloc.h"
#include <memory>
 
#ifndef_CGH_VECTOR_
#define _CGH_VECTOR_
 
namespace CGH
{
         template<class T, class Alloc = cghAllocator<T>>
         class cghVector
         {
         public:
                   typedef T                             value_type;
                   typedef value_type*                  pointer;
                   typedef value_type*                  iterator;
                   typedef value_type&                 reference;
                   typedef size_t                    size_type;
                   typedef ptrdiff_t                difference_type;
 
                   //typedef __false_type    has_trivial_default_constructor;
                   //typedef __false_type    has_trivial_copy_destructor;
                   //typedef __false_type    has_trivial_assignment_operator;
                   //typedef __false_type    has_trivial_destructor;
                   //typedef __false_type    is_POD_type;
 
         protected:
                   typedef simple_alloc<value_type,Alloc> data_allocator; // 定义空间配置器
                   iterator start;
                   iterator finish;
                   iterator end_of_storage;
 
                   void insert_aux(iterator position,const T& value)
                   {
                            if (finish != end_of_storage)
                            {
                                     construct(finish,*(finish - 1));
                                     ++finish;
                                     T x_copy = value;
                                     std::copy_backward(position,finish - 2, finish - 1);
                                     *position =x_copy;
                            }
                            else
                            {
                                     ptrdiff_t old_size= size();
                                     const size_typelen = old_size != 0 ? 2 * old_size : 1;
                                     /*
                                               配置原则:如果原大小为0,则配置1个元素大小
                                               如果原大小不为0,则配置原大小的两倍
                                     */
                                     iterator new_start= data_allocator::allocate(len);
                                     iterator new_finish= new_start;
                                     try
                                     {
                                               //把 start 到 position 这段内存拷贝到 new_start 处,返回 new_finish = new_start + ( position - start )
                                               new_finish= std::uninitialized_copy(start, position, new_start);
                                               construct(new_finish,value); // 在 new_finish 处构造新元素                                        ++new_finish;
                                               //new_finish= std::uninitialized_copy(position, finish, new_finish);
                                     }
                                     catch (std::exceptionex)
                                     {
                                               //如果执行失败就要回滚
                                               destroy(new_start,new_finish);
                                               data_allocator::deallocate(new_start,len);
                                               throw;
                                     }
                                     destroy(begin(),end());
                                     deallocate();
                                     start = new_start;
                                     finish = new_finish;
                                     end_of_storage= new_start + len;
                            }
                   }
 
                   #pragma region 释放内存和析构元素
                   void deallocate()
                   {
                            if (start)
                            {
                                     data_allocator::deallocate(start,end_of_storage - start);
                            }
                   }
                   #pragma endregion
 
                   #pragma region 分配内存和构造元素
                   /*
                    *     fill_initialize和allocate_and_fill把cghVector的初始化分为了两步:
                    *     1.fill_initialize的职责是分配一段内存
                    *     2.fill_initialize调用allocate_and_fill,在分配的内存中调用构造函数创建cghVector的元素
                    */
                   void fill_initialize(size_typen, const T& value)
                   {
                            start = allocate_and_fill(n,value);
                            finish = start + n;
                            end_of_storage = finish;
                   }
 
                   iterator allocate_and_fill(size_typen, const T& x)
                   {
                            iterator result = data_allocator::allocate(n);
                            iterator cur = result;
                            for (; n > 0; --n,++cur)
                            {
                                     construct(&*cur,x);
                            }
                            return result;
                   }
                   #pragma endregion
         public:
                   #pragma region 对cghVector的读操作
                   iterator begin(){ return start;} // 返回cghVector头元素的地址
                   iterator end(){ return finish;} // 返回cghVector尾元素的地址
                   size_type size(){ return size_type(int(end()- begin())); } // cghVector的长度 = 尾元素地址 - 头元素地址
                   size_type capacity()const{ returnsize_type(end_of_storage - start); } // cghVector的容量 = 最大容量地址 - 头元素地址
                   bool empty()const { return begin()== end(); } // cghVector是否为空:头元素地址是否等于 尾元素地址
                   reference operator[](size_typen){ return *(begin() + n); } // 返回指定位置的元素引用
                   reference front(){ return *begin();} // 获得头元素位职
                   reference back(){ return *(end()- 1); } // 获得尾元素位置
                   #pragma endregion
 
                   #pragma region 构造函数
                   cghVector() :start(0), finish(0),end_of_storage(0) { } // 初始化空的cghVector
                   cghVector(size_type n, const T&value){ fill_initialize(n, value); } // 初始化包含n个值为value的cghVector
                   cghVector(int n, const T&value){ fill_initialize(n, value); } // 同上
                   cghVector(long n, const T&value){ fill_initialize(n, value); } // 同上
                   explicit cghVector(size_type n){fill_initialize(n, T()); } // 初始化cghVector的长度为n
                   #pragma endregion
 
                   #pragma region 析构函数
                   ~cghVector()
                   {
                            destroy(start, finish);// 先调用cghVector中元素的析构函数
                            deallocate(); // 再释放cghVector占用的内存
                   }
                   #pragma endregion
 
                   #pragma region 对cghVector的写操作
                   /**
                   *  弹出尾元素
                   */
                   void pop_back()
                   {
                            --finish;
                            destroy(finish);
                   }
 
                   /**
                   *  在cghVector末尾插入一个元素
                   */
                   void push_back(const T& x)
                   {
                            // 判断cghVector的容量是否满了,如果没满我们直接在已有的内存区域上构造元素
                            if (finish != end_of_storage)
                            {
                                     construct(finish,x);
                                     ++finish;
                            }
                            else // 如果满了我们就要重新分配内存并重新构造函数
                            {
                                     insert_aux(end(),x);
                            }
                   }
 
                   /**
                   *  清除[first, last)区间内的元素
                   */
                   iterator erase(iterator first,iterator last)
                   {
                            // 把last到finish这段元素拷贝以first作为起始处的内存空间,返回first+ ( finish - last ) 的地址
                            iterator i = std::copy(last,finish, first);
                            destroy(i, finish); //销毁(first + ( finish - last ), finish]这段内存
                            finish = finish - (last- first); // 重新设置finish
                            return first;
                   }
 
                   /**
                   *  清除某个位置上的元素
                   */
                   iterator erase(iterator position)
                   {
                            if (position + 1 != end())
                            {
                                     copy(position+ 1, finish, position);
                            }
                            --finish;
                            destroy(finish);
                            return position;
                   }
                   #pragma endregion
         };
}
 
#endif

cghVector实现了pop_back、push_back、erase。

注释已经写得足够详细。 cghVector.h中调用了三个标准全局函数:std::copy_backward、std::uninitialized_copy、std::copy,这三个函数。

 

测试代码:

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  [email protected]
*
*  文件名称:cghVector容器的测试代码
******************************************************************/
 
#include "stdafx.h"
#include "cghAlloc.h"
#include "globalConstruct.h"
#include "cghVector.h"
using namespace::std;
 
int _tmain(int argc,_TCHAR* argv[])
{
         using namespace::CGH;
 
         cghVector<int> test(2, 2);
         cout << "size = " <<test.size() << "\t";
         cout << "capacity = " <<test.capacity() << endl;
 
         test.push_back(1);
         cout << "size = " <<test.size() << "\t";
         cout << "capacity = " <<test.capacity() << endl;
 
         test.push_back(2);
         cout << "size = " <<test.size() << "\t";
         cout << "capacity = " <<test.capacity() << endl;
 
         test.push_back(3);
         cout << "size = " <<test.size() << "\t";
         cout << "capacity = " <<test.capacity() << endl;
 
         test.pop_back();
         cout << "size = " <<test.size() << "\t";
         cout << "capacity = " <<test.capacity() << endl;
 
         std::cout << test[2] << endl;// 返回指定位置处的元素,这里体现了vector迭代器的随机访问性质(random access iterators)
 
         test.erase(test.begin() + 1, test.begin()+ 2);
         test.erase(test.begin());
         for (cghVector<int>::iterator it =test.begin(); it != test.end(); it++)
         {
                   std::cout << *it <<std::endl;
         }
         system("pause");
         return 0;
}

4.STL简单容器(cghVector)的实现_第1张图片

可以看到当cghVector的大小(size)超过容量(capacity)时,cghVector自动扩容,每次扩充为原来的2倍。

你可能感兴趣的:(4.STL简单容器(cghVector)的实现)