C++ Primer 学习笔记 0x00
1. std::clog
除了 std::cin, std::cout, std::cerr 还有一个与 C 输出流 stderr 关联但不同于 std::cerr 的 std::clog ,它不自动冲入这些流,且不自动与 std::cout tie() :
1 | std::clog << "hello world\n"; |
这些里面
c表示 char
2. 字符字面值
1 | std::cout << '\x82' // chr(0x82), 'R' |
3. 初始化
3.1. 精度损失
1 | int i = 0; |
这些都是正确的初始化语句,但:
1 | double d = 1.14514; |
会使编译器产生 warning ,因为从 double 转 int 会损失精度,而:
1 | double d = 1.14514; |
不会使编译器产生 warning 。
3.2. 类成员初始化顺序
1 | class Foo { |
类成员初始化的顺序和代码中的顺序一致,也就是说 Foo 的成员的初始化顺序为 i -> j ,所以构造函数中初始化列表就是有问题的,看起来好像先用 v 初始化了 j ,然后用 j 初始化了 i 。实际上会先用 j 初始化 i ,而这时 j 是未初始化的,所以是未定义行为。
3.3. 委托构造
1 | class Foo { |
这种叫委托构造,委托构造后面不能再接初始化列表了,比如:
1 | class Foo { |
3.4. 使用 static_cast 显式使用构造函数
众所周知构造函数前面加上 explicit 就禁止了隐式转换:
1 | class Foo { |
比如有这样的函数:
1 | void foo(const Foo& f) {} |
像这样用 static_cast 也可以实现转换:
1 | foo(static_cast<Foo>(1)); |
4. decltype
decltype 用多个 ( ) 包围时推断出来的类型为引用类型
1 | int i = 0; |
因为
i表示变量,而(i)是一个表达式,它的结果是一个整数
但多个 ( ) 包围一个返回值时不会被推断成引用类型:
1 | int foo(); |
5. sizeof
1 | int i = 114514; |
四种 sizeof 用法都是正确的,但习惯上求类型的大小用 sizeof(type) ,求某个变量的大小用 sizeof var
6. 隐式类型转换
在一下情况下编译器会自动地转换运算对象的类型:
- 在大多数表达式中,比
int类型小的整数类型值首先提升为较大的整数类型 - 在条件中,非
bool值会被转换成bool值 - 初始化过程中,初始值转换成变量的类型;在赋值语句中,右侧运算对象转换成左侧运算对象的类型
- 算术运算或关系运算中如果有多种类型的运算对象,则会转换成同一种类型
- 。。。
7. stdexcept
exception 头文件只定义 std::exception 类作为其他异常的基类, stdexcept 头文件定义了供用户/库使用的很多异常类
标准异常:
logic_error:程序内部错误的逻辑导致的错误,比如违背逻辑前提条件或打破了不可变条件,可能被避免invalid_argument:数值未被接受,比如std::stoi传入非整数字符串domain_error:定义域错误,数学表达式不合法或不能以数学方式表示,比如或 ,标准库组件不会抛出此异常 length_error:超出方法实现的长度限制导致的错误,比如std::string的长度超过了std::string::max_size()out_of_range:试图访问合法范围外的元素导致的错误future_error:异步和共享状态等机制失败时导致的错误
runtime_error:源于程序作用域外,且不可被轻易预测到的错误range_error:值域错误(计算结果超过了有意义的值域范围)overflow_error:计算上溢(计算结果对于目标类型过大)underflow_error:计算下溢(计算结果是无意义浮点值)
8. 字面值常量类
满足以下条件的类为字面值常量类:
- 数据成员都必须是字面值类型
- 至少含有一个
constexpr构造函数 - 类内初始化必须使用常量表达式或
constexpr函数 - 使用默认析构函数
9. while (std::cin >> var) 的原理
约定俗成这么用,但没仔细思考过,众所周知这个 >> 的原型差不多是:
1 | std::cin& operator>>(std::cin& in, T& var); |
既然返回的是 std::cin& ,为什么能作为 while 的条件呢,首先想到的是有 operator bool() ,查了一下还真是:
我这里是 ArchLinux 的 core/gcc 13.2.1-3
1 | /* -- /usr/include/c++/13.2.1/bits/basic_ios.h:117 -- */ |
10. 关联输入和输出流
把一个输入流与输出流关联起来后,在使用这个输入流进行输入前,关联的输出流会先被刷新,也就是说比如:
1 | std::string str; |
在进行输入前, “Input: “ 会被先打印出来,所以交互式系统关联输入流和输出流可以帮开发者完成刷新操作。绑定操作通过 tie 完成,输入新绑定的流的指针,返回之前绑定的流的指针,传入 nullptr 表示解绑:
1 | std::ostream* out = std::cin.tie(&std::cout); |
11. istringstream
可以像这样更精细地处理用户输入:
1 | std::string line; |