https://cppquiz.org/
16. 类成员构造析构顺序
子类的构造函数先执行类成员的构造函数,再执行函数体;析构相反。
17. 父类子类构造析构顺序
子类的构造函数先执行父类的构造函数,再执行函数体;析构相反。
默认执行无参构造函数,如果在初始化列表中写明,则按初始化列表为准。初始化列表会先于构造函数体执行。
25. 有符号整数溢出
有符号整数溢出是 UB。虽然大多数 CPU 会绕回,但总有不是的。具体见 C++ - 未定义行为 或 https://zhuanlan.zhihu.com/p/391088391。
38. decltype
注意 decltype((e)) 与 decltype(e) 可能不同,当 e 为标识符表达式时(左值)前者为 T&,后者为 T。
具体规则见 模板 - decltype。
48. async
没用过。TODO
113. 函数特化的匹配规则
可能同时存在重载的非模板函数与模板函数。选择要调用哪个函数的规则为:确定参数类型,先找出能够完全匹配的函数(模板可以匹配任意类型),如果有多个,优先选择非模板函数,其次是最特化的模板函数,最后是无特化的。
见 函数 - 函数特化的匹配规则。
126. 静态成员定义时的名字查找
https://zh.cppreference.com/w/cpp/language/unqualified_lookup 见静态数据成员的定义
对于在静态数据成员的定义中所使用的名字,它的查找规则与查找成员函数中使用的名字相同,即使这个定义可能在类外部。
(非 inline 静态数据成员可以延迟定义,只在类内进行声明。在定义前是不完整类型?)
struct X {static int x;static const int n = 1; // 第一个找到
};
int n = 2; // 第二个找到
int X::x = n; // x = 1
131. explicit
复制初始化选择构造函数时,不会考虑 explicit 函数。explicit 函数的参数需要通过直接初始化构造(即显式使用构造函数)。它不能用于隐式转换和复制初始化。
153. 指针转换
如果 cv2 的限定符比 cv1 更多,则“指向 cv1 T 的指针的纯右值”可以转换为“指向 cv2 T 的指针的纯右值”。否则不可。
(即可以额外加 cv,但不能去除)
由于"abc"
是 const char 数组可被隐式转换为const char*
,因此char *s = "abc"
会CE,因为去掉了 const。
160. 虚函数的默认参数值
设 A 是 B 的基类,A *x = new B()
,则称 x 的静态类型 (static type) 为其直接指向的基类 A,动态类型 (dynamic type) 为其实际类型 B。
注意,如果虚函数的参数有默认值,则默认值取决于 x 的静态类型 A 中的定义,即使虚函数的实现确实取决于 B。
如:父类 A 有虚函数virtual void f(int a = 1)
,子类 B 覆盖虚函数virtual void f(int a = 2)
,则x->f()
会调用B::f()
,但参数的默认值来自A::f()
,是 1。
161. switch case
case、default 只是一个 label,用于控制跳转(考虑转换成的汇编;和写 goto 类似),不会影响后面代码的顺序执行和跳转。
所以不加 break 时程序可以执行多个 case 内的语句。
197.
int i, j;
中在 i 进行声明时,这个 j 还不存在。
207. map.operator []
使用std::map m; m[key]
时,等价于调用m.try_emplace(move(key))
:如果该 key 不存在,则插入 pair(key, ValueType())。
默认放入的 value 会被值初始化:对于类则调用无参构造(如果没有则不进行初始化),对于非类则零初始化。见 C++ - 初始化。
226. mutable
设 A 类对象 a 拥有 mutable 的 B 类成员 b,则不管 a(或 *this)有没有 const 修饰,a.b 都不会有 const 修饰符,因此可以被修改。
因此将 a.b 传入函数时,会优先匹配B&
而非const B&
。
**235. initializer_list **
因为 initializer_list 只是保存指向常量数组的指针,因此将其直接传入参数也不会发生元素的拷贝(只是拷贝结构体,也就是两个指针)。
而通过变量构造它时,会发生变量到数组的拷贝。如:initializer_list<Node> i{node};
需要将 node 拷贝到指定区域。
见 STL - initializer_list。
248. min/max 返回 const 引用
std::min/max 返回 const T& 而非数值,且在各参数大小相同时返回第一个参数。所以const int &m = max(a, b)
定义的 m 值会随着 a/b 变化。
249. 引用初始化规则
由于引用初始化规则,将int
赋值给const char&
,会通过该int
隐式创建一个 char 临时量,绑定给该引用。因此原int
与该引用无关,修改也不会影响。
见 C++ - 引用初始化。
254. 函数类型
计算函数类型时,会移出参数类型中的 top-level cv 修饰符。见 函数 - 函数类型。
275. 类型大小
每个实现关于基础类型的大小所做的选择被统称为数据模型。
因此,基础类型的大小是 implementation-defined 的,标准只规定了一部分,比如 int 至少是 16 位的。
见 C++ - 数据模型。
283. 对象销毁顺序
类似栈变量的释放顺序,delete[] 一个数组时也是从后往前销毁元素的,与 new T[] 时的顺序相反。
289. 函数默认参数,lambda
默认参数不需要是常量,它在每次函数调用时,都会进行一次求值。见 函数 - 函数的默认参数。
每个 lambda 表达式会生成一个唯一的类。如果其内部有 static 变量,则每次调用 lambda 都会使用相同的 static 变量。