文章目录

  • ​​1.向量的实现细节​​

1.向量的实现细节

  • 向量(vector) 类似数组, 但向量是动态的, 即它的元素个数可以随时动态改变。
    例如:
vector<int> MyVec(100);//定义长度为100的向量
MyVec[50] = 1024;//给向量的某个元素赋值
int i =0 ;
for( ; i<25; i++ )
{
MyVec.push_back(1);
}
MyVec.resize(400);//重新定义向量的长度
  • eg:自定义一个动态数组类模板。
#include <stdlib.h>
#include <iostream>
using namespace std;
enum ErrorType { invalidArraySize,memoryAllocationError,indexOutOfRange };

//指针数组
//每个元素char*类型的
char *errorMsg[]={"Invalid array size",
"Memory allocation error",
"Invalid index"};

template <class T>
class Array
{
private:
T *alist;//当作数组名来使用
int size;//数组长度
void Error(ErrorType error,int badIndex=0) const;//常成员函数,不能改变数据成员的值
public:
Array(int sz=50); //构造函数。默认数组长度是50
Array(const Array<T>& A); //拷贝构造函数,用一个动态数组类的对象初始化另外一个对象
~Array(void);//析构函数
Array<T>& operator=(const Array<T>& rhs);//对赋值运算符重载,可以将数组的内容通过=赋值给另一个数组
T& operator[](int);
operator T*() const;
int ListSize() const;//求数组长度
void Resize(int sz);//改变数组长度
};

template <class T> //模板函数Error实现输出错误信息功能
void Array<T>::Error(ErrorType error,int badIndex) const//因为在类外实现的成员函数,所以要在函数前面加上类名,
//因为error函数是类模板,所以要加上T
{
cout<<errorMsg[error];//根据错误类型输出相应的错误信息,error=0,1,2
if(error==indexOutOfRange)
cout<<badIndex; //如果下标越界, 输出错误的下标

cout<<endl;
exit(1);
}

//构造一个长度为size,类型为T的动态数组
template <class T>//构造函数
Array<T>::Array(int sz)
{
if(sz<=0) //如果数组长度小于0则输出错误信息
Error(invalidArraySize);
else
{//如果数组长度大于0则生成数组
size=sz;
alist=new T[size];//动态生成数组,new生成一个T类型的数组,首地址是alist
if(alist==NULL)//如果分配内存不成功则输出错误信息
Error(memoryAllocationError);
}
}

template <class T> //拷贝构造函数(深拷贝)
Array<T>::Array(const Array<T>& X)//拷贝构造函数一定是:参数必须是同类型对象的引用
{
int n=X.size;
··=n;
alist=new T[n];//new个新的动态数组
if(alist==NULL)
Error(memoryAllocationError);

T *srcptr=X.alist; //X.alist是对象X的数组首地址
T *destptr=alist; //本对象数组首地址
while(n--) //逐个复制数组元素
*destptr++=*srcptr++;
}

===========================================================
//为啥不用浅拷贝?,拷贝构造函数(浅拷贝),解释如下:
Array<int> a(10);
Array<int> b(a);
template <class T>
Array<T>::Array(const Array<T>& X)
{
size=X.size;
alist=X.alist;//浅拷贝操作,X是a的
//拷贝构造函数就是用来将一个对象a初始化另外一个对象b,这里对象a和对象b的alist指针都占用同一块内存空间,
到时候用析构函数释放内存时,到时候先释放a,那么对于b而言,他的alist指向的是一个不存在的存储空间了,变成了迷途指针了,
再释放b就会出错,一段内存不能释放2次!!!
}
==========================================================



template <classT>//析构函数
Array<T>::~Array()
{
delete [] alist;
}

template <class T>//重载“=”运算符, 将一个数组赋值给另一个数组
Array<T>& Array<T>::operator =(const Array<T>& rhs)
{
int n=rhs.size;
if(size!=n)//若2个数组的长度不相等的话,新生成一个数组,将其长度重新赋值
{
delete [] alist;
alist=new T[n];
if(alist==NULL)
Error(memoryAllocationError);
size=n;
}
//从rhs向本对象复制元素
T* destptr=alist;
T* srcptr=rhs.alist;
while(n--)
*destptr++=*srcptr++;
return *this;//返回当前的对象,this指针的意思是:返回调用对象的本身!!!!所以返回类型是Array<T>类型的引用
}

template <class T>//重载“[]”运算符, 实现通过下标访问数组元素
T &Array<T>::operator [](int n)
{
if(n<0||n>size-1) //检查下标是否越界
Error(indexOutOfRange,n);

return alist[n];//返回下标为n的数组元素,返回的是T类型的
}

template <class T>//重载类型转换
Array<T>::operator T*() const
{
return alist;
}
/*为了解决:
Array<int> a(10)
int *b=a;
将a类型的地址转换为int类型的地址
*/
template <class T>//取当前数组的长度
int Array<T>::ListSize() const
{
return size;
}

template <class T>//修改数组的长度为sz,重新生成一个数组,将老数组的内容拷贝到新数组里面
void Array<T>::Resize(int sz)
{
if(sz<=0)
Error(invalidArraySize);
if(sz==size)
return;

T *newlist=new T[sz];//newlist的指针指向新生成的数组,长度是sz
if(newlist==NULL)
Error(memoryAllocationError);

int n=(sz<=size)?sz:size;//将sz和size中较小的一个赋给n

T *srcptr=alist;//原数组的首地址
T *destptr=newlist;//新数组的首地址
while(n--)
*destptr++=*srcptr++;

delete [] alist;
alist=newlist;
size=sz;//使alist指向新数组, 并更新sz
}

int main()
{
int i,*p;
Array<int> a(5);//数组对象a
for(i=0;i<5;i++)
cin>>a[i];
for(i=0;i<5;i++)
cout<<a[i]<<' '; cout<<endl;

Array<int>b=a;//要用到拷贝构造函数
for(i=0;i<5;i++)
cout<<b[i]<<' '; cout<<endl;

a.Resize(10);
for(p=a;p<a+10;p++)
cout<<*p<<' ';
return 0;
}