总目录
一、设计目标与实现思路
二、使用方法与注意事项
三、开发者指南
四、一些讨论和个人心得
在上一篇文章《设计目标与实现思路》中。我介绍了POD_STL的总体设计思路。而这篇文章则是给想要在项目中使用POD_STL的人员看的。本文描述了使用者需要注意的内容。
目录结构
POD_STL根目录下有VS2013的工程,这主要给开发者使用,使用者可以不必关心。
POD_STL子目录下又有3个子目录存放着代码:
1、headers目录中存放所有的POD_STL头文件。你应当修改项目工程属性使之包含这个目录。
2、cpp目录中存放所有POD_STL的非模板实现。你必须在项目工程中添加并编译这些cpp文件。注意这和普通STL版本有所不同——一般的STL函数实现都是模板,可以直接放到头文件中包含。但POD_STL把很多函数去模板化了,因此一定要编译链接这些cpp中的实体函数。
3、test目录下是cppunit测试框架和所有测试用例。这是给开发者使用的,不需要添加到使用项目中。
所有以下划线开头的文件都是POD_STL内部实现文件,使用者不应当直接include。当然,如果你对于STL内部实现有相当的了解,也可以按自己的需要使用。
用户定制文件
在配置好你的项目后,请务必关注这两个文件:_base_depends.h和base_depends.cpp。
所有和平台、操作系统强相关的定义(比如64位整型定义关键字、C语言库函数,内存分配/释放函数)都在这两个文件中实现。如果你的平台不支持对应的函数,或者你希望使用其他的库函数,请修改这两个文件中的函数内容。你甚至可以把一些函数名定义成宏。
另外,_base_depends.h中定义的STL_ASSERT宏是用于debug版本错误检查的,应当在release版本中置成空语句。当然,你也可以利用这个宏来打造错误日志记录等功能。
注意事项
1、POD_STL中所有的定义都在podstl名字空间中。因此,你应当把using namespace std语句修改成using namespace podstl。理论上讲,POD_STL和标准STL是可以混用的,但必须指定名字空间来区分同名符号。
2、所有POD_STL容器只允许存放具备以下条件的元素类型:
1)元素可以使用memcpy来复制,并且复制后不产生野指针,重复指针等问题;
2)元素不需要执行析构函数(内部动态分配空间的元素不符合这个条件,因为其析构函数有释放内存操作,如果不执行会内存泄露);
3)元素没有构造函数,或者有无参的构造函数,或者有可以以无参方式调用的构造函数(缺省参数)。这点和标准STL要求是一样的。
3、POD_STL容器本身不符合上面的条件,因此一般来说容器不允许作为元素嵌套入其他容器使用。但vector和string两个容器可以在非常谨慎的前提下,作为其他容器的元素。如果你要这么做,必须遵守以下几点(以下说明中,将vector和string称之为“元素”,包含这些元素的容器称之为“外部容器”):
1)向外部容器插入新元素时,只允许插入空的vector/string;
2)修改元素时,可以用迭代器或者operator[]等操作取出元素的引用来修改;
3)删除元素时,应当调用erase/clear/pop_back/pop_front等接口;
4)禁止使用外部容器的所有可能会附带插入/删除元素的操作函数,比如外部容器赋值运算符、resize、assign等函数。
4、如果需要将不符合以上条件的类型作为容器元素,建议在容器中保存此类型的指针。比如map<int, deque<char> * >。
5、对于关系型容器(如set/multiset/map/multimap),作为key的元素应当可以用<运算符比较大小。如果key是自定义类型,应当自定义operator<成员函数。这点和标准STL用法一致。
6、所有的STL算法和标准STL都是一样的,会调用元素的赋值运算符,比较操作符等。因此可以放心的用于任何定义了合适操作符的复杂类型。
7、部分容器操作函数,对于插入元素时引用自己内部的元素存在限制。但也有一些函数特别为此做了考虑,比如下面的例子:
1 | string str = "This is test string for string calls" ; |
4 | str.insert(10, str.c_str() + 5, 15); |
5 | CPPUNIT_ASSERT( str == "This is teis test string st string for string calls" ); |
8 | s.replace(s.begin() + 4, s.end(), s.begin(), s.end()); |
9 | CPPUNIT_ASSERT( s == "1234123456" ); |
在此我把所有可以引用自身元素和不能引用自身元素的接口总结如下:
可以引用自己内部元素的操作函数:
01 | vector::push_back( const _Tp& __element) |
02 | vector::insert(iterator __pos, const _Tp& __element) |
03 | vector::insert(iterator __pos, size_type __count, const _Tp& __element) |
04 | vector::resize(size_type __new_size, const _Tp& __fill_value) |
06 | string::assign( const _Self& __other_string) |
07 | string::assign( const _Self& __string, size_type __pos, size_type __length) |
08 | string::assign( const char * __c_string, size_type __length) |
09 | string::assign( const char * __c_string) |
10 | string::insert(size_type __pos, const _Self& __other_string) |
11 | string::insert(size_type __pos, const _Self& __string, size_type __begin, size_type __length) |
12 | string::insert(size_type __pos, const char * __c_string, size_type __length) |
13 | string::insert(size_type __pos, const char * __c_string) |
14 | string::insert(iterator __pos, const char * __first, const char * __last) |
15 | string::replace(size_type __pos, size_type __length, const _Self& __string) |
16 | string::replace(size_type __pos1, size_type __length1, const _Self& __string, size_type __pos2, size_type __length2) |
17 | string::replace(size_type __pos, size_type __length1, const char * __c_string, size_type __length2) |
18 | string::replace(size_type __pos, size_type __length, const char * __c_string) |
19 | string::replace(iterator __first, iterator __last, const _Self& __string) |
20 | string::replace(iterator __first, iterator __last, const char * __c_string, size_type __length) |
21 | string::replace(iterator __first, iterator __last, const char * __c_string) |
22 | string::replace(iterator __first, iterator __last, const char * __new_first, const char * __new_last) |
24 | list::insert(iterator __pos, const_reference __data) |
25 | list::insert(iterator __pos, size_type __count, const_reference __data) |
26 | list::push_front(const_reference __data) |
27 | list::push_back(const_reference __data) |
28 | list::resize(size_type __new_size, const_reference __fill_data = value_type()) |
29 | list::assign(size_type __count, const_reference __data) |
30 | list::assign(_InputIterator __first, _InputIterator __last) |
32 | deque::assign(size_type __count, const _Tp& __data) |
33 | deque::assign(_InputIterator __first, _InputIterator __last) |
34 | deque::push_back( const value_type& __data) |
35 | deque::push_front( const value_type& __data) |
36 | deque::resize(size_type __new_size, const value_type& __fill_data = value_type()) |
38 | set::insert( const value_type& __data) |
39 | set::insert(iterator __pos, const value_type& __data) |
41 | multiset::insert( const value_type& __data) |
42 | multiset::insert(iterator __pos, const value_type& __data) |
44 | map::insert( const value_type& __data) |
45 | map::insert(iterator __pos, const value_type& __data) |
47 | multimap::insert( const value_type& __data) |
48 | multimap::insert(iterator __pos, const value_type& __data) |
禁止引用自己内部元素的操作函数:
01 | vector::assign(size_type __count, const _Tp& __element) |
02 | vector::assign(_InputIterator __first, _InputIterator __last) |
03 | vector::insert(iterator __pos, _InputIterator __first, _InputIterator __last) |
05 | string::operator+=( const _Self& __string) |
06 | string::append( const char * __first, const char * __last) |
07 | string::append( const _Self& __string) |
08 | string::append( const _Self& __string, size_type __pos, size_type __length) |
09 | string::append( const char * __c_string, size_type __length) |
10 | string::append( const char * __c_string) |
11 | string::assign( const char * __first, const char * __last) |
13 | list::insert(iterator __pos, _InputIterator __first, _InputIterator __last) |
15 | deque::insert(iterator __pos, const value_type& __data) |
16 | deque::insert(iterator __pos, size_type __count, const value_type& __data) |
17 | deque::insert(iterator __pos, _InputIterator __first, _InputIterator __last) |
19 | set::insert(_InputIterator __first, _InputIterator __last) |
21 | multiset::insert(_InputIterator __first, _InputIterator __last) |
23 | map::insert(_InputIterator __first, _InputIterator __last) |
25 | multimap::insert(_InputIterator __first, _InputIterator __last) |
原则上,如果让这些函数都能引用自己内部的元素,会给使用者带来方便。但是,这个需求的实现会使性能下降。比如对于list,要判断一个迭代器是否在自己内部就得做一个O(n)遍历,其代价是不小的。期待以后能有高手想出尽量不降低性能的技巧,来更多的支持自身元素引用。
另外多说一句,即使是标准STL的实现,也没有允许所有操作引用自己的元素。而且不同版本的STL可能限制还不一样。各位使用者一定要小心。
兼容性问题
由于STL的实现应用了大量C++模板高级特性,所以一般的STL版本都只支持特定类型的编译器。有些版本(比如STLport)通过大量的宏开关来实现多个编译器的支持。POD_STL没有使用这种编译宏开关,因为现在的主流编译器对于模板的支持已经相当完善了。但是,使用者还是应当尽量采用高版本的编译器。
如果你在编译时遇到了兼容性问题,欢迎在iSource上反馈。你也可以自己拉一个分支进行修改来解决自己遇到的问题。
下一章:没有模板代码膨胀的STL:三、开发者指南
POD_STL的全部代码已上传到GitHub开源平台,欢迎有兴趣的朋友参与开发和提出点评。
链接:https://github.com/Goalsum/POD_STL