C++ Primer 读书笔记03

输出缓冲

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

导致缓冲刷新的原因:
  • 程序正常结束,作为 main函数的return操作的一部分,缓冲刷新
  • 缓冲区满时
  • 使用操纵符endl来显示刷新缓冲区
  • 每个输出操作后用操作符unitbuf设置流的内部状态,来清空缓冲区,默认cerr是设置unitbuf
  • 读写关联的流时,如读cin会导致cout的缓冲区被刷新
刷新缓冲区
cout << "hi" << endl;	  // 输出 hi 和一个换行,然后刷新缓冲区
cout << "hi" << flush;	// 输出 hi ,然后刷新缓冲区
cout << "hi" << ends;	  // 输出 hi 和一个空字符,然后刷新缓冲区
cout << unitbuf;	// 所有之后的输出操作都会立即刷新缓冲区
cout << nounitbuf;	// 恢复正常

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

顺序容器

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

vectorstring不支持push_frontemplace_back

emplace

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

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则会创建一个局部临时对象并将其压入容器中

frontback返回首元素和尾元素的引用,注意在调用之前,先用enpty判断是否为空

迭代器失效

在向容器添加元素后:

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

删除一个元素后:

  • 对于listforward_list,指向容器其他位置的迭代器(包括尾后迭代器和首前迭代器)、引用和指针有效
  • 对于deque,如果在首尾位置之外的任何位置删除位置,那么指向被删除元素外其他元素的迭代器、引用和指针会失效;如果删除deque的尾元素,则尾后迭代器会失效,其他不受影响;如果删除首元素,这些也不会受影响
  • 对于vectorstring,指向被删元素之前的迭代器、引用和指针仍有效;注意:当我们删除元素时,尾后迭代器总是会失效
vector
  1. size指已经保存的元素的数量
  2. capacity是在不分配新的内存空间前提下最多可以保存多少元素
容器适配器

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

栈适配器

默认情况下stackqueue是基于deque实现的,可以使用除arrayforward_list之外的任何容器类型来构造

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

queue适配器可以构造于listqueue之上,但不能基于vector构造

priority_queue可以构造于vectordeque之上,但不能基于list构造

lambda

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

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

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

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

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

lambda可以通过捕获列表使用定义它的函数的局部变量,但是只能使用哪些明确指明的变量

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

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创建时拷贝,而不是在调用时拷贝:

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

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

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

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

可变 lambda

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

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指定返回类型时,需要使用尾置返回类型

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

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

其形式如下:

auto newCallable = bind(callable, arg_list);c

arg_list的参数包含形如_n的名字,这些参数是占位符,表示newCallable的参数

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

插入迭代器

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

  • back_inserter创建一个使用push_back的迭代器
  • front_inserter创建一个使用```push_front的迭代器
  • inserter创建一个使用insert的迭代器,次函数接受第二个参数,一个指向容器的迭代器,元素被插入到给定迭代器的前面
iostream 迭代器

istream_iterator读取输入流,ostream_iterator向一个输出流写数据

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

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

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());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

thewangcj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值