使用allocator
使用allocator的最简单方法,当然是将它们作为参数传给容器类;用
std::vector<char, malloc_allocator<char> > v;
取代简单的std::vector<char>,或用
typedef std::list<int, mempool_allocator<int> > list;
list l(mempool_allocator<int>(p));
取代简单的std::list<int>。
但是你能做得更多。stl的卖点是它是可扩展性:正如你能写自己的allocator,你也能写你自己的容器类。如果你很小心, 并且你写的容器类使用它的allocator来处理所有的内存相关操作,那么别人将能够加入他们自己的用户自定义allocator。
诸如std::vector和std::list这样的容器类是很复杂的,而且大部分复杂性与内存管理无关。让我们以一个简单的例子开始,这样我们可以只关注于allocator。考虑一个固定大小数组的类,array,元素的个数是在构造函数中设定的,并且在此之后不会改变。(这有点像std::valarray的一个简化版本。)它有二个模板参数,元素类型和allocator类型。
容器,和allocator一样,以巢式类型申明开始:value_type, reference,const_reference,size_type,difference_type,iterator,和const_iterator。通常,这些类型中的绝大部分都可以直接从它的allocator中获得--这也解释了为什么容器的value_type必须和allocator中的相匹配。
当然,iterator类型通常不来自于allocator;通常iterator是一个类,完全取决于容器的内在表示。array类比通常见到的容器简单,因为它实际上将所有元素存储在单块连续内存中;我们只要维护指向内存块开始和结束处的两个指针。iterator就是指针。
在更进一步之前,我们必须决定:我们将怎样储存allocator?构造函数将接受一个allocator对象作为参数。我们必须在容器的整个生命期内保存它的一个拷贝,因为在析构函数中还需要它。
依感觉,这儿没什么问题:我们只要申明一个allocator类型的成员变量,然后使用它。方法是正确的,但不爽。毕竟,在99%的时间里,用户都不想考虑有关allocator的事;他们只会写array<int>并使用默认值--而默认的allocator可能是一个没有任何非static成员变量的空类。问题是即使allocator是一个空类,这样的成员变量也会有开销。(这是c++标准所要求的。) 我们的array类将会有三个word的开销,而不是两个。也许一个word的额外开销不是大问题,但总是不爽,它迫使所有用户为一个几乎从不使用的功能承担了开销。
都很多方法来解决这个问题,其中一些使用了traits类和偏特化。或许最简单的解决方法就是使用(私有)继承而不是成员变量。编译器被允许优化掉空基类,而且时下绝大多数的编译器都这么做了。
我们最终能写下定义的骨架了:
template <class t, class allocator = std::allocator<t> >
class array : private allocator
{
public:
typedef t value_type;