1、静态数组
处理静态数组:
#define N 10 //数组的长度N在编译时已知
T static_array[N];
这里,数组的长度在编译时是已知的并且不会改变。当然,为了使用具有边界检查的安全数组,也可以vector模板,并在一个构造函数中指定它的长度:
scpp::vector vect(N);
它的效果与静态数组完全相同,但问题在于效率。静态数组是在堆栈上分配内存,而vector模板是在构造函数中用new操作符分配内存的,速度相对慢一些。如果运行时效率至关重要,最好使用array模板,即namespace scpp
{
//固定长度的数组
template <typename T, unsigned N>
class array
{
public:
typedef unsigned size_type;
//最常用的构造函数
array() {};
explicit array(const T& initial_value)
{
for(size_type i=0; i<size(); ++i)
data_[i] = initial_value;
}
//注意:这里并没有提供拷贝构造函数和赋值操作符
//依赖的是编译器生成的这些方法的默认版本
T& operator[] (size_type index)
{
SCPP_TEST_ASSERT(index < N,
"Index "<<index<<" must be less than "<<N);
return data_[index];
}
const T& operator [] (size_type index) const
{
SCPP_TEST_ASSERT(index < N,
"Index "<<index<<" must be less than "<<N);
return data_[index];
}
//模拟迭代器的访问方法
T* begin()
{
return &data_[0];
}
const T* begin()const
{
return &data_[N];
}
private:
T data_[N];
};
}//namespace SCPP
这个数组的行为与C的静态数组完全一样。但是,在编译时激活了表示安全检查的SCPP_TEST_ASSERT宏时,它会提供索引边界检查。它提供了用于模拟迭代器的begin()和end()的方法,因此这种数组在有些场合可以代替vector模板(例如,用于对数的排序)、例如下面的程序使用了STL的sort算法对这个数组进行排序:
#include<algorithm>
scpp::array<int, 5> a(0);
a[0] = 7;
a[1] = 2;
a[2] = 3;
a[3] = 9;
a[4] = 0;
cout<<"Array before sort: "<<a<<endl;
sort(a.begin(), a.end());
cout<<"Array after sort: "<<a<<endl;
endl;
程序运行后产生的结果输出为:
Array before sort:7 2 3 9 0
Array after sort: 0 2 3 7 9
作为一个附加的优点,我们还可以使用<<操作符,它允许我们像前面那个例子一样把一个数组输出到流中,只要这个数组不至于太大,并且模板类型T定义了<<操作符。当然,这种固定长度的数组在使用时必须限制在数组长度N不会太大的情况下。否则,就会把大量的堆栈内存(一种有限的资源)消耗在这个数组上。
因此,建议不要使用静态或者动态分配数组,而是使用vector或array模板。这种做法可以解决当我们使用了带了方括号的new操作符时,在使用delete操作符时也要带方括号。如果错用了这两个操作符带方括号(new带方括号,而delete没带方括号或者反过来),就会破坏内存堆,一般会导致不良后果。决定不使用动态分配的数组(通过带方括号的new操作符创建)之后,我们就会避免索引越界问题和混用带方括号的操作符问题,这样可以极大减少麻烦。