看侯捷老师的《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. ActiveMQ持久化消息(转)

    ActiveMQ的另一个问题就是只要是软件就有可能挂掉,挂掉不可怕,怕的是挂掉之后把信息给丢了,所以本节分析一下几种持久化方式: 一.持久化为文件 ActiveMQ默认就支持这种方式,只要在发消息时设 ...

  2. Spark配置&启动脚本分析

    本文档基于Spark2.0,对spark启动脚本进行分析. date:2016/8/3 author:wangxl Spark配置&启动脚本分析 我们主要关注3类文件,配置文件,启动脚本文件以 ...

  3. 配置chrome支持本地(file协议)ajax请求

    将html代码拖拽进入chrome通过file协议浏览时,发送的ajax请求本地文件,会报跨域错误. XMLHttpRequest cannot load file:///E:/webs/extJS/ ...

  4. HTML5简单入门系列(三)

    前言 本篇介绍HTML5支持的Web存储(Web Storage)和HTML 5 应用程序缓存. 客户端存储数据介绍 HTML5 提供了两种在客户端存储数据的新方法: localStorage - 没 ...

  5. Python之路第十二天,高级(6)-paramiko

    paramiko 一.安装 pip3 install paramiko 二.使用 SSHClient 用于连接远程服务器并执行基本命令 1. 基于用户和密码链接 import paramiko # 创 ...

  6. Python学习笔记一,输入输出

    输出:用print()在括号中加上字符串,就可以向屏幕上输出指定的文字.比如输出'hello, world',用代码实现如下>>>print('hello,world') 也可以是多 ...

  7. syslinux 3.84 2009-12-18 ebios copyright (c) 1994-2009 H. Peter Anvin et al

    使用USB方式安装win7+Ubuntu12.04双系统时出现"syslinux 3.84 2009-12-18 ebios copyright (c) 1994-2009 H. Peter ...

  8. node.js及相关组件安装

    第一步:下载安装文件(下载地址:官网http://www.nodejs.org/download/ )第二步:安装nodejs(双击直接安装) 安装完成后使用命令行查看版本信息,出现版本号说明安装成功 ...

  9. 2013Q1全球网速排名 韩国第1美国第9 中国呐?(图)

    德国著名统计公司Statista,最近公布了2013 Q1季度全球互联网网速排名数据.其中,韩国以平均14.2Mbps的网速继续排名世界第一. 此外,第二至第十名的国家和地区分别为:日本(11.7 M ...

  10. express设置ejs并将后缀改为html

    http://www.cnblogs.com/-nothing-/p/4943354.html http://blog.csdn.net/macyang/article/details/8841966 ...