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 把結果插入到特定位置