3.3.4 Vector类的简单实现
实现一个vector,绝对是C++中的重点知识。下面例3.13中提供了类的简单实现。
【例3.17】 vector类的简单实现。
#include<algorithm>
#include<iostream>
#include <assert.h>
using namespace std;
template<typename T>
class myVector
{
private:
/*walk length*/
/*myVector each time increase space length*/
#define WALK_LENGTH 64;
public:
/*default constructor*/
myVector():array(0),theSize(0),theCapacity(0){ }
myVector(const T& t,unsigned int n):array(0),theSize(0),theCapacity(0){
while(n--){
push_back(t);
}
}
/*copy constructor*/
myVector(const myVector<T>& other):array(0),theSize(0),theCapacity(0){
*this = other;
}
/*= operator*/
myVector<T>& operator =(myVector<T>& other){
if(this == &other)
return *this;
clear();
theSize = other.size();
theCapacity = other.capacity();
array = new T[theCapacity];
for(unsigned int i = 0 ;i<theSize;++i)
{
array[i] = other[i];
}
return *this;
}
/*destructor*/
~myVector(){
clear();
}
/*the pos must be less than myVector.size();*/
T& operator[](unsigned int pos){
assert(pos<theSize);
return array[pos];
}
/*element theSize*/
unsigned int size(){
return theSize;
}
/*alloc theSize*/
unsigned int capacity(){
return theCapacity;
}
/*is empty*/
bool empty(){
return theSize == 0;
}
/*clear myVector*/
void clear(){
deallocator(array);
array = 0;
theSize = 0;
theCapacity = 0;
}
/*adds an element in the back of myVector*/
void push_back(const T& t){
insert_after(theSize-1,t);
}
/*adds an element int the front of myVector*/
void push_front(const T& t){
insert_before(0,t);
}
/*inserts an element after the pos*/
/*the pos must be in [0,theSize);*/
void insert_after(int pos,const T& t){
insert_before(pos+1,t);
}
/*inserts an element before the pos*/
/*the pos must be less than the myVector.size()*/
void insert_before(int pos,const T& t){
if(theSize==theCapacity){
T* oldArray = array;
theCapacity += WALK_LENGTH;
array = allocator(theCapacity);
/*memcpy(array,oldArray,theSize*sizeof(T)):*/
for(unsigned int i = 0 ;i<theSize;++i){
array[i] = oldArray[i];
}
deallocator(oldArray);
}
for(int i = (int)theSize++;i>pos;--i){
array[i] = array[i-1];
}
array[pos] = t;
}
/*erases an element in the pos;*/
/*pos must be in [0,theSize);*/
void erase(unsigned int pos){
if(pos<theSize){
--theSize;
for(unsigned int i = pos;i<theSize;++i){
array[i] = array[i+1];
}
}
}
private:
T* allocator(unsigned int size){
return new T[size];
}
void deallocator(T* arr){
if(arr)
delete[] arr;
}
private:
T* array;
unsigned int theSize;
unsigned int theCapacity;
};
void printfVector(myVector<int>& vector1){
for(unsigned int i = 0 ; i < vector1.size();++i){
cout<<vector1[i]<<",";
}
cout<<"alloc size = "<<vector1.capacity()<<",size = "<<vector1.size()<<endl;
}
int main(){
myVector<int> myVector1;
myVector<int> myVector2(0,10);
myVector2.push_front(1);
myVector2.erase(11);
printfVector(myVector2);
myVector1.push_back(2);
myVector1.push_front(1);
printfVector(myVector1);
myVector1.insert_after(1,3);
printfVector(myVector1);
myVector2 = myVector1;
myVector2.insert_before(0,0);
myVector2.insert_before(1,-1);
printfVector(myVector2);
return 0;
}
程序的执行结果为:
1,0,0,0,0,0,0,0,0,0,0,alloc size = 64,size = 11
1,2,alloc size = 64,size = 2
1,2,3,alloc size = 64,size = 3
0,-1,1,2,3,alloc size = 64,size = 5
STL库中vector是一个自动管理的动态数组,只要明白vector的类型是一个数组,至于怎么去实现它其实不难。例3.17中选择了一种简单的方式去实现它:定义一个步长WALK_LENGTH,在数组空间不够时,再重新申请长度为theCapacity +WALK_LENGTH的内存,这样就避免了每次当myVector元素增加的时候,需要去重新申请空间的问题,当然不好的地方就是会浪费一定的空间,但是时间效率上会提高很多。因为vector可以支持下标访问,所以就不用单独构造一个iterator,从而提高效率。
例3.17中,myVector拥有3个成员变量:元素的个数theSize、容量theCapacity和一个指针数组array。
默认构造函数里,把元素的个数theSize、容量theCapacity都赋值为0,数组赋值为空,代码如下:
myVector():array(0),theSize(0),theCapacity(0){ }
用几个相同的值赋值给myVector,那应该是逐个添加的:
myVector(const T& t,unsigned int n):array(0),theSize(0),theCapacity(0){
while(n--){
push_back(t);
}
}
进行重载:
myVector<T>& operator =(myVector<T>& other){
if(this == &other)
return *this;
clear();
theSize = other.size();
theCapacity = other.capacity();
array = new T[theCapacity];
for(unsigned int i = 0 ;i<theSize;++i)
{
array[i] = other[i];
}
return *this;
}
如果参数与本myVector相同,那就无需赋值了;不相同时才需要赋值,并需要分别对3个成本变量进行赋值,元素个数、容量大小和数组内容。
析构函数里直接调用clear函数,如下所示:
~myVector(){
clear();
}
用下标的方式访问myVector中的元素,其实就是访问数组array中的元素,注意下标必须小于元素个数,如下所示:
T& operator[](unsigned int pos){
assert(pos<theSize);
return array[pos];
}
获得元素个数和容器大小,直接返回成员变量即可,如下所示:
/*element theSize*/
unsigned int size(){
return theSize;
}
/*alloc theSize*/
unsigned int capacity(){
return theCapacity;
}
判断myVector是否为空,直接判断元素个数是否等于0即可,如下所示:
bool empty(){
return theSize == 0;
}
清空myVector中的元素,需要删除掉数组指针,并把元素个数和容量大小都置0,如下所示:
void clear(){
deallocator(array);
array = 0;
theSize = 0;
theCapacity = 0;
}
push_back、push_front都可以归根于insert,在哪个位置插入,如下所示:
void push_back(const T& t){
insert_after(theSize-1,t);
}
void push_front(const T& t){
insert_before(0,t);
}
void insert_after(int pos,const T& t){
insert_before(pos+1,t);
}
void insert_before(int pos,const T& t){
if(theSize==theCapacity){
T* oldArray = array;
theCapacity += WALK_LENGTH;
array = allocator(theCapacity);
/*memcpy(array,oldArray,theSize*sizeof(T)):*/
for(unsigned int i = 0 ;i<theSize;++i){
array[i] = oldArray[i];
}
deallocator(oldArray);
}
for(int i = (int)theSize++;i>pos;--i){
array[i] = array[i-1];
}
array[pos] = t;
}
myVector通过一个连续的数组存放元素,如果集合已满,在新增数据的时候,就要分配一块更大的内存,将原来的数据复制过来,释放之前的内存,再插入新增的元素。这个元素后面的所有元素都向后移动一个位置,在空出来的位置上存入新增的元素。
删除某个元素,则要把这个元素后面的都往前挪,并把元素个数-1,如下所示:
void erase(unsigned int pos){
if(pos<theSize){
--theSize;
for(unsigned int i = pos;i<theSize;++i){
array[i] = array[i+1];
}
}
}
通过分析代码,可以发现vector的特点,如下所述。
(1)随即访问元素效率很高。
(2)push_back的效率也会很高。
(3)push_front的效率非常低,不建议使用。
(4)insert需要把插入位置以后的元素全部后移,效率比较低,不建议使用。
(5)erase需要把删除位置后面的元素全部前移,效率比较低,不建议使用。
(6)当内存不够时,需要重新申请内存,再把以前的元素复制过来,效率也比较低。
3.4 map