看侯捷老师的《STL源码剖析》有一段时间了,打算自己整理一下思路,试着实现一下。主要目的有两个:1、巩固自己对源码的理解,让自己更加深刻的体会其中各种机制的奥妙。别人的知识永远是别人的,只有自己亲自动手实践过,才能转化为自己的。2、通过实现这些优秀的算法,来提高自己的“内功”修养。

vector其实就是动态数组。它的性质与数组基本一致。同样使用的是一片连续的内存空间。它与数组相比,其优点是灵活性较强,其空间大小可变。但是其效率没有数组高。这是因为受到了其空间配置器的影响。SGI-STL中,为了减少内存碎片,在空间配置方面有很多机制。创建一个vector时,需要分配填充内存池、填充free-list、分配内存等一系列操作。这使得它的效率稍微低了些。此外,当空间不足,需要扩充空间时,SGI-STL采用“三步走”的措施,即:分配新的空间——将原来数据拷贝过去——释放原来的空间。我们在使用时,应该尽量避免扩充vector的空间。还有一点应当注意,当写出如下代码的时候,虽然分配了10个元素的空间,当你紧接着再往容器中添加数据时,同样会扩充空间。也就是程序执行完成第2行代码时,其正在的元素个数已经变成了11,而不是原来的10。其具体原因请看源代码

vector<int> Vec(10);
Vec.push_back(10);

  以下是我自己根据源代码,仿造的vector。

 #ifndef CVECTOR_H_
#define CVECTOR_H_ #include "DefaultAllocTemplate.h"
#include "AllocatorInterface.h"
#include "comm.h"
#include <iterator.h>
#include <new.h>
#include <stdlib.h>
#include <unistd.h> template <class T, class Alloc = CDefaultAllocTemplate>
class CVector {
/*以下是Vector的迭代器类型*/
public:
typedef T ValueType;
typedef ValueType* Pointer;
typedef ValueType* Iterator;
typedef ValueType& Reference;
typedef size_t SizeType;
typedef ptrdiff_t DifferenceType;
protected: /*空间配置器*/
typedef AllocatorInterface<ValueType,Alloc>DataAllocator; /*目前正在使用的空间的头部*/
Iterator m_Start; /*目前正在使用空间的尾部*/
Iterator m_Finish; /*可用空间的尾部,它与m_Start决定了容器的最大容量*/
Iterator m_EndOfStorage; /*向Vetory中插入一个成员*/
void InsertAux(Iterator Pos, const T& x); /*销毁容器*/
void Deallocate()
{
if( != m_Start)
{
/*调用空间配置器接口中的函数销毁销毁容器*/
DataAllocator::Deallocate(m_Start, m_EndOfStorage - m_Start);
}
} /*创建容器,并为其分配存储空间。该函数可以获取uiNum个对象的内存空间,
*并将内存指针保存在m_Start中。以后的构造函数中,都会调用这个函数*/
void FillInitialize(SizeType uiNum, const T& Value)
{
m_Start = AllocateAndFill(uiNum, Value);
/*注意,这里会使得原来的你在创建vector的时候,无论你申请了多少个元素的空间,当你(即使是申请后马上)
*调用PushBack函数时,也会扩大原来的内存空间*/
m_Finish = m_Start + uiNum;
m_EndOfStorage = m_Finish;
} /*配置空间并填充内容*/
Iterator AllocateAndFill(SizeType uiNum, const T& Value)
{
Iterator Result=DataAllocator::Allocate(uiNum);
//调用全局函数为对象配置内存
uninitialized_fill_n(Result, uiNum, Value);
return Result;
} public:
/*获取容器起始位置*/
Iterator Begin()
{
return m_Start;
} /*获取容器中正在使用空间的尾部*/
Iterator End()
{
return m_Finish;
} /*返回容器中元素个数*/
SizeType Size()
{
return (SizeType)(m_Finish - m_Start);
} SizeType Capacity()
{
return (SizeType)(m_EndOfStorage - m_Start);
} /*判断容器是否为空*/
int Empty()
{
return m_Start == m_Finish;
} /*重载"[]"符号,让容器可以通过下标获取元素*/
Reference operator[](SizeType uiNum)
{
return *(m_Start + uiNum);
} /*无参构造函数,创建一个空的容器*/
CVector() : m_Start(), m_Finish(), m_EndOfStorage()
{
//这里面本身就不需要任何内容
} /*以下三个构造函数功能一样,都是创建一个可以容纳uiNum个对象的容器。并且会将Value中的数据拷贝uiNum个过去。
*只是其中的uiNum类型不一样,这里写三个是为了兼容不同的类型*/
CVector(SizeType uiNum, const T& Value)
{
/*这三行是为了消除警告*/
m_Start = ;
m_Finish = ;
m_EndOfStorage = ; /*调用函数分配内存空间*/
FillInitialize(uiNum, Value);
} CVector(int uiNum, const T& Value)
{
/*这三行是为了消除警告*/
m_Start = ;
m_Finish = ;
m_EndOfStorage = ; /*调用函数分配内存空间*/
FillInitialize(uiNum, Value);
} CVector(long uiNum, const T& Value)
{
/*这三行是为了消除警告*/
m_Start = ;
m_Finish = ;
m_EndOfStorage = ; /*调用函数分配内存空间*/
FillInitialize(uiNum, Value);
} /*创建能容纳uiNum个对象的容器*/
explicit CVector(long uiNum)
{
/*这三行是为了消除警告*/
m_Start = ;
m_Finish = ;
m_EndOfStorage = ; /*调用函数分配内存空间,其中的T()将会创建一个临时变量。但是为什么需要这样去创建呢??
*为了让该函数能够调用,所以创建一个临时对象*/
FillInitialize(uiNum, T());
} explicit CVector(int uiNum)
{
/*这三行是为了消除警告*/
m_Start = ;
m_Finish = ;
m_EndOfStorage = ; /*调用函数分配内存空间,其中的T()将会创建一个临时变量。但是为什么需要这样去创建呢??
*为了让该函数能够调用,所以创建一个临时对象*/
FillInitialize(uiNum, T());
} explicit CVector(SizeType uiNum)
{
/*这三行是为了消除警告*/
m_Start = ;
m_Finish = ;
m_EndOfStorage = ; /*调用函数分配内存空间,其中的T()将会创建一个临时变量。但是为什么需要这样去创建呢??
*为了让该函数能够调用,所以创建一个临时对象*/
FillInitialize(uiNum, T());
} ~CVector()
{
/*一个对象的销毁分为两步完成:
*1、释放容器的内存空间
*2、销毁容器*/
destroy(m_Start, m_EndOfStorage);//释放容器占用的内存空间
Deallocate();//调用容器中的函数,销毁容器
} /*返回第一个元素*/
Reference Front()
{
return *(m_Start);
} /*返回最后一个元素*/
Reference Back()
{
return *(m_Finish - );
} /*向vector的尾部添加一个元素*/
void PushBack(const T& Element)
{
/*判断容器是否已经满了,如果没有满,则直接进行插入。如果满了,则需要调用InsertAux函数,
*为容器重新分配内存空间*/
if(m_Finish != m_EndOfStorage)
{
construct(m_Finish, Element);
++m_Finish;
}
else
{
InsertAux(End(), Element);
}
} /*将容器中最后一个元素取出来*/
void PopBack()
{
--m_Finish;
destory(m_Finish);
} /*清除指定位置的某个元素*/
Iterator Erase(Iterator Position)
{
if(Position + != End())
{
copy(Position + , m_Finish, Position);//将后面的数据向前移动
}
m_Finish--;
destroy(m_Finish);//调用全局函数,释放多余的空间
return Position;
} /*清除指定某区域中的元素*/
Iterator Erase(Iterator StartPosition, Iterator EndPosition)
{
if(EndPosition + != End())
{
copy(EndPosition + , m_Finish, StartPosition);//将后面的数据向前移动
}
m_Finish -= (StartPosition - EndPosition); //调整末尾指针
destroy(m_Finish, StartPosition - EndPosition);//调用全局函数,释放多余的空间
return StartPosition;
} /*重新为容器分配空间,其大小为uiNewSize,如果新的空间大于原来的空间,
*则新增加的空间将会以InitialValue作为初值进行初始化;如果新的空间
*小于原来的空间,将会把多余的数据清除掉*/
void Resize(SizeType uiNewSize, const T& InitialValue)
{
if(uiNewSize < Size())
{
Erase(Begin() + uiNewSize, End());//空间过小,清除元素
}
else
{
insert(End(), uiNewSize - Size(), End());
}
} /*重新为容器分配空间,其大小为uiNewSize。但是用户可以不进行初始化*/
void Resize(SizeType uiNewSize)
{
Resize(uiNewSize, T());
} /*清空容器中的元素*/
void Clear()
{
Erase(Begin(), End());
}
}; template <class T, class Alloc>
void CVector<T, Alloc>::InsertAux(Iterator Position, const T& Element)
{
if(m_Finish != m_EndOfStorage) //还有备用空间
{
construct(m_Finish, *(m_Finish - ));//在备用空间中构造一个
m_Finish++;//调整备用空间剩余容量
T ElementCpoy = Element;//将要插入的节点进行拷贝
/*调用全局函数,为要插入的节点移出一块内存空间*/
copy_backward(Position, m_Finish - , m_Finish - );
*Position = ElementCpoy;//将节点插入
}
else//没有备用空间了
{
const SizeType uiOldSize = Size();//计算原来空间的长度
/*得到需要的长度,若原来大小不为零,则新开辟的大小应该为其原本大小的两倍,反之则为1*/
const SizeType uiLen = uiOldSize != ? * uiOldSize : ;
Iterator NewStart = DataAllocator::Allocate(uiLen);//开辟内存并且将新的内存地址保存
Iterator NewFinish = NewStart;
try
{
/*将vector中的数据拷贝到新的vector内存中*/
NewFinish = uninitialized_copy(m_Start, Position, NewStart);
/*为新的元素设定值*/
construct(NewFinish, Element);
++NewFinish;
/*将备用空间的内容也拷贝过来,这可能没什么用!!*/
NewFinish = uninitialized_copy(Position, m_Finish, NewFinish);
}
catch(...)
{
destroy(NewStart, NewFinish);
DataAllocator::Deallocate(NewStart, uiLen);
throw;
} destroy(Begin(), End());
Deallocate(); m_Start = NewStart;
m_Finish = NewFinish;
m_EndOfStorage = NewStart + uiLen;
}
} #endif

以上代码更多的是为自己的学习做个笔记,欢迎交流讨论!

2、vector的实现的更多相关文章

  1. c++ vector 使用

    1. 包含一个头文件: 1 #include <vector> 2. 申明及初始化: std::vector<int> first; // empty vector of in ...

  2. Vector Tile

    Mapbox Vector Tile Specification A specification for encoding tiled vector data. <?XML:NAMESPACE ...

  3. ArrayList、Vector、LinkedList的区别联系?

    1.ArrayList.Vector.LinkedList类都是java.util包中,均为可伸缩数组. 2.ArrayList和Vector底层都是数组实现的,所以,索引数据快,删除.插入数据慢. ...

  4. ArrayList、Vector、HashMap、HashSet的默认初始容量、加载因子、扩容增量

    当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全部复制到新的内存上,这无疑使效率大大降低. 加载因 ...

  5. Java中Vector和ArrayList的区别

    首先看这两类都实现List接口,而List接口一共有三个实现类,分别是ArrayList.Vector和LinkedList.List用于存放多个元素,能够维护元素的次序,并且允许元素的重复.3个具体 ...

  6. C++使用vector

    #include <iostream> #include <string> #include <vector> using namespace std; void ...

  7. [LeetCode] Flatten 2D Vector 压平二维向量

    Implement an iterator to flatten a 2d vector. For example,Given 2d vector = [ [1,2], [3], [4,5,6] ] ...

  8. C++ 数组array与vector的比较

    转:http://blog.csdn.net/yukin_xue/article/details/7391897 1. array 定义的时候必须定义数组的元素个数;而vector 不需要: 且只能包 ...

  9. vector定义初始化

    头文件 #include<vector> using std::vector; vector<T> v1; vector<T> v2(v1); vector< ...

  10. vector迭代器用法

    #include<iostream> #include<vector> using namespace std; int main() { vector<int> ...

随机推荐

  1. 01 日志组件XLog

    本文地址为:http://www.cnblogs.com/ADTL/p/5357259.html XLog为XCode的日志组件,为系统基本功能. 使用示例: 1.新建WinForm程序 2.引用Ne ...

  2. mongo db 使用方法

    1 下载 mogodb http://www.mongodb.org/display/DOCS/Downloads 2 打开服务 我安装在e盘下了 可以指定数据文件位置 到 E:\mongoDB\mo ...

  3. ERP行业推荐参考书籍

    1 书名:<ERP 理论.方法与实践> 作者: 周玉清等编著 出版社:电子工业出版社 简介:本书全面介绍了ERP的基本原理和处理逻辑,以大量篇幅讨论了ERP的计划功能,特别是主生产计划功能 ...

  4. GitHub项目协作基本步骤

    1.查找某个项目,然后Fork 2.打开GitHub For Windows,发现刚才Fork的项目 3.对着项目点击Clone,将之复制至本地 4.使用Eclipse等进行开发,如新增一个文件 5. ...

  5. MySQL行级锁、表级锁、页级锁详细介绍

    原文链接:http://www.jb51.net/article/50047.htm 页级:引擎 BDB.表级:引擎 MyISAM , 理解为锁住整个表,可以同时读,写不行行级:引擎 INNODB , ...

  6. [C++程序设计]字符数组的赋值与引用

    只能对字符数组的元素赋值,而不能用赋值语句对整个数组赋值. char c[5]; c={′C′,′h′,′i′,′n′,′a′}; //错误,不能对整个数组一次赋值 c[0]=′C′; c[1]=′h ...

  7. PHP中__autoload()的不解之处,求高手指点

    一整段代码: 运行结果: 使用__autoload(),分为两页代码: 第一段代码: ACMEManager.php,代码如下: 运行结果:

  8. iOS GCD使用整理

    自己进行一个复习整理 1.最简单的用法 全局并行 dispatch_async(dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_ ...

  9. PHP中output control

    Output Control 函数可以让你自由控制脚本中数据的输出.它非常地有用,特别是对于:当你想在数据已经输出后,再输出文件头的情况.输出控制函数不对使用 header() 或 setcookie ...

  10. javascript之Date

    JSON 日期转 JS日期,我们知道,日期类型转成JSON之后,返回的数据类似这样: /Date(1379944571737)/ 但是这种日期并不能直接显示,因为根本没有人知道这是什么意思,下面提供一 ...