CppQuiz

发布时间 2023-11-19 01:56:41作者: SovietPower

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 变量。