Effective STL
剥离
向基类对象的容器中插入派生类对象,派生类对象独有的特性会丢失
empty
检查容器是否为空用empty而不是size
善于使用区间成员函数(assign,etc)
v1.assign(v2.begin() + v2.size()/2, v2.end())
16.将vector和string传给旧的API(c api)
vector元素是连续存储在内存中的
if (!v.empty()) {
func(&v[0]);
}
不能用v.begin()
,因为这将返回一个迭代器,而不是指针
string元素不一定是连续存储在内存中的,所以有专门的成员函数
s.c_str()
17.使用swap将多余内存还给系统
`vector<int> contestents(10, 0);
contestents.pop_back();
cout << "Capacity is " << contestents.capacity() << "\n";// 10
vector<int>(contestents).swap(contestents);
cout << "Capacity is " << contestents.capacity() << "\n";// 9`
C++11起有了成员函数shrink_to_fit()
18.vector
失败的实验,可以采用deque
19.相等与等价
等价
!(a < b) && !(a > b)
20.为包含指针内容的关联容器指定比较类型
set里面存string*,但是想要set按照字符串的字典序排序。
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <set>
using namespace std;
struct StringPtrLess:
public binary_function<const string*, const string*, bool> {//binary_function 于 C++11 弃用并于 C++17 移除。
bool operator() (const string *ps1, const string *ps2) const {
return *ps1 < *ps2;
}
};
int main()
{
set<string*, StringPtrLess> ss;
string a = "sadad";
string b = "dsfaf";
ss.insert(&a);
ss.insert(&b);
for (auto it : ss) {
cout << *it << "\n";
}
}
模板类型:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <set>
using namespace std;
struct DereferenceLess {
template<typename PtrType>
bool operator() (PtrType pT1, PtrType pT2) const {
return *pT1 < *pT2;
}
};
int main()
{
set<string*, DereferenceLess> ss;
string a = "sadad";
string b = "dsfaf";
ss.insert(&a);
ss.insert(&b);
for (auto it : ss) {
cout << *it << "\n";
}
}
21.总是让比较函数在等值情况下返回false
如果set中的比较函数是operator<=,那么等价性的判断就 !(a <= b) && !(a >= b)
,显然为false,那么就认为 a 和 b 是不等价的。
那么set中就会包含两个相等的值,就破坏了 set 容器。
用于对关联容器排序的比较函数必须为他们所比较对象定义一个”严格的弱序化“
22.切勿直接修改 set 或 multiset中的键
map 和 multimap 中的键是 const 的
set 和 multiset中的键是非 const 的
试图修改 set 或 multiset 元素的代码将是不可移植的
应对方案:
- 不考虑移植性,非键部分可以改动
- 考虑移植性,修改非键部分可以采用强制类型转换(const_cast),来去掉访问的 const 性质
强制转换???有点奇妙
23.考虑排序vector代替关联容器
查找操作几乎不和插入、删除操作混在一起
24.效率至关重要,选择map::operator[]还是map::insert()
operator[] 先默认构造再赋值,所以单纯添加内容优先选择 insert()
26. iterator 优先于其他迭代器
27.const_iterator 转换成 iterator
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
std::vector<int> v = {1, 3, 5};
typedef std::vector<int>::iterator It;
typedef std::vector<int>::const_iterator CIt;
int main()
{
CIt ci = v.begin();
It i = v.begin();
std::advance(i, std::distance(i, ci));
std::cout << *i << "\n";
return 0;
}
上述代码无法通过编译,distance 的两个参数需是同一类型
解决方法是指明参数类型 std::advance(i, std::distance<CIt>(i, ci));
28.reverse_iterator 的 base() 成员函数产生的 iterator 用法
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
int main()
{
std::vector<int> v;
for (int i = 0; i < 10; ++i) {
v.push_back(i);
}
std::vector<int>::reverse_iterator ri = std::find(v.rbegin(), v.rend(), 7);
std::vector<int>::iterator i(ri.base());
std::cout << *ri << "\n";
std::cout << *i << "\n";
return 0;
}
base() 函数返回底层迭代器,指向 reverse_iterator 当前指向的下一个元素
http://upload.cppreference.com/mwiki/images/3/39/range-rbegin-rend.svg
如果在 ri 的前面插入一个元素,和在 ri.base() 前面插入一个元素的逻辑是一样的。
insert() 和 erase() 函数不接受 reverse_iterator 作为参数,需要使用 iterator。
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
int main()
{
std::vector<int> v;
for (int i = 0; i < 10; ++i) {
v.push_back(i);
}
std::vector<int>::reverse_iterator ri = std::find(v.rbegin(), v.rend(), 7);
std::vector<int>::iterator i(ri.base());
std::cout << *ri << "\n";
std::cout << *i << "\n";
v.erase(--i);
for (int x : v) {
std::cout << x << "\n";
}
return 0;
}
v.erase(–i);//应该是编译不过的,但是我编过了
但是中间解释不太懂
v.erase((++ri).base());//应该这样用
29.istreambuf_iterator
int main()
{
ifstream input_file("int.txt");
string file_data((istreambuf_iterator<char>(input_file)), istreambuf_iterator<char>());
}
30.确保区间足够大
transform
transform(v.begin(), v.end(), res.end(), [](int x){return x;});//无效对象的赋值操作
生成迭代器
transform(v.begin(), v.end(), back_inserter(res), [](int x){return x;});
- back_inserter 返回的迭代器调用 push_back()
- front_inserter 返回的迭代器调用 push_front()
- inserter 把结果插入到特定位置