相信大多数人应该和我一样,对STL的认识是从使用它开始的。但是,这等利器都是双刃剑,不明原理地使用,往往会适得其反。举个例子来说,对vector进行增添或者删除之后,出于内存安全的考虑,原有的迭代器应该摒弃不用。或许,只有真正走进STL的源码,才能解答这些疑问。 《STL源码剖析》这本书的源码来自大名鼎鼎的SGI STL,它是相对来说可读性最高的一个版本。
从实现的角度来看,空间配置器是一切的基础。当我们使用各种容器,进行各种操作时, 空间配置器总是默默地在我们看不见的地方做出自己的贡献。
SGI STL对内存的使用可以用两个词形容,可能不太准确:奢侈、精打细算。这一切都是为了:效率。下面是我的几点拙见。
1、内存池(memory pool)的概念
在STL二级空间配置中,内存池的使用可谓奢侈。若在第一次使用时,你想要一个8bytes区块,STL会要得40个这样的 8bytes区块,其中一个给你用,19个挂到free-list的一号桶下面,剩余20个会放到内存池中,以供将来使用。这样做的好处是,以后分配内存会直接从内存池中分配,提高了效率。
2、将内存分配和对象的构造/析构分开
template <class T1, class T2>
inline void construct (T1* p, const T2& value) {
new (p) T1(value);
}
template <class T>
inline void destroy(T* pointer) {
pointer->~T();
}
这里只列出了第一种destroy方法,第二中可以析构掉一批对象。
这里通过new占位符,来进行单纯的对象构造,只需要传入之前分配好的内存池中的地址,就可以在它之上构造出T1对象。destroy方法只负责析构对象,内存不会同时被释放掉。这样做的好处是:可以避免进行频繁的对象构造/析构时,内存也随之不停地获取和释放,从而提高了速度。
3、联合体的使用
在free-list中,使用了联合体来实现其中的节点:
union obj {
union obj * free_list_link;
char client_data[1];
}
这种结构既可以连接下一个节点,也可以为客端提供区块。这种“一物二用”的思想这是节省到家了!
联合体的另一个传神使用是:
struct in_addr{
union {
struct {u_char s_b1, s_b2, s_b3, s_b4;} S_un_b;
struct {u_short s_w1, s_w2;} S_un_w;
u_long S_addr;
} S_un;
};
这种结构用被用于windows socket编程中,存储IP。这样做的好处不言而喻。
未完待续......