Boost源码简析系列——Array(1)
Array是Nicolai M.Josuttis为boost扩展库编写的一个类,她为静态数组增加了容器的一些特性,比如增加了迭代器,类似vector的元素访问访问方式等等。
下面对boost的简介部分翻译自boost说明文档。
C++标准模板(STL)作为C++标准库的一部分,她提供了种类繁多的容器类,然而,对于普通的数组类,STL就没有提供其容器类通用的接口,比如迭代器(iterator)接口。当然STL用std::vector来作为普通数组的替代品。STL的vector类与普通数组不同,她是一个动态的数组,也就是可以动态的改变其中存放元素的数量。在功能上说,vector远远的超越了一个普通数组提供的功能。但是正是由于vector功能繁复,所以在存取数据操作的性能方面比起作为内部数据类型的数组存在差距。
类似boost::array类,还有其他几个,比如,在Matthew H的《Generic Programming and the STL》中介绍了一种包装类block,她比起普通数组来说,更加安全(在操作中,比如处理上溢),而且性能没有损失。在Bjarne Stroustrup的《The C++ Programming Language,3rd edition》中介绍了一个类c_array。而我们本文介绍的数组类array,曾经在Nicolai Josuttis的书《The C++ Standard Library-A Tutorial and Reference》中被称为carray,然后几经易名,最后定名为boost::array。
Boost::array实现了大部分但不是全部的STL的容器类接口,因此boost::array不能算是一个真正的STL容器类。
首先是,boost::array没有提供用户定义构造函数。
其次是,其对象的元素会有一个不确定的初始值,这也是没有构造函数的造成的。
再次是,swap()函数的复杂性是不变的。
然后是,其对象的数据容量不能改变,她由创建对象是的第二个模板参数决定。
最后是,这个类没有提供分配器的支持。
这也就是她与一个STL容器类的所有区别了。
下面先看一个使用boost::array类的简单例程。
//例1
#include <iostream>
#include <string>
#include <boost/array.hpp>
#include <stdlib.h>
int main(void)
{
boost::array<std::string,4> seasons = {
{ "spring", "summer", "autumn", "winter" }
};
boost::array<std::string,4>::const_iterator pos;
boost::array<std::string,4>::reverse_iterator reverse_pos;
for(pos=seasons.begin();pos!=seasons.end();++pos)
{
std::cout<<*pos<<'\n';
}
for(reverse_pos=seasons.rbegin(); reverse_pos<seasons.rend(); ++reverse_pos)
{
std::cout<<*reverse_pos<<'\n';
}
system("pause");
return 0;
}
这个例程在Dev-C++ 4.9.8.0上调试通过,但是在VC6.0上调试不能通过。
在这里我们看到boost::array创建一个对象时需要提供两个模板参数,前一个为元素类型,后一个参数定义了这个对象所能够盛放的元素数量。另一方面,我们用到了像STL容器类类似的迭代器,并透过迭代器对象遍历并访问了array对象中所有的元素,打印出了他们。但是array所提供的功能不止如此。
Boost::array类的定义在array.hpp中。
下面就是她的定义源码:
1. 包含头文件
#ifndef BOOST_ARRAY_HPP
#define BOOST_ARRAY_HPP
#include <cstddef>
#include <stdexcept>
#include <algorithm>
#include <boost/detail/iterator.hpp>
#include <boost/config.hpp>
//…
#endif
<cstddef>是C++标准库的一个部分,这个头文件里面定义了一些特别的数据类型,比如size_t,wchar_t等类型就这里面定义的。
<stdexcept>声明了C++的标准异常类,在自定义类的某些member function要throw出异常的时候,多数情况下我们要包含她。在array中at()中用到了上溢异常。
<algorithm>声明了C++标准库容器类里经常用到的算法。STL的算法总共只有六十几个但是用处非常广泛,她涵盖了在容器上的各种最具普遍性的操作,例如遍历、排序、检索、插入、删除等。
< boost/detail/iterator.hpp >,boost为自己定义一个迭代器,array要提供迭代器功能自然要包含她。由于我对于iterator的定义源码还没有看的明白,在这里不能深讲了。
2. 定义array类中一组标准的类型名
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;
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && !defined(BOOST_MSVC_STD_ITERATOR) && !defined(BOOST_NO_STD_ITERATOR_TRAITS)
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#elif defined(_MSC_VER) && (_MSC_VER == 1300) && defined(BOOST_DINKUMWARE_STDLIB) && (BOOST_DINKUMWARE_STDLIB == 310)
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, iterator,reference, iterator, reference> > reverse_iterator;
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, const_iterator,const_reference, iterator, reference> > const_reverse_iterator;
#else
typedef std::reverse_iterator<iterator,T> reverse_iterator;
typedef std::reverse_iterator<const_iterator,T> const_reverse_iterator;
#endif
//…
};
value_type
对象中的元素类型。
iterator
迭代器类型。
reference
对象中的元素引用类型。
size_type
表示对象中的元素数量类型。
difference_type
容器的两个迭代器之差得到的结果的类型。
reverse-iterator
反向迭代器类型。
就想STL中容器一样,在定义的时候都要先定义类似的成员类型,这样用户就可以写出使用容器的代码,而不必知道所涉及的实际类型。这是设计一个类或者库时,通常使用的方法,这样增加了代码可移植性和可读性,是良好的编程风格。
3. 迭代器成员函数。
定义了成员类型就可以接着定义成员函数了。
//…
T elems[N];//定义了一个数组,类型为T,元素数量为N。
//…
iterator begin() { return elems; }
const_iterator begin() const { return elems; }
iterator end() { return elems+N; }
const_iterator end() const { return elems+N; }
//…
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
//…
begin()
返回数组首地址,也就是第一个元素地址。
end()
返回数组首元素之后第N个,也就是最后一个元素之后一个位置。
rbegin()
同end()
rend()
同begin()
这个容器中迭代器的使用,通常都用到了这组函数,特别是遍历元素时主要是通过他们实现,在例1中,为了打印出所有的元素,就用到了begin()和end()。
而提供rbegin()和rend()是以便能反向的迭代通过容器的各个元素。反向迭代器也是完整无缺的常规迭代器,有关迭代器的知识可以参考Bjarne Stroustrup的《The C++ Programming Language,3rd edition》第三部分相关章节。rbegin()返回容器中元素序列的最后一个元素位置,rend()返回元素序列的第一个元素之前的一个位置。
4. 元素访问
除了用迭代器访问元素以外,用户还需要一组函数可以直接的高效地按任何顺序随机访问容器中的任何元素,这也是普通数组通过下标访问元素的方式。在我们使用普通数组进行随机存取元素的时候,数组自己不能提供上溢检测,在boost::array中就提供了一种访问方式提供了上溢异常检测并throw出异常的函数at()。
//…
reference operator[](size_type i) { return elems[i]; }
const_reference operator[](size_type i) const { return elems[i];
reference at(size_type i) { rangecheck(i); return elems[i]; }
const_reference at(size_type i) const { rangecheck(i); return elems[i]; }
reference front() { return elems[0]; }
const_reference front() const { return elems[0]; }
reference back() { return elems[N-1]; }
const_reference back() const { return elems[N-1]; }
//...
operator[]
重载下标运算符,用下标随机访问容器中元素。
at()
用输入参数随机访问容器中元素。
front()
访问元素序列中首元素。
back()
访问元素序列中尾元素。
这四个函数返回的都是元素的引用,前面两个函数,operator[]和at()的功能基本一致都是根据调用函数时的输出参数决定随机访问的函数位置。区别在于前者在访问位置被设在了元素序列位置之外时不会throw出异常,而后者要throw出访问溢出异常。
对于front()和back(),都是操作数组时最常用到的函数,在用boost::array()构造队列或是栈的时候,这两个函数就起到很大的作用。对于这组函数使用例子,见例2。
由于这篇文章有点长,我分成了两篇,在下篇中提供例2的代码。
sixsavage(野人)于March 22, 2004作
sixsavage@yahoo.com