模板栈的优化
基于模板栈(一)中简单模板栈的编写,接下来将对模板栈进行优化升级
简单模板栈描述如下:
template<class Type>
class SeqStack
{
private:
Type *data; //指向所存储的数据
int capacity;//栈容量
int pos; //栈顶
public:
SeqStack(int sz = 100):data(NULL),capacity(sz),pos(-1)//构造函数,初始化private成员
{ data = new Type[capacity];//通过new运算符申请空间,并调用构造函数; }
void push(const Type &x)//入栈--改变对象状态,不能为常方法
{ data[++pos] = x; }
void pop()//出栈--改变对象状态,不能为常方法
{ --pos; }
Type & top()//得到栈顶元素
{ return data[pos]; }
const Type & top() const//得到栈顶元素
{ return data[pos]; }
bool empty() const//判空--不改变对象状态,常方法
{ return size() == 0; }
size_t size() const//得到栈中元素--对象个数,不改变对象状态,常方法
{ return pos+1; }
~SeqStack()//析构函数
{ delete []data;//释放空间
data = NULL;capacity = 0;pos = -1; }
};
简单模板栈存在的问题:
当该模板栈对象调用构造函数时,对象的指针data属性将会通过new申请空间并调用构造函数,创建capacity个对象,从而进行数据的入栈,而若主函数无数据的入栈,则会浪费内存空间,造成内存碎片化;同时模板栈对象类型不仅仅只是内置类型,还可以是自己定义的类型,因此为解决这种情况,我们将使用malloc以及定位new对模板栈进行优化。
1. 对构造函数SeqStack()进行优化:
不再使用运算符new申请空间并创建对象,而是通过malloc申请空间,不创建对象:
template<class Type>
class SeqStack
{
private:
Type *data; //指向所存储的数据
int capacity;//栈容量
int pos; //栈顶
public:
SeqStack(int sz = 100):data(NULL),capacity(sz),pos(-1)//构造函数,初始化private成员
{
//data = new Type[capacity];//通过new运算符申请空间,并调用构造函数;
//通过malloc申请空间,但并不创建对象
data = (Type*)malloc(sizeof(Type)*capacity);
}
2. 对入栈函数void push()进行优化:
由于构造函数SeqStack()通过malloc申请空间,因此数据在进行入栈时,将通过定位new,在参数的地址空间完成对象的构建过程。
template<class Type>
class SeqStack
{
private:
Type *data; //指向所存储的数据
int capacity;//栈容量
int pos; //栈顶
public:
void push(const Type &x)//入栈--改变对象状态,不能为常方法
{
//data[++pos] = x;
//通过定位new完成对象的构建过程,在data[pos]的地址构建对象
new(&data[++pos]) Type(x);
}
};
注意:
(1)类型名()------->表示构造函数;
(2)在优化构造函数之后,data[++pos] = x;将不再适用,理由:因为在构造函数中,通过malloc申请空间,得到的只是空间,不能在空间中进行赋值,而是要在空间中构建对象再进行赋值,所以只能对象赋值给对象,而不能对象赋值给空间;
(3)new(&data[++pos]) Type(x)含义:将通过图片详细描述
(4)对于求对象空间大小,需要注意对象的大小只是自身空间大小,并不包括堆区数据的大小(指针类型大小为4),如下:
template<class Type>
class Object
{
private:
int value;
public:
Object(int sz = 100):value(sz)//构造函数
{
cout<<"creat Object"<<this<<endl;
}
~Object()//析构函数
{
cout<<"destroy Object"<<this<<endl;
}
};
int main()
{
SeqStack<Object> objst;
sizeof(objst); //12
Object obj(10);
Objst.push(obj);
sizeof(objst); //12
}
第一个sizeof:栈中还未创建对象,只是具有三个私有数据成员:Type *data、int capacity、int pos,指针所占空间4个字节,整型所占空间4个字节,因而12个字节;
第二个sizeof:栈中已创建空间,但.data是指针,指向所创建的对象,而对象在堆区存放,因而栈所占的空间大小仍是12;
具体空间可见上方图片
3. 对出栈函数void pop()进行优化:
简单模板栈的出栈函数只是将pos移动,则代表数据出栈,但并不是最合适的数据操作处理方式,因此为优化出栈函数,对象出栈时,将通过调用析构函数,将资源释放,但该空间并没有被销毁,还仍能继续使用。
template<class Type>
class SeqStack
{
private:
Type *data; //指向所存储的数据
int capacity;//栈容量
int pos; //栈顶
public:
void pop()//出栈--改变对象状态,不能为常方法
{
//--pos;
//将data[pos]对象调用析构函数,释放资源,但并没有销毁空间
(&data[pos])->~Type();
--pos;
}
};
注意:
只是调用析构函数进行资源的释放,但并没有销毁空间
4. 对析构函数~SeqStack()进行优化:
基于前面对函数的优化,析构函数也将会随之优化,通过for循环,栈中每一个对象调用析构函数进行资源的释放,最后进行空间的销毁。
template<class Type>
class SeqStack
{
private:
Type *data; //指向所存储的数据
int capacity;//栈容量
int pos; //栈顶
public:
~SeqStack()//析构函数
{
/*
delete []data;//释放空间
data = NULL;
capacity = 0;
pos = -1;
*/
for(int i = 0;i<=top;i++)//调用析构函数,将资源释放
{
(&data[i])->~Type();
}
free(data);//销毁空间
data = NULL;
capacity = 0;
pos = -1;
}
};
此模块为模板栈的一级优化,仍存在一些需要解决的问题,例如:栈满时应该如何操作?重载赋值函数和拷贝构造函数如何编写呢?
后续将会继续优化模板栈