C++标准库读书笔记
1.vector
1.1 元素访问
只有at检查范围,如果索引越界,会抛出out_of_range,剩下三个会引发不明确的错误
- c[idx]
- c.at[idx]
- c.front()
- c.back()
对一个空的vector调用operator[],front(),back()都会引发不明确的错误
1
2
3
4
5
std::vector v;
v[5] = 1; // undefined behavior
std::cout << coll.front() // undefined behavior
所以调用operator[]必须确保索引有效,调用front()和back()必须确保容器不为空
正确应该这样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
std::vector v;
if(v.size() >5)
{
v[5] = 1;
}
if(!v.empty())
{
cout << v.front();
}
v.at(5) = 1; // out_of_range exception
1.2 非更易性操作
c.size()返回目前元素个数
c.max_size()返回元素个数之最大可能量 //根据系统或库的实现不一样
返回的是向量可以容纳的元素的最大数量。这个值通常取决于系统或库实现的限制,而不是向量实际分配的空间大小
c.capacity()如果不进行控件分配情况下,元素的最大容纳量
c.shrink_to_fit,把容量缩小到现在的vector里面元素的个数
另外:
resize是会改变vector的大小的,而reserve不改变
resize如果比现在的大, 则会把现在的类型初始化为resize的大小,比如原来有vector里面有5个,现在resize(8),则会把vector里面的元素增加3个
但是reserve(8),只是把空间变大为8,但是vector的大小不会变,还是5个
如果resize(3)则会把后两个vector数据删除,虽然vector大小变为3,但其容量还是5
1
2
3
4
5
6
7
8
9
int main() {
std::vector<int> vec(5);
std::cout << "Initial size of vector = " << vec.size() << ", capacity = " << vec.capacity() << std::endl;
vec.resize(10);
std::cout << "Size of vector after resizing to 10 = " << vec.size() << ", capacity = " << vec.capacity() << std::endl;
vec.reserve(20);
std::cout << "Size of vector after reserving space for 20 elements = " << vec.size() << ", capacity = " << vec.capacity() << std::endl;
return 0;
}
1.3 迭代器
迭代器的函数是:begin()
vector的第一个元素是front(),返回指向第一个元素的引用
begin返回指向vector第一个元素的迭代器
*v.begin() 和v.front()结果是一样的
Vector自己没有提供任何可以删除与某个值相同的元素,这时候用到算法
1
2
3
std::vector<int> v;
// 算法开始 val就是要删除的
v.erase( remove(v.begin(), v.end(), val), v.end() );
这个表达式 v.erase( remove(v.begin(), v.end(), val), v.end() ) 是 C++ 中常用的一种模式,被称为 “erase-remove” 惯用法。
std::remove 函数并不实际删除元素,而是将所有不等于 val 的元素移动到向量的前部,并返回一个迭代器,指向新的 “逻辑结束” 位置。也就是说,remove 会将所有需要保留的元素移动到向量的前部,并返回一个迭代器,指向第一个需要删除的元素。
然后,vector::erase 函数接受两个迭代器参数,表示要删除的元素范围,并实际删除这些元素2在这个例子中,remove 函数返回的迭代器和 v.end() 一起定义了要删除的范围。
所以,remove 函数的作用是重新排列向量中的元素,并确定出需要删除的元素范围;而 erase 函数则根据这个范围来实际删除元素。
如果只是移除与某值相等的第一个元素
1
2
3
4
5
6
7
8
std::vector<int> v;
//算法开始
std::vector<int>::iterator pos;
pos = find(v.begin(), v.end(), val);
if(pos != v.end())
{
v.erase(pos);
}
1.4 综合运用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
vector<string> sentence;
sentence.reserve(5);
sentence.push_back("Hello,");
sentence.insert(sentence.end(), { "how","are","you","?" });
// 将容器sentence中的所有元素以空格分隔的形式输出到控制台
copy(sentence.cbegin(), sentence.cend(), ostream_iterator<string>(cout, " "));
cout << endl;
cout << " max_size(): " << sentence.max_size() << endl;
cout << " size(): " << sentence.size() << endl;
cout << " capacity(): " << sentence.capacity() << endl;
//swap second and fourth element
swap(sentence[1], sentence[3]);
// 在?前面添加always
sentence.insert(find(sentence.begin(), sentence.end(), "?"), "always");
//最后一个元素插入"!"
sentence.back() = "!";
copy(sentence.cbegin(), sentence.cend(), ostream_iterator<string>(cout, " "));
cout << endl;
cout << " size(): " << sentence.size() << endl;
cout << " capacity(): " << sentence.capacity() << endl;
// delete last two elements
sentence.pop_back();
sentence.pop_back();
sentence.shrink_to_fit();
cout << " size(): " << sentence.size() << endl;
cout << " capacity(): " << sentence.capacity() << endl;
输出为
1
2
3
4
5
6
7
8
9
Hello, how are you ?
max_size(): 461168601842738790
size(): 5
capacity(): 5
Hello, you are how always !
size(): 6
capacity(): 7
size(): 4
capacity(): 4
因为原来是5,现在增加了always和!,所以会vector自动扩容,不同的stl实现扩容不一样,导致了capacity()可能是7,也可能是10
2.map
1.1 非更易性操作
map的成员函数find()可以查找第一个“拥有某key”的元素,并返回一个迭代器指向该位置,如果没找到这样的元素,则返回容器的end()
但是不能用find()查找拥有某特定value的元素,必须改用stl算法如find_if()或者干脆自己写一个循环,像下面这样就是对拥有特定value的所有元素进行某项操作
1
2
3
4
5
6
7
8
9
std::multimap<std::string, float> m;
std::multimap<std::string, float>::iterator pos;
for(pos = m.begin(); pos != m.end(); ++pos)
{
if(pos->second == value)
{
do_something();
}
}
如果用find_if的话,可能会更复杂,因为需要提供一个函数对象
1.2 元素访问
map和multimap不支持元素直接访问,音速元素的访问通常是由range-base for循环或迭代器进行,不过有个例外:map提供at()和下标操作符可以直接访问元素
- range-base for循环访问map元素
1
2
3
4
5
6
std::map<std::string, float> m;
for(auto elem& : m)
{
std::cout << "key:" << m.first << "\t"
<< "value:" << m.second << std::endl;
}
- 迭代器访问元素(C++11之前必须使用这种方法)
1
2
3
4
5
6
7
std::map<std::string, float> m;
std::map<Std::string, float>::iterator pos;
for(pos = m.begin(); pos != m.end(); ++pos)
{
std::cout << "key:" << pos->first << "\t"
<< "value:" << pos->second << std::endl;
}
1.3元素安插和移除
从C++11开始,安插元素最方便的就是把他们以初始列的形式传递进去
1
2
std::map<std::string, float> m;
m.insert({"otto", 22.3});
有三种不同的方法可以将value传入map或者multimap内
1.用value_type
为了避免隐式类型转换,可以用它,它是容器本身提供的类型定义
1
2
3
4
std::map<std::string, float> m;
m.insert(std::map<std::string, float>::value_type("otto", 22.3));
或
m.insert(decltype(m)::value_type("otto", 22.3));
2.用pair<>
1
2
3
std::map<std::string, float> m;
m.insert(std::pair<std::string, float>("otto", 22.3));
m.insert(std::pair<const std::string, float>("otto", 22.3));
在这个特定的情况下,使用std::pair<std::string, float>和std::pair<const std::string, float>来插入元素到std::map中并没有区别。
这是因为在std::map中,键(key)总是常量,无论你是否在插入时指定了它为常量。这是因为一旦一个元素被插入到std::map中,你就不能改变它的键,否则会破坏std::map的内部结构。
所以,无论你是使用std::pair<std::string, float>还是std::pair<const std::string, float>来插入元素,结果都是一样的。
3.用make_pair()
C++11之前都是用的它
1
2
std::map<std::string, float> m;
m.insert(std::make_pair("otto", 22.3));
这三种从效率上来说微乎其微,主要是为了更多的灵活性和方便性,让我的话我还是用make_pair吧
其中也可以用数组插入元素
1
m[key] = value;
优点:
简洁性:数组方式的语法更简洁,更易于理解和使用。 覆盖性:如果键已经存在于map中,数组方式会覆盖原有的值。而使用insert函数插入元素时,如果键已经存在于map中,则不会覆盖原有的值。
缺点:
效率:当map的value是对象时,使用数组方式可能会导致额外的构造和析构调用,从而降低效率。而使用insert或emplace函数可以避免这种情况。 安全性:当map的value是指针时,如果不进行存在性检查而直接使用数组方式,可能会导致运行时错误。因为你直接调用 [] 就直接给 map 插入了一个 key,并且其对应的值为空指针 nullptr。
1.4把map当做关联式数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <map>
#include <string>
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
typedef map<string, float> StringFloatMap;
StringFloatMap stocks;
stocks["BASF"] = 360.23;
stocks["SAF"] = 361.23;
stocks["WE"] = 362.23;
stocks["TYU"] = 363.23;
stocks["PHG"] = 364.23;
StringFloatMap::iterator pos;
cout << left; //左对齐
for (pos = stocks.begin(); pos != stocks.end(); ++pos)
{
cout << "stock: " << setw(12) << pos->first
<< "price: " << pos->second << endl;
}
cout << endl;
for (pos = stocks.begin(); pos != stocks.end(); ++pos)
{
pos->second *= 2;
}
for (pos = stocks.begin(); pos != stocks.end(); ++pos)
{
cout << "stock: " << setw(12) << pos->first
<< "price: " << pos->second << endl;
}
cout << endl;
stocks["SAFSAF"] = stocks["SAF"];
stocks.erase("SAF");
for (pos = stocks.begin(); pos != stocks.end(); ++pos)
{
cout << "stock: " << setw(12) << pos->first
<< "price: " << pos->second << endl;
}
}
1.5 查找具有某特定Value的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <map>
#include <string>
#include <iostream>
#include <iomanip>
using namespace std;
class CompareThree
{
public:
bool operator()(const std::pair<float, float>& p)
{
return p.second == 3.0;
}
};
int main()
{
map<float, float> m = { {1,7}, {2,4}, {3,2}, {4,3},
{5,6}, {6,1}, {7,3} };
// 找key
auto posKey = m.find(3.0);
if (posKey != m.end())
{
cout << "key 3.0 found ("
<< posKey->first << ":"
<< posKey->second << ")" << endl;
}
// 找value 方法1
/*auto posVal = find_if(m.begin(), m.end(), CompareThree());
if (posVal != m.end())
{
cout << "key 3.0 found ("
<< posVal->first << ":"
<< posVal->second << ")" << endl;
}*/
// 找value 方法2
auto posVal = find_if(m.begin(), m.end(),
[] (const pair<float, float>& elem) {
return elem.second == 3.0;
});
if (posVal != m.end())
{
cout << "key 3.0 found ("
<< posVal->first << ":"
<< posVal->second << ")" << endl;
}
return 0;
}
1.6综合运用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <map>
#include <string>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cctype>
using namespace std;
class RuntimeStringCmp
{
public:
enum cmp_mode {normal, nocase};
private:
const cmp_mode mode;
static bool nocase_compare(char c1, char c2)
{
return toupper(c1) < toupper(c2);
}
public:
RuntimeStringCmp(cmp_mode m = normal) : mode(m)
{
}
bool operator() (const string& s1, const string& s2) const {
if (mode == normal)
{
return s1 < s2;
}
else
{
return lexicographical_compare(s1.begin(), s1.end(),
s2.begin(), s2.end(),
nocase_compare);
}
}
};
typedef map<string, string, RuntimeStringCmp> StringStringMap;
void fillAndPrint(StringStringMap& coll);
int main()
{
StringStringMap coll1;
fillAndPrint(coll1);
RuntimeStringCmp ignorecase(RuntimeStringCmp::nocase);
StringStringMap coll2(ignorecase);
fillAndPrint(coll2);
}
void fillAndPrint(StringStringMap& coll)
{
coll["Deutschland"] = "Germany";
coll["deutsch"] = "German";
coll["Haken"] = "snag";
coll["arbeien"] = "work";
coll["Hund"] = "dog";
coll["gehen"] = "go";
coll["Unternehmen"] = "ebterprise";
coll["unternehmen"] = "undertake";
coll["gehen"] = "walk";
coll["Bestatter"] = "undertaker";
cout.setf(ios::left, ios::adjustfield);
for (const auto& elem : coll)
{
cout << setw(15) << elem.first << " "
<< elem.second << endl;
}
cout << endl;
}