C++ Primer, 5th Edition 笔记2

人仰马翻劳碌半月,终于有余暇可以看看书。

第II部分:C++标准库

IO库

P278

顺序容器

参见VectorListDequeChoose STL ContainerStringStackQueuePriority Queue

泛型算法

迭代器令算法不依赖于容器,但算法依赖于元素类型的操作。
算法永远不会执行容器的操作,只会运行于迭代器之上,执行迭代器的操作。算法永远不会改变底层容器的大小。算法可能改变容器中保存的元素的值,也可能在容器内移动元素,但永远不会直接添加或删除元素。除非使用了标准库定义的inserter。
参见AlgorithmFunction Object and Using LambdaIterator

关联容器

参看Map and MultimapSet and MultisetUnordered Container

动态内存

静态内存用来保存局部static对象、类static数据成员以及定义在任何函数之外的变量。
栈内存用来保存定义在函数内的非static对象。

动态内存与智能指针

参看Smart Pointers
或用new/delete直接管理内存。
注意不要混合使用普通指针和智能指针。

动态数组

应该使用标准库容器而不是动态分配的数组。new[]/delete[]。

使用allocator类

new有一些灵活性上的局限,其中一方面表现在它将内存分配和对象构造组合在了一起。类似的,delete将对象析构和内存释放组合在了一起。分配单个对象时,通常希望将内存分配和对象初始化组合一起。这样肯定知道对象应有什么值。
而当分配一大块内存时,通常计划在这块内存上按需构造对象。这时希望将内存分配和对象构造分离。我们可以分配大块内存,但只在真正需要时才真正执行对象创建操作。
allocator定义在头文件<memory>中,分配的内存是原始的、未构造的。用allocate分配内存后需要用construct创建对象。使用未构造的内存,行为是未定义的。用完对象后,必须对每个构造的元素调用destroy来销毁它们。只能对真正构造了的元素进行destroy操作。之后可以再构造其他元素或者用deallocate释放内存。
标准库allocator类及其算法

1
2
3
4
5
6
7
8
9
10
11
12
13
// allocator类
allocator<string> alloc; // 可以分配string的allocator对象
auto const p = alloc.allocate(n); // 分配n个未初始化的string
// allocator分配未构造的内存
auto q = p; // q指向最后构造的元素之后的位置
alloc.construct(q++); //*q为空字符串
alloc.construct(q++, 10, 'c'); //*q为cccccccccc
alloc.construct(q++, "hi"); //*q为hi
cout << *p << endl; //正确:使用string的输出运算符
cout << *q << endl; //灾难:q指向未构造的内存!
while (q != p)
alloc.destroy(--q); // 释放我们真正构造的string
alloc.deallocate(p, n); // 释放内存

标准库还为allocator类定义了两个伴随算法,可以在未初始化内存中创建对象。
allocator算法
例如,假定有一个int的vector,希望将其内容拷贝到动态内存中。我们将分配一块比vector中元素所占用空间大一倍的动态内存,然后将原vector中的元素拷贝到前一半空间,对后一半空间用一个给定值进行填充:

1
2
3
4
5
6
// 分配比vi中元素所占用空间大一倍的动态内存
auto p = alloc.allocate(vi.size() * 2);
// 通过拷贝vi中的元素来构造从p开始的元素
auto q = uninitialized_copy(vi.begin(), vi.end(), p);
// 将剩余元素初始化为42
uninitialized_fill_n(q, vi.size(), 42);

[1] C++ Primer, 5th Edition