如果你是STL的惯用者,且对效率持有莫大的兴趣,你可能会对原生数组而不能优雅的与 STL 算法结合而感到不满和懊恼,你充其量可以这样使用:
int nArray[4] = {1,2,3,4};
const int Len = sizeof( nArray ) / sizeof( int );
int nCount = std::count( nArray, nArray + Len, 3 );
然而你不肯运用其他如 swap 等算法,数组是一个小型集合, 不能直接赋值。可能在模板中你更渴望有直接赋值的要求:
int nArray[4] = {1,2,3,4};
int nArray2[4] = nArray; // 这种优雅的语法实在比 memcpy 等更具有观赏性
std::swap( nArray, nArray2 ); // 适应这种算法也更通用
幸而,boost提供了这样一个物件: array, 说到底,array 只是原生数组的浅薄包装而已,你甚至可以像原生数组那样直接以大括号形式的赋值方式赋值:
array <int, 2> array = {{1,2}};
array <int, 2> array = {1,2}; // 显然大部分编译器已经支持一个大括号的初始化了
这种赋值方式的技术要求是:
1. 不允许有用户定义的构造函数( 可以有析构 )
2. 不允许有 private 和 protected 的数据成员 ( 可以有 static 成员 )
3. 不允许有 父类
4. 无虚函数
从 C++ 对象模型角度来说,其结构在内存中是连续的一段,没有额外的其他东西如虚表指针.
下面是其简单定义:
template<class T, std::size_t N>
class array {
public:
T elems[N]; // 被包装之原生数组
public:
// 类型定义
typedef T value_type;
typedef T* iterator;
typedef const T* const_iterator;
typedef T& reference;
typedef const T& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
// 迭代器支持
iterator begin() { return elems; }
iterator end() { return elems+N; }
// 反向迭代器支持
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
// 操作符[]
reference operator[](size_type i)
{
BOOST_ASSERT( i < N && "out of range" );
return elems[i];
}
// 带有检查的 at()
reference at(size_type i) { rangecheck(i); return elems[i]; }
const_reference at(size_type i) const { rangecheck(i); return elems[i]; }
// front() 和 back()
reference front()
{
return elems[0];
}
reference back()
{
return elems[N-1];
}
// 大小为常量, 提供了 enum 来支持模板编程
static size_type size() { return N; }
static bool empty() { return false; }
static size_type max_size() { return N; }
enum { static_size = N };
// swap (线性复杂度)
void swap (array<T,N>& y) {
std::swap_ranges(begin(),end(),y.begin());
}
// direct access to data (read-only)
const T* data() const { return elems; }
T* data() { return elems; }
// use array as C array (direct read/write access to data)
T* c_array() { return elems; }
// 允许类型转换的赋值
template <typename T2>
array<T,N>& operator= (const array<T2,N>& rhs) {
std::copy(rhs.begin(),rhs.end(), begin());
return *this;
}
// 数组填充某一值
void assign (const T& value)
{
std::fill_n(begin(),size(),value);
}
// 检测区间
static void rangecheck (size_type i) {
if (i >= size()) {
throw std::out_of_range("array<>: index out of range");
}
}
};
当 N = 0 时, 像elem[0]这样的语法是错误的,故以偏特化。内部去掉了 T elems[N]; 大部分接口只是为了统一,内部并未实作,顶多是抛出异常。
另外,一些有用的协助函数,主要是比较函数: ==, <, !=, >, <=, >=. 还有一个全局的swap,内部主要调用array内部的swap实现:
// swap()
template<class T, std::size_t N>
inline void swap (array<T,N>& x, array<T,N>& y) {
x.swap(y);
}
瑕疵:
不过,我们只有显示的提供给数组一个大小,不能再像下面一样使用编译器来替我们工作了:
而只有这样:
array<int, 5> arr = {1,2,3,4,5};
另外,像 char 这样的特殊元素, 我们就不免小心(见下篇)。
不过,比起array之功,这点瑕疵也完全可以忽略。