原因分析
首先问一下为什么需要实现一个数组类?
先看一段小程序:
StaticList<int*,5> s1;
for(int i = 0; i < s1.capacity(); i++)
{
s1[i] = i*i;
}
这是想干什么?将线性表当做数组使用。但是操作可行吗?答案是肯定是不允许的,因为线性表都还没有元素插入怎么能直接操作,我们看看[]操作符的重载中是如何实现的,首先判断获取的位置是否合法,合法的话就返回正确元素,不合法就抛出异常,这里显然是由于访问的位置不合法导致的,因为线性表中没有插入元素, 所以我们禁止将线性表当做数组使用,因为线性表必须先插入元素才能使用[]操作符访问元素。
在顺序存储结构线性表提供了数组操作符的重载,通过重载能够快捷方便的获取目标位置处的数据元素,在具体的使用形式上类似于数组,但是线性表是先插入再使用,而数组是直接能够使用这片空间,由于本质不同,不能代替数组使用。
原生数组不能提供自身信息,也没办法在越界时提醒开发人员,而且顺序存储线性表可能被当成数组误用,正是由于这个原因我们才需要一个开发数组类作为原生数组的替代品。
抽象数组类创建
那么对于数组类我们有什么需求?
数组类应该包含长度信息。
数组类能够主动发现越界访问。
提供一个父类Array,做成抽象类供子类调用。
Array为抽象类模板,存储空间的位置和大小由子类完成。
重载数组操作符,判断访问下标是否合法。
提供数组长度的抽象访问函数。
提供数组对象间的复制操作。
Array类的声明如下:
template <typename T>
class Array : public Object
{
protected:
T* m_array;
public:
virtual bool set(int i, const T& e);//O(1)
virtual bool get(int i, T& e)const;//O(1)
T& operator [](int i);//O(1)
T operator [](int i)const;//O(1)
virtual int length()const = 0;
};
具体实现和前面的前面的List类几乎差不多,需要注意的就是
[ ]操作符的重载,当越界操作时会抛出异常。
在子类中实现数组长度。
T& operator [](int i)
{
if((i >= 0) && (i < length()))
{
return m_array[i];
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsException,"Parameter i is Out of Bounds...");
}
}
T operator [](int i)const//当const对象使用[]操作符时首先会将它转换成普通对象,然后再使用(*this)[i],这时就会自动调用非const版本的重载
{
return const_cast<Array<T>&>(*this)[i];
}
设计要点:
类模板封装
继承Array类
使用原生数组实现
使用数值模板参数作为数组大小
实现函数返回数组长度
增加原生数组没有的功能:拷贝构造和赋值操作
实现如下:
template <typename T,int N>
class StaticArray : public Array<T>
{
protected:
T m_space[N];
public:
StaticArray()//O(1)
{
this->m_array = m_space;
}
StaticArray(const StaticArray<T,N>& obj)//O(n)
{
this->m_array = m_space;
for(int i = 0; i < N; i++)
{
m_space[i] = obj.m_space[i];
}
}
StaticArray<T,N>& operator =(const StaticArray<T,N>& obj) //O(n)
{
if(this != &obj)
{
for(int i = 0; i < N; i++)
{
m_space[i] = obj.m_space[i];
}
}
return *this;
}
int length() const //O(1)
{
return N;
}
};
类实现中使用的是原生数组作为存储空间使用,可以提供长度信息和越界操作提醒,并提供了拷贝构造和数组间的直接赋值功能。
一眼看来好像StaticList和StaticArray几乎一样,但是他们有他们各自的特点:
线性表在插入元素后才能使用数组操作符,而数组类直接就能使用数组操作符。
线性表中避免拷贝构造和赋值操作,当使用赋值操作时,可能发生多次释放同一片空间情况导致程序出BUG,使用拷贝构造时可能出现插入覆盖的现象,它们的产生原因都是指向了同一片内存空间。
数组类中添加了原生数组没有的拷贝构造和赋值操作,使数组类的使用更加强大。
那么为什么不在线性表中添加拷贝构造和赋值操作的功能呢?
因为C++是支持面向对象理论的,线性表是属于容器类型,容器类型是不支持复制的,好比一杯水,你能将一杯水复制到另一杯中吗?现实生活中显然是不可能出现的。
所以C++就是要面向生活,将生活映射到程序中。