学习STL

STL深入解析

最近在学习STL,加上别人的一点总结如下:

 

1. Allocator采用两级配置

第一级直接用malloc和free分配内存,第二级采用内存池技术, 内存池技术主要解决小内存分配问题,容器默认是采用第二级配置器。

2. Iterator分为五种

支持不同的操作,通过traits技术实现,本质是通过声明不同的类来区分函数重载,这样可以省掉一个变量定义,容器内只要使用typedef就可以, traints技术在stl中用的比较多(type traits, iterator traits).

Iterator中, OutputIterator和InputIterator 不能支持迭代器赋值操作(iter1 = iter2), 因为只能有左值或者右值的一种,OutputIterator不能支持相等判断 (iter1 == iter2 or iter1 != iter2), 只有RandomAccessIterator支持 + - > < 操作。

迭代器是一种抽象, 是为了建立容器和算法之间的关系, 不同的算饭支持不同的迭代器, 因而也可能支持不同的容器。

3. 标准容器中使用的迭代器类型如下:

vector, deque, stack, queue: RandomAccessIterator.

list, map, set, multimap, multiset: BidirectionalIterator.

slist, hash_set, hash_map, hash_multiset, hash_multimap: ForwardIterator.

istream: InputIterator

ostream: OutputIterator.

4. 关于迭代器的无效性

vector的插入操作如果引起resize迭代器会无效, 执行erase操作了后面的迭代器指的对象也可能无效。

list的插入操作和接合操作不会引起迭代器无效,erase 操作只会影响指向被erase的元素的迭代器,其他的迭代器也不受影响。

deque如果在头部和尾部插入或者删除的话迭代器不会失效(除了被删除的元素), 如果在中间插入和删除则迭代器会失效。


5. vector没有提供push_front操作,因为效率很挫, list和deque提供了

map和set没有提供push_front和push_back操作,因为他们要维护一个有序的队列, 所以只能用insert.

6. 关于erase操作

对于序列容器(vector, deque, list), erase的返回值返回的是被删除元素的下一个元素的位置(有可能是endl)

对于关联式容器(set,map), erase可能没有返回值,所以只能在内部做 iter++操作.

7. map, set提供了自己的find成员函数, 应该用这个(二分查找).

    vector, deque, list需要用algorithm里面的find函数, 或者sort以后可以用 binary_search.

8. list 提供了自己的 remove, remove_if, unqiue, sort, splice, merge, swap函数

stl算法里面的remove和remove_if算法设计的比较通用,会把后面的元素都作移动(in place move), 这对list是没有必要的.

unique也是因为同样的原因, list不需要作全局移动,所以提供了自己的版本。

quick sort算法需要RandomAccessIterator, 因为需要计算三点的位置, 所以list不适合, 故提供自己的版本。

list提供的merge是in place的merge操作, 因为效率的原因所以使用自己的版本,如果不是in place merge的话可以使用

算法里面的merge函数。

splice操作时list特有的连接操作, swap操作因为list只需要改变链接所以提供自己的版本。

map和set应该用自己的erase函数去删除某个值 (不需要remove, remove_if) (map和set的erase有三个版本, 删除某个位置的元素,删除一个区域, 删除某个值)

9. 容器的对象内存释放过程

容器的内存释放由容器自己管理 (STL容器都是拷贝式容器),

比如一个vector在退出作用域的时候会调用~vector,

vector的析构函数会调用 _Destroy(_M_start, _M_finish)

_Destroy根据是否有trival dtor 确定是否要调用_Destory (_Destory的作用是调用对象的析构函数)

vector的析构函数执行完了以后,接着执行Vector_base的析构函数

~Vector_base会调用 _M_deallocate函数, 这个函数会对这个容器的对象的使用的内存真正释放。

所以两个不同的析构函数作用是不一样的, 第一个调用的对象的析构函数,可以释放对象内部可能分配的内存, 第二个直接释放这个对象。

10. RB-Tree属于平衡二叉搜索树

平衡二叉搜索树的主要作用是防止树的深度过于不平衡而导致的某个分支过深, 这样会降低查找速度

平衡二叉树有AVL Tree, RB-tree, AA-tree等形式.

SGI STL 采用RB-tree为底层数据结构实现set和map,

11. hashtable是一种以空间换时间的数据结构, 能够达到常量级的查找速度

hashtable内部用一个vector实现buckets,用单向链表实现node

hashtable也有resize的问题, resize的时候选择不同的质数来构建buckets.

12. copy和copy_backward算法

copy和copy_backward算法如果用在in place copy中,需要注意源和目的的区间, 对于copy算法

1) 如果输出区间和输入区间没有重叠,那肯定没有问题

2) 如果输出区间的尾端和输入区间重叠, 但是头端没有重叠, 则也不会有问题

3) 如果输出区间的头端和输入区间重叠,则可能会有问题。(如果copy采用memmove实现的,则不会有问题, 对于 char* wchar*_t*, 以及has trivial operator = 的T*, copy 都是采用memmove实现的, 所以不会有问题)

13. for_each和transform算法

for_each函数调用仿函数后不会改变元素内容, 仿函数的返回值被忽略

如果需要改变元素内容, 则应该使用transform算法

14. remove, remove_copy, remove_if, remove_copy_if

这四个算法都只需要用一个for循环就能实现,其中 remove, remove_if应用了 in place的技巧, unique算法同样采用了这一方法

15. rotate和rotate_copy算法

rotate算法针对三种不同的迭代器类型实现了三个不同的版本。

rotate_copy很简单。

16. lower_bound, upper_bound, binary_search, merge, equal_range, inplace_merge

这些算法都只能用于有序区间, 比如map, set和排序后的vector, list, deque等。

17. sort

sort 采用插入排序和快速排序相结合的方法

18. partition, nth_element, sort, partial_sort, merge_sort

这些都是某种意义上的排序算法, 不过并不是每个算法都会完整排序

19. 仿函数的本质

仿函数其实就是用类来实现的一个对象,然后实现了operator()接口, 这个对象可以接受参数作为状态。

20. 配接器的本质

Adapter的本质是一个类, 类里面的成员变量可以进行配件, 然后重载实现函数以达到配接。

insert_iterator 内部存了容器, 重载operator=() 用push_back实现。

reverse_iterator 内部存了容器原始的iterator, 重载了所有的迭代器操作。

istream_iterator内部保存了流变量, 重载了operator++() 操作 (很少用)

ostream_iterator内部保存了流变量, 重载了operator=()操作

bind1st, bind2nd, 内部保存了op函数

ptr_fun和mem_fun, mem_fun_ref同样保存了内部函数。

面向对象带来的一个重要的好处就是对象可以内部保存状态, 这一点个人认为甚至比多态重要。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值