Effective STL 条款30
http://blog.csdn.net/amin2001/article/details/8063
STL容器在被添加时(通过insert、push_front、push_back等)自动扩展它们自己来容纳新对象。这工作的很好,有些程序员因为这个信仰而被麻痹,认为他们不必担心要为容器中的对象腾出空间,因为容器自己可以照顾好这些。如果是那样就好了!当程序员想向容器中插入对象但并没有告诉STL他们所想的时,问题出现了。这是一个常见的可以自我表现的方法:
int transmogrify(int x); // 这个函数从x// 产生一些新值 vector<int> values; ... // 把数据放入values vector<int> results; // 把transmogrify应用于 transform(values.begin(), values.end(), // values中的每个对象results.end(), // 把这个返回的valuestransmogrify); // 附加到results// 这段代码有bug!
在本例中,transform被告知它的目的区间是从results.end()开始的,所以那就是开始写在values的每个元素上调用transmogrify的结果的地方。就像所有使用目标区间的算法,transform通过对目标区间的元素赋值的方法写入结果,transform会把transmogrify应用于values[0]并把结果赋给*results.end()。然后它会把transmogrify应用于value[1]并把结果赋给*(results.end()+1)。那只能带来灾难,因为在*results.end()没有对象,*(results.end()+1)也没有!调用transform是错误的,因为它会给不存在的对象赋值。(条款50解释了STL的一个调试实现怎么在运行期检测这个问题。)犯了这种错误的程序员几乎总是以为他们调用算法的结果能插入目标容器。如果那是你想要发生的,你就必须说出来。STL是一个库,不是一个精神。在本例中,说“请把transform的结果放入叫做results容器的结尾”的方式是调用back_inserter来产生指定目标区间起点的迭代器:
vector<int> results; // 把transmogrify应用于 transform(values.begin(), values.end(), // values中的每个对象,back_inserter(results), // 在results的结尾transmogrify); // 插入返回的values
在内部,back_inserter返回的迭代器会调用push_back,所以你可以在任何提供push_back的容器上使用back_inserter(也就是任何标准序列容器:vector、string、deque和list)。如果你想让一个算法在容器的前端插入东西,你可以使用front_inserter。在内部,front_inserter利用了push_front,所以front_insert只和提供那个成员函数的容器配合(也就是deque和list):
... // 同上 list<int> results; // results现在是list transform(values.begin(), values.end(), // 在results前端front_inserter(results), // 以反序transmogrify); // 插入transform的结果
因为front_inserter用push_front把每个对象添加到results,results中的对象顺序会和values中对应的对象顺序相反。这也是为什么front_inserter没有back_inserter那么常用的原因之一。另一个原因是vector不提供push_front,所以front_inserter不能用于vector。
如果你要transform把输出结果放在results前端,但你也要输出和values中对应的对象顺序相同,只要以相反的顺序迭代values:
list<int> results; // 同上 transform(values.rbegin(), values.rend(), // 在results前端front_inserter(results), // 插入transform的结果transmogrify); // 保持相对的对象顺序
front_inserter让你强制算法在容器前端插入它们的结果,back_inserter让你告诉它们把结果放在容器后端,有点惊人的是inserter允许你强制算法把它们的结果插入容器中的任意位置:
vector<int> values; // 同上 ... vector<int> results; // 同上,除了现在 ... // 在调用transform前// results已经有一些数据 transform(values.begin(), values.end(), // 把transmogrify的inserter(results, results.begin() + results.size()/2), // 结果插入transmogrify); // results的中间
不管你是否使用了back_inserter、front_inserter或inserter,每次对目的区间的插入只完成一个对象。条款5解释了对于连续内存容器(vector、string和deque)来说这可能很昂贵,但条款5的建议解决方法(使用区间成员函数)不能应用于使用算法来完成插入的情况。在本例中,transform会对目的区间每次写入一个值,你无法改变。
当你要插入的容器是vector或string时,你可以通过按照条款14的建议最小化这个代价,预先调用reserve。你仍然要承受每次发生插入时移动元素的开销,但至少你避免了重新分配容器的内在内存:
vector<int> values; // 同上 vector<int> results; ... results.reserve(results.size() + values.size()); // 确定results至少// 还能装得下// values.size()个元素 transform(values.begin(), values.end(), // 同上,inserter(results, results.begin() + results.size() / 2),// 但resultstransmogrify); // 没有任何重新分配操作
当使用reserve来提高一连串插入的效率时,总是应该记住reserve只增加容器的容量:容器的大小仍然没有改变。即使调用完reserve,当你想要让容器把新元素加入到vector或string时,你也必须对算法使用插入迭代器(比如,从back_inserter、front_inserter或inserter返回的迭代器之一)。
要把这些完全弄清楚,这里有一个提高本条款开始时的那个例子的效率的错误方法(就是我们把transmogrify作用于values里的数据的结果附加到results的那个例子):
vector<int> values; // 同上 vector<int> results; ... results.reserve(results.size() + values.size()); // 同上 transform(values.begin(), values.end(), // 写入transmogrify的结果results.end(), // 到未初始化的内存transmogrify); // 行为未定义!
在这段代码中,transform愉快地试图对results尾部的原始的、未初始化的内存赋值。通常,这会造成运行期错误,因为赋值只在两个对象之间操作时有意义,而不是在一个对象和一块原始的比特之间。即使这段代码碰巧作了你想要它做的事情,results也不会知道transform在它的未使用容量上“创造”的新“对象”。直到results知道之前,它的大小在调用transform后仍然和原来一样。同样的,它的end迭代器仍和调用transform前指向同样的位置。结论呢?使用reserve而没有用插入迭代器会在算法内部导致未定义行为,也会弄乱容器。
正确地写这个例子的代码的方法是使用reserve和插入迭代器:
vector<int> values; // 同上 vector<int> results; results.reserve(results.size() + values.size()); // 同上 transform(values.begin(), values.end(), // 把transmogrify的结果back_inserter(results), // 写入results的结尾,transmogrify); // 处理时避免了重新分配
到目前为止,我都在假设你让像transform那样的算法把它们的结果作为新元素插入容器。这是通常的期望,但有时候你要覆盖现有容器的元素而不是插入新的。当这种情况时,你不需要插入迭代器,但你仍然需要按照本条款的建议来确保你的目的区间足够大。
比如,假设你让transform覆盖results的元素。如果results至少有和values一样多的元素,那很简单。如果没有,你也必须使用resize来确保它有。
vector<int> values; vector<int> results; ... if (results.size() < values.size()){ // 确保results至少results.resize(values.size()); // 和values一样大 } transform(values.begin(), values.end(), // 覆盖values.size()个results.begin(), // results的元素transmogrify);
或者你可以清空results然后用通常的方式使用插入迭代器:
... results.clear(); // 销毁results中// 的所有元素 results.reserve(values.size()); // 保留足够空间 transform(values.begin(), values.end(), // 把transform地返回值pack_inserter(results), // 放入resultstransmogrify);
本条款论证了这个主题的很多变化,但我希望你能牢牢记住本质。无论何时你使用一个要求指定目的区间的算法,确保目的区间已经足够大或者在算法执行时可以增加大小。如果你选择增加大小,就使用插入迭代器,比如ostream_iterators或从back_inserter、front_inserter或inserter返回的迭代器。这是所有你需要记住的东西。
注意:
copy(ve.begin(),ve.end(),back_inserter(v1));是将ve的数据插入到v1的后面
一:函数模板类
1、一元函数原型
template<class _A,class _R>
struct unary_function
{
typedef _A argument_type;
typedef _R result_type;
};
2、二元函数原型
template<class Arg1,class Arg2,class Result>
struct unary_function
{
typedef Arg1 first_argument_type;
typedef Arg2 Second_argument_type;
typedef Result result_type;
};
3、系统函数对象
plus 返回两个数的和:a+b
minus 返回两个数的差:a-b
multiplies 返回两个数的乘积:a*b
divides 返回两个数的商:a/b
mudulus 返回两个数的模:a%b
negate 返回某个数的相反数:-a
equal_to 判断两个数是否相等:a==b
not_equal_to 判断两个数是否不等:a!=b
greater 判断第一个数是否大于第二个数:a>b
less 判断第一个数是否小于第二个数:a<b
greate_equal 判断第一个数是否大于等于第二个数:a>=b
less_equal 判断第一个数是否小于等于第二个数:a<=b
logical_and 返回两个数的逻辑与结果:a&&b
logical_or 返回两个数的逻辑或结果:a||b
logical_not 返回某个数的逻辑非结果:!a
二:通用容器
1、vector
vector/vector(int nSize) 创建向量容器
assign 对Vector中的元素赋值
at 返回指定位置的元素
back 返回最末一个元素
begin 返回第一个元素的迭代器
capacity 返回vector所能容纳的元素数量(在不重新分配内存的情况下)
clear 清空所有元素
empty 判断Vector是否为空(返回true时为空)
end 返回最末元素的迭代器(译注:实指向最末元素的下一个位置)
erase 删除指定元素
front 返回第一个元素
get_allocator 返回vector的内存分配器
insert 插入元素到Vector中
max_size 返回Vector所能容纳元素的最大数量(上限)
pop_back 移除最后一个元素
push_back 在Vector最后添加一个元素
rbegin 返回Vector尾部的逆迭代器
rend 返回Vector起始的逆迭代器
reserve 设置Vector最小的元素容纳数量
resize 改变Vector元素数量的大小
size 返回Vector元素数量的大小
swap 交换两个Vector
2、list
assign 给list赋值
back 返回最后一个元素
begin 返回指向第一个元素的迭代器
clear 删除所有元素
empty 如果list是空的则返回true
end 返回末尾的迭代器
erase 删除一个元素
front 返回第一个元素
get_allocator 返回list的配置器
insert 插入一个元素到list中
max_size 返回list能容纳的最大元素数量
merge 合并两个list
pop_back 删除最后一个元素
pop_front 删除第一个元素
push_back 在list的末尾添加一个元素
push_front 在list的头部添加一个元素
rbegin 返回指向第一个元素的逆向迭代器
remove 从list删除元素
remove_if 按指定条件删除元素
rend 指向list末尾的逆向迭代器
resize 改变list的大小
reverse 把list的元素倒转
size 返回list中的元素个数
sort 给list排序
splice 合并两个list
swap 交换两个list
unique 删除list中重复的元素
3、deque
push_front 头部添加一个元素
push_back 尾部添加一个元素
insert 插入一个元素
erase 删除一个元素
pop_front 删除最前一个元素
pop_back 删除最后一个元素
clear 清楚所有元素
at 返回指定位置的元素
4、stack
stack<class T,class Container = deque/vector/list<T>> 构造栈
empty 堆栈为空则返回真
pop 移除栈顶元素
push 在栈顶增加元素
size 返回栈中元素数目
top 返回栈顶元素
5、queue
queue<class T,class Container = deque/list<T>> 构造队列
back 返回最后一个元素
empty 如果队列空则返回真
front 返回第一个元素
pop 删除第一个元素
push 在末尾加入一个元素
size 返回队列中元素的个数
6、set
begin 指向set的头指针
clear 删除所有元素
count 返回某键值元素的个数
empty 返回是否为空
end 指向set的尾指针
erase 删除某位置元素
pair<iterator, iterator>equal_range(const key_type& x) 返回一个迭代器对(指向键不小于x的第一个元素的迭代器,指向键大于x的第一个元素的迭代器)
find 返回某索引元素指针
get_allocator 返回构造函数的一个拷贝
pair<iterator,bool>insert(const value_type& x)返回<指向元素x的迭代器,是否插入成功>
lower_bound 返回键值不小于某值的第一个元素的迭代器
max_size 返回该set可以控制的最大长度
rbegin 返回反向set的反向头指针
rend 返回反响set的反向尾指针
resize(size_type Sz, T C=T()) 插入或删除使元素的个数为n,插入的元素为C
7、map
map(const Pred& comp=Pred(),const A& al=A()) 构造函数
pair<const_iterator,const_iterator>equal_range(const Key& key)const: 返回迭代器中键值等于key的迭代指针[fitst,last)
8、迭代器函数
back_inserter 返回容器的后插迭代器
front_inserter 返回容器的前插迭代器
三:非变异算法
1、循环
for_each 对每个元素执行相同的函数操作
2、查询
find 某值第一次出现位置
find_if 符合某谓词的第一个元素
find_first_of 子序列某元素第一次出现位置
adjacent_find 第一次相邻元素相等的位置
find_end 子序列最后出现位置
search 子序列第一次出现位置
search_n 一个值连续出现n次的位置
3、计数
count 某值出现的此时
count_if 与某谓词匹配的次数
4、比较
equal 两个序列对应元素都相同为真
mismatch 两个序列相异的第一个元素
四:变异算法
1、变换
transform 将某操作应用于指定范围的元素
2、填充
fill 用给定值填充所有元素
3、生成
generate 用一操作的结果填充所有元素
4、唯一
unique/unique_copy 删除相邻位置重复的元素
5、反转
reverse/reverse_copy 循环移动元素
6、随机
random_shuffle 随机移动元素
7、划分
partition/stable_paritition 满足谓词的元素都放在前面
五:排序及相关操作
1、排序
sort 以很好的平均效率排序
stable_sort 稳定排序
partial_sort 局部排序
partial_sort_copy 复制时进行局部排序
2、二分检索
lower_bound 大于等于某值第一次出现的迭代器位置
upper_bound 大于某值第一次出现位置
equal_range 等于某值迭代器范围
binary_search 是否存在某值
3、归并
merge 归并两个迭代器有序序列
inplace_merge 归并单个迭代器有序序列
4、有序结构上的集合操作
includes 一个序列为另一个序列子集为真
set_union 构造两集合有序并集
set_intersection 构造有序交集
set_difference 构造有序差集
set_symmetric_difference 构造有序对称差
5、堆操作
make_heap 从序列构造堆
pop_heap 从堆中弹出元素
push_heap 加入元素
sort_heap 排序
6、最大最小
max
min
min_element
max_element
7、词典比较
lexicographical_compare 两个序列按字典序排序
8、排列生成器
next_permutation 按字典序的下一个排序
prev_permutation 按字典序的上一个排序
9、数值算法
accumulate 累积求和
inner_product 内积求和
partial_sum 创建新序列,每个元素值代表指定范围内该位置前所有元素之和
adjacent_difference 相邻元素差集
http://www.crackerban.com/?p=1158
Effective STL 条款30相关推荐
- Effective C++条款30:透彻了解inlining的里里外外(Understand the ins and outs of inlining)
Effective C++条款30:透彻了解inlining的里里外外(Understand the ins and outs of inlining) 条款30:透彻了解inlining的里里外外 ...
- Effective C++ 条款30、31
条款30 透彻了解inlining的里里外外 Inline函数,看起来像函数,动作像函数,比宏好得多,可以调用它们又不需要蒙受函数调用所招致的额外开销.但是,就像世界没有免费的午餐一样,inline的 ...
- effective stl 条款15 小心string实现的多样性
实际上每个string实现都容纳了下面的信息: ● 字符串的大小,也就是它包含的字符的数目. ● 容纳字符串字符的内存容量.(字符串大小和容量之间差别的回顾,参见条款14.) ● 这个字符串的值,也就 ...
- 《Effective STL》条款解读
条款01:慎重选择容器类型 vector.list和deque有着不同的复杂度,vector是默认使用的序列类型.当需要频繁在序列中间做插入和删除操作时,应使用list.当大多数插入和删除操作发生在序 ...
- 《Effective STL》学习笔记(第一部分)
本书从STL应用出发,介绍了在项目中应该怎样正确高效的使用STL.本书共有7个小节50个条款,分别为 (1) 容器:占12个条款,主要介绍了所有容器的共同指导法则 (2) vector和string: ...
- effective c++条款44 将与参数无关的代码抽离templates
effective c++条款44 将与参数无关的代码抽离templates 首先了解这个条款的含义:使用template可能导致代码膨胀,二进制码会带着重复(或者几乎重复)的代码.数据,或两者.其结 ...
- 《Effective STL》重读笔记整理
最近有闲,在工作之余重读了<effective STL>一书,并通过 twitter 记了一下笔记,今天整理收集到这里. twitter 真的非常适合记读书笔记,哈哈,以后要好好地发扬.另 ...
- 【绝版C++书籍】《Effective STL》读书笔记
<Effective STL>读书笔记 写在前面 0<Effective STL>中可能过时的内容 1 容器 第1条:慎重选择容器类型. 第2条:不要试图编写独立于容器类型的代 ...
- Effective STL 50条有效使用STL的经验笔记
Scott Meyers大师Effective三部曲:Effective C++.More Effective C++.Effective STL,这三本书出版已很多年,后来又出版了Effective ...
最新文章
- PyTorch入门学习(二):Autogard之自动求梯度
- 【tensorboard】从tensorboard上看出模型是否存在过拟合
- PLSQL乱码TNS-12557: protocol adapter not loadable解决
- python 时间序列分析之ARIMA(不使用第三方库)
- [TypeScript] Export public types from your library
- 【BZOJ1922】【Tyvj1736】【codevs2129】大陆争霸,无语最短路
- TCN-时间卷积网络
- 动漫头像1000张萌妹子图片,可以做高清头像壁纸
- 16 计算二叉树叶子结点数目(耿 6.14)
- spark报错:Cannot overwrite a path that is also being read from.
- Keep不甘做“工具人”
- 回车、换行、空格的ASCII码值(不同OS平台下文件换行定义)
- Python编程IDE的选择
- 送给23岁的风华年少
- (附源码)Springboot宠物领养系统毕业设计241104
- DQN(Deep Q Network)及其代码实现
- 新手程序员首份工作月薪三千,网友:3000元?你是在丢码农的脸吗
- 超级文案撰写技巧之对比写法
- 如果有一天我不更新博客了
- RedHat Linux 9.0的安装+下载+入门指南(图文并茂)
热门文章
- 分布式内存数据库---Redis操作String、list、set、hash和Zset
- PageRanke算法
- pch在c语言中占内存字节数,c语言期末测试题(附答案)
- ACL 2020 | 基于稠密段落检索的开放域问答系统技术
- IJCAI 2019 论文解读 | 基于超图网络模型的图网络进化算法
- Wasserstein距离在生成模型中的应用
- 综述 | 知识图谱向量化表示
- LightOJ 1401 No More Tic-tac-toe 博弈论SG打表
- CentOS系统中使用yum快速安装python3
- 元件库导入_Axure RP9【元件库的学习】