|
泛型 :基于策略的basic_string实现(4) 可爱的C++ 好吧,我们手中握有三个很棒的basic_string实现,每个实现大概有433行代码,这样我们已经有了1300行代码。还不坏,尤其是当你想到你能很容易地增加新的实现。 如果你觉得这很有趣,本文至此已经达到了目的。但别忘了开头一节告诉你会有大量乐趣,而现在只是开始。 我们看看SSO(小字符串优化)[5]。SSO背后的想法是在字符串对象中直接存放小字符串(而不是在动态分配内存中)。当大小超过string能容纳范围时,使用一个动态分配的策略。这两种策略共享字符串对象内的内存来存放记录数据。String类能够通过某种标记来区分这两种机制。 template <class E, other parameters> class sso_string { struct DynamicData { .. ..}; static const unsigned int maxSmallStringLen = 12; union { E[maxSmallStringLen] inlineBuffer_; DynamicData data_; }; bool isSmall_; ... }; 如果isSmall_为真,字符串直接存储在inlineBuffer_,否则data_有效。问题是DynamicData用何种动态分配机制?一个std::vector?一个SimpleStringStorage?一个AllocatorStringStorage?回答当然是“请给我上述所有而且还要多” 非常明显,使用SSO和你选择使用的任何存储机制正交。这样,SmallStringOpt模板类有另一个模板参数作为存储机制。 Template <class E, unsigned int threshold, class Storage, typename Align = E*> Class SmallStringOpt { enum { temp = threshold > sizeof(Storage) ? threshold : sizeof(Storage) }; public: enum { maxSmallString = temp > sizeof(Align) ? temp : sizeof(Align) ]; private union { E buf[maxSmallString + 1]; Align align_; }; .. ..执行存储策略.. .. }; buf_成员变量存放一个Storage对象或者是字符串本身。但Align有什么用呢?当处理类似于“预分配(seated allocation)”的情况时,你必须非常小心地处理对齐问题。因为没有通用的方法来算出Storage对齐的需要,SmallStringOpt接受一个指定对齐的类型,并存放在虚设的align_变量中。 SmallStringOpt怎样区分大小字符串?当为小字符串时,buf_的最后一个元素(就是buf_[maxSmallString])存放maxSmallString减去字符串实际长度后的结果,当为长字符串时存放一个常量。对一个长度为maxSmallString的字符串,buf_[maxSmallString]为零,这样很好地充当了null终止符和标志这两个角色。 你能在SmallStringOpt中看到一些技巧,转换和低层次的东西。(我们这里正在讲优化,不是吗?)但结果是惊人的。我们能把SmallStringOpt和任何其他Storage实现结合起来,当然包括SimpleStringStorage,VectorStringStorage和AllocatorStringStorage。那么现在我们有六个basic_string实现——我们多一点努力(顺便提一下,也非常有趣不是吗?)就有成倍的回报。现在代码有1440行长,所以得到每个basic_string实现有240行。如果说C++编程是空手道,那么有效地同时使用你所写代码的多个产品就会象一次同时对付多个敌人。
|