4.STL简单容器(cghVector)的实现
Posted chengonghao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4.STL简单容器(cghVector)的实现相关的知识,希望对你有一定的参考价值。
我用VS2013写的程序(github ),vector版本的代码位于cghSTL/version/cghSTL-0.3.1.rar
我是照着侯捷老师的《STL源码剖析》做的cghSTL,现在才看到第三章,觉得这本书的编排非常适合自学。
第一章讲解空间配置器,这是STL最基础的部件,没什么好说的。
第二章讲解迭代器,老师举了单向链表的例子,这非常有考究,单向链表不需要连续的存储空间,意味着使用单向链表实现的容器不需要考虑空间溢出等问题,我们可以把注意力集中在容器的迭代器上。
第三章讲解序列式容器,第一节介绍vector的实现,vector需要考虑空间的连续使用和空间溢出等问题,而正因为vector维护的是连续线性空间,所以不论其元素类型为何,普通指针都可以作为vector的迭代器而满足所有必要条件!这成功的避开了设计容器时不得不分散精力设计迭代器的坑!
侯捷老师写书真是有水平啊!
言归正传,接下来我们看看一个vector的实现细节。
我们一共有四个文件:
1. globalConstruct.h,构造和析构函数文件,位于allocator/cghAllocator
2. cghAlloc.h,空间配置器文件,位于allocator/cghAllocator文件夹下
3. cghVector.h,容器的实现文件,位于cghSTL/sequence containers/cghVector/
4. cghSTL.cpp,测试文件,位于cghSTL/test/
先看第一个,globalConstruct.h构造函数文件
/*******************************************************************
* Copyright(c) 2016 Chen Gonghao
* All rights reserved.
*
* chengonghao@yeah.net
*
* 功能:全局构造和析构的实现代码
******************************************************************/
#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, const T2& value)
{
new (p)T1(value);
}
template<class T>
inline void destroy(T* pointer)
{
pointer->~T();
}
template<class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last)
{
// 本来在这里要使用特性萃取机(traits编程技巧)判断元素是否为non-trivial
// non-trivial的元素可以直接释放内存
// trivial的元素要做调用析构函数,然后释放内存
for (; first < last; ++first)
destroy(&*first);
}
#pragma endregion
}
#endif
按照STL的接口规范,正确的顺序是先分配内存然后构造元素。构造函数的实现采用placement new的方式;为了简化起见,我直接调用析构函数来销毁元素,而在考虑效率的情况下一般会先判断元素是否为non-trivial类型。
关于 trivial 和 non-trivial 的含义,参见:stack overflow
cghAlloc.h是空间配置器文件,空间配置器负责内存的申请和回收。
/*******************************************************************
* Copyright(c) 2016 Chen Gonghao
* All rights reserved.
*
* chengonghao@yeah.net
*
* 功能:cghAllocator空间配置器的实现代码
******************************************************************/
#ifndef _CGH_ALLOC_
#define _CGH_ALLOC_
#include <new>
#include <cstddef>
#include <cstdlib>
#include <climits>
#include <iostream>
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 << "out of 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_type n, const void* hint = 0)
{
return _allocate((difference_type)n, (pointer)0);
}
static void deallocate(pointer p, size_type n)
{
_deallocate(p);
}
void construct(pointer p, const T& value)
{
_construct(p, value);
}
void destroy(pointer p)
{
_destroy(p);
}
pointer address(reference x)
{
return (pointer)&x;
}
const_pointer const_address(const_reference x)
{
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_t n)
{
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的实现代码如下:
cghVector.h
/*******************************************************************
* Copyright(c) 2016 Chen Gonghao
* All rights reserved.
*
* chengonghao@yeah.net
*
* 功能:cghVector容器的实现代码
******************************************************************/
#ifndef _CGH_VECTOR_
#define _CGH_VECTOR_
#include "globalConstruct.h"
#include "cghAlloc.h"
#include <memory>
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_type len = 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::exception ex)
{
// 如果执行失败就要回滚
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_type n, const T& value)
{
start = allocate_and_fill(n, value);
finish = start + n;
end_of_storage = finish;
}
iterator allocate_and_fill(size_type n, 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{ return size_type(end_of_storage - start); } // cghVector的容量 = 最大容量地址 - 头元素地址
bool empty()const { return begin() == end(); } // cghVector是否为空:头元素地址 是否等于 尾元素地址
reference operator[](size_type n){ 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.
*
* chengonghao@yeah.net
*
* 文件名称: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;
}
可以看到当cghVector的大小(size)超过容量(capacity)时,cghVector自动扩容,每次扩充为原来的2倍。
以上是关于4.STL简单容器(cghVector)的实现的主要内容,如果未能解决你的问题,请参考以下文章