“C++ Primer”


输出缓冲

每个输出流都管理一个缓冲区,用来保存程序读写的数据

导致缓冲刷新的原因:
刷新缓冲区
1
2
3
4
5
cout << "hi" << endl;	  // 输出 hi 和一个换行,然后刷新缓冲区
cout << "hi" << flush;	// 输出 hi ,然后刷新缓冲区
cout << "hi" << ends;	  // 输出 hi 和一个空字符,然后刷新缓冲区
cout << unitbuf;	// 所有之后的输出操作都会立即刷新缓冲区
cout << nounitbuf;	// 恢复正常

如果程序异常终止,输出缓冲区是不会被刷新的

顺序容器

向一个vectorstringdeque插入元素会使所有指向容器的迭代器、引用和指针失效


##### emplace

使用```push_back```时实际上放入容器的是对象值得一个拷贝,而不是对象本身,而当调用一个```emplace```成员函数时,则是将参数传递给元素类型的构造函数,```emplace```成员使用这些参数在容器管理的内存空间中直接构造元素

```c++
c.emplace_back("1234", 25, 15.99);	// 在 c 的末尾构造一个 Sales_data 对象
c.push_back(Sales_data("1234", 25, 15.99));	// 创建一个临时的 Sales_data 对象传递给 push_back

调用emplace_back会在容器管理的内存空间中直接构造对象,而调用push_back则会创建一个局部临时对象并将其压入容器中


##### 迭代器失效

在向容器添加元素后:

* 如果容器是```vector```或```string```,且存储空间被重新分配,则指向容器的迭代器、引用和指针失效,如果存储空间为被重新分配,指向插入位置之前的元素的迭代器、引用和指针仍有效,但指向插入位置之后的迭代器、引用和指针失效
* 对于```deque```,插入到除首尾位置之外的任何位置都会导致迭代器、引用和指针失效,如果在首尾位置添加元素,迭代器会失效,但指向存在的元素的指针和引用不会失效
* 对于```list```和```forward_list```,指向容器的迭代器、引用和指针有效

删除一个元素后:

* 对于```list```和```forward_list```,指向容器其他位置的迭代器(包括尾后迭代器和首前迭代器)、引用和指针有效
* 对于```deque```,如果在首尾位置之外的任何位置删除位置,那么指向被删除元素外其他元素的迭代器、引用和指针会失效;如果删除```deque```的尾元素,则尾后迭代器会失效,其他不受影响;如果删除首元素,这些也不会受影响
* 对于```vector```或```string```,指向被删元素之前的迭代器、引用和指针仍有效;*注意*:当我们删除元素时,尾后迭代器总是会失效

#### vector

1. ```size```指已经保存的元素的数量
2. ```capacity```是在不分配新的内存空间前提下最多可以保存多少元素

#### 容器适配器

适配器是标准库的一个通用概念,容器、迭代器和函数都有适配器,本质上,一个适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样,一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型

##### 栈适配器

默认情况下```stack```和```queue```是基于```deque```实现的,可以使用除```array```和```forward_list```之外的任何容器类型来构造

```c++
stack<int> stk(deq);	// 假设 deq 是一个 deque<int> ,从deq 拷贝元素到 stk
stack<string, vector<string>> str_stk;	// 在 vector 上实现的空栈
队列适配器

```priority_queue```可以构造于```vector```和```deque```之上,但不能基于```list```构造

#### lambda

一个```lambda```表达式表示一个可调用的代码单元,可以理解为一个未命名的内联函数,其形式如下:

```c++
[capture list](parameter list) -> return type { function body }

其中 capture list (捕获列表)实验一个lambda所在函数中定义的局部变量的列表,与普通函数不同,lambda必须使用尾置返回

可以忽略参数列表和返回类型,但是必须包含捕获列表和函数体,如:

1
2
auto f = [] { return 42; }
cout << f() << endl;
捕获列表

捕获列表只用于局部非```static```变量,一个```lambda```可以使用定义在当前函数之外的名字,如```cout```

```c++
void biggies(vector<string> &words, vector<string>::size_type sz)
{
	elimDups(words); // 将 words 按字典排序,删除重复单词
  // 按长度排序,长度相同的单词维持字典排序
	stable_sort(words.begin(), words.end(), [](const string &a, const string &b) { return a.size() < b.size();});
  // 获取一个迭代器,指向第一个满足 size() >= sz 的元素
  auto wc = find_if(words.begin(), words.end(), [sz](const string &a) { return a.size() >= sz; });
	// 计算满足 size() >= sz 的元素的数目
	auto count = words.end() - wc;
  cout << count << " " << make_plural(count, "word", "s")
       << " of length " << sz << " or longer" << endl;
	// 打印长度大于等于给定值得单词
	for_each(wc, words.end(), [](const string &s){cout << s << " ";});
	cout << endl;
}

当定义一个lambda时,编译器生成一个与lambda对应的新的类类型

  1. 值捕获

    lambda捕获的变量的值是在lambda创建时拷贝,而不是在调用时拷贝:

    1
    2
    3
    4
    5
    6
    
    void fun1() {
      size_t v1 = 42;
      auto f = [v1] {return v1;};
      v1 = 0;
      auto j = f();	// j 为 42
    }
    
  2. 引用捕获

    1
    2
    3
    4
    5
    6
    
    void fun2() {
      size_t v1 = 42;
      auto f = [&v1] {return v1;};
      v1 = 0;
      auto j = f();	// j 为 0
    }
    
  3. 隐式捕获

    可以让编译器根据lambda体中的代码来推断我们要使用哪些变量,为了指示编译器推断捕获列表,应在捕获列表中写一个&或=

    &告诉编译器采用引用方式,=采用值捕获方式

可变 lambda

默认一个值被拷贝的变量,lambda不会改变其值,如果希望改变,需要在参数列表前加上mutable

1
2
3
4
5
6
void fun3() {
  size_t v1 = 42;
  auto f = [v1] mutable {return ++v1;};
  v1 = 0;
  auto j = f();	// j 为 43
}
指定 lambda 的返回类型

如果一个lambda体中包含return以外的任何语句,则编译器假定此lambda返回void

当我们要为一个lambda指定返回类型时,需要使用尾置返回类型

1
transform(vi.begin(), vi.end(), vi.begin(), [](int i) -> int {if (i < 0) return -i; else return i;});

bind(11)

定义在functional中,可以看做一个通用的函数适配器,接受一个可调用对象,生成一个新的可调用对象来适应原函数的参数列表

其形式如下:

1
auto newCallable = bind(callable, arg_list);

```c++
ostream &print(ostream &os, const string &s, char c) {
	return os << s << c;
}
for_each(words.begin(), words.end(), bind(print, ref(cout), _1, ' '));// _1 表明新的print函数只接受一个参数

为了使用_n,需要声明using namespace std::placeholders;

默认情况下,bind将不是占位符的参数拷贝到bind返回的可调用对象中,为了给bind传递一个对象而又不拷贝它,需要使用标准库ref函数,该函数也定义在functional

插入迭代器

插入迭代器是一种迭代器适配器,接受一个容器,生成一个迭代器,能实现向给定容器插入元素

iostream迭代器


```c++
istream_iterator<int> int_it(cin); // 从 cin 中读 int
istream_iterator<int> int_eof;     // 默认初始化迭代器,相当于创建了一个尾后迭代器
vector<int> v(int_it, int_eof);    // 构造 v

反向迭代器

除了forward_list之外,其他容器都支持反向迭代器

1
2
3
4
5
sort(vec.rbegin(), vec.rend());	// 逆序排序
// first,middle,last  在一个逗号分隔的列表中查找最后一个元素
auto rcomma = find(line.crbegin(), line.crend(), ',');
cout << string(lien.crbegin(), rcomma);	// 错误,将输出 tsal
cout << string(rcomma.base(), line.cend());