C++入门教程全章思考题汇编
# C++入门教程 全章思考题汇编
汇总《C++入门到精通》18 章全部思考题,每题配一句核心解答思路。共 321 题。
# 目录
# 00. README
# 01. Cpp简史
Q01.1 C++98 和 C++03 在语言层面有差别吗?为什么大部分书把它们当作"同一版本"来讲?
简答:
C++支持多范式。选择标准:核心业务逻辑需要多态用OOP,计算密集型用模板,混合时边界接口用OOP、内部用模板优化。
Q01.2 C++11 引入 nullptr 的根本原因是什么?写一段代码,演示 NULL 在重载场景下的歧义。
简答:
C++最初是C的超集,现在已有不兼容之处。兼容性好让C库可直接调用,问题在于最佳实践不同(malloc vs new),混用容易出bug。
Q01.3 C++17 的 std::filesystem 在 C++14 时代你会用什么替代?
简答:
auto减少冗长类型声明,Lambda让函数式编程自然,智能指针消除手动delete,移动语义避免不必要拷贝——让代码更安全、更简洁。
Q01.4 为什么 Concepts 和 Modules 等了将近 20 年才进标准?
简答:
不同编译器对未定义行为的处理不同。启示:用标准特性而非扩展,用-std=c++17统一行为,多编译器CI验证。
Q01.5 假如你正在维护一个用 C++03 写了十年的金融交易系统,你会建议升级到 C++17 还是 C++20?说明理由。
简答:
在线编译器:零配置快速验证语法片段。本地IDE:完整调试和重构工具。学新特性用在线,开发用本地。
Q01.6 std::optional
解决了什么问题?没有它的时候,C++ 程序员怎么表达"可能没有值"?
简答:
推荐CMake + GCC/Clang。CMake解决跨平台构建,GCC/Clang遵循标准。同时用CI跑MSVC做Windows验证。
Q01.7 用一句话告诉非 C++ 程序员"C++11 跟 C++98 的最大区别是什么"。
简答:
尖括号从系统目录搜索(如/usr/include),双引号先从当前目录再搜系统。标准库用<>,自己的头文件用""。
Q01.8 查一下你机器上的 GCC / Clang 版本,能编译 C++ 几?
简答:
使用std::cout等全限定名。大型项目中不建议全局using namespace std,避免命名冲突。
Q01.9 Boost 库与 C++ 标准库是什么关系?什么时候应该用 Boost?
简答:
返回0表示正常退出(EXIT_SUCCESS),非0表示异常。脚本可通过$?获取返回值做流程控制——Unix基本契约。
Q01.10 假如你要选一门"未来 5 年最值得深入"的语言,C++ 还是 Rust?请给出至少 3 条对比理由。
简答:
分步编译可并行(-j),只编译修改过的文件,适合大工程。一步编译适合hello world。增量编译是效率基石。
# 02. 基础语法
Q02.1 C++既支持面向过程又支持面向对象,那么在实际项目中如何选择使用哪种范式?什么场景下混合使用更好?
简答:
声明告诉编译器类型和名字(可多次),定义分配存储(只能一次)。头文件声明+源文件定义=修改实现不需重编译所有使用者。
Q02.2 为什么说"任何合法的C程序都是合法的C++程序"?这种兼容性在实际开发中带来了哪些好处和潜在问题?
简答:
声明时初始化更安全(C++11 {}最安全)。先声明后赋值有中间未初始化的风险窗口。现代C++强烈推荐声明时初始化。
Q02.3 C++11引入了很多现代特性(如auto、Lambda等),你认为这些特性解决了旧版C++的哪些痛点?
简答:
局部变量随代码块结束销毁,全局/静态变量程序结束后系统回收。理解这点才能避免悬空引用——永远不要返回局部变量的引用。
Q02.1 为什么不同编译器对同一份C++代码可能产生不同的行为?这对跨平台开发有什么启示?
简答:
声明告诉编译器类型和名字(可多次),定义分配存储(只能一次)。头文件声明+源文件定义=修改实现不需重编译所有使用者。
Q02.2 在线编译器和本地IDE各有什么优缺点?在什么场景下你会选择使用在线编译器?
简答:
声明时初始化更安全(C++11 {}最安全)。先声明后赋值有中间未初始化的风险窗口。现代C++强烈推荐声明时初始化。
Q02.3 如果你要开发一个跨平台项目(同时支持Windows、Linux、macOS),你会选择哪个编译器?为什么?
简答:
局部变量随代码块结束销毁,全局/静态变量程序结束后系统回收。理解这点才能避免悬空引用——永远不要返回局部变量的引用。
Q02.1 #include
中的尖括号 < > 和双引号 " " 有什么区别?分别在什么场景使用?
简答:
声明告诉编译器类型和名字(可多次),定义分配存储(只能一次)。头文件声明+源文件定义=修改实现不需重编译所有使用者。
Q02.2 如果去掉 using namespace std;,程序该如何修改才能正常运行?
简答:
声明时初始化更安全(C++11 {}最安全)。先声明后赋值有中间未初始化的风险窗口。现代C++强烈推荐声明时初始化。
Q02.3 return 0 中的返回值 0 有什么含义?如果返回其他值(如1)会怎样?
简答:
局部变量随代码块结束销毁,全局/静态变量程序结束后系统回收。理解这点才能避免悬空引用——永远不要返回局部变量的引用。
Q02.1 分步编译(先编译再链接)相比一步编译有什么优势?在大型项目中为什么通常选择分步编译?
简答:
声明告诉编译器类型和名字(可多次),定义分配存储(只能一次)。头文件声明+源文件定义=修改实现不需重编译所有使用者。
Q02.2 为什么发布版本推荐使用 -O2 而不是 -O3?在什么场景下 -O3 可能反而导致性能下降?
简答:
声明时初始化更安全(C++11 {}最安全)。先声明后赋值有中间未初始化的风险窗口。现代C++强烈推荐声明时初始化。
Q02.3 调试版本(-g -O0)和发布版本(-O2)的可执行文件有什么区别?为什么调试时不建议开优化?
简答:
局部变量随代码块结束销毁,全局/静态变量程序结束后系统回收。理解这点才能避免悬空引用——永远不要返回局部变量的引用。
Q02.1 "好的代码是自文档化的"——这是否意味着不需要写注释?你认为什么样的注释才是有价值的?
简答:
声明告诉编译器类型和名字(可多次),定义分配存储(只能一次)。头文件声明+源文件定义=修改实现不需重编译所有使用者。
Q02.2 多行注释 /* */ 不能嵌套使用,如果需要临时注释掉一段已包含多行注释的代码,该怎么做?
简答:
声明时初始化更安全(C++11 {}最安全)。先声明后赋值有中间未初始化的风险窗口。现代C++强烈推荐声明时初始化。
Q02.3 在团队协作中,统一的注释规范有什么好处?你觉得 Doxygen 风格的文档注释在哪些场景下特别有用?
简答:
局部变量随代码块结束销毁,全局/静态变量程序结束后系统回收。理解这点才能避免悬空引用——永远不要返回局部变量的引用。
Q02.1 为什么在头文件中使用 using namespace std; 被认为是不好的实践?如果所有头文件都这样做会发生什么?
简答:
声明告诉编译器类型和名字(可多次),定义分配存储(只能一次)。头文件声明+源文件定义=修改实现不需重编译所有使用者。
Q02.2 匿名命名空间和 static 关键字都能实现"文件内部可见",它们有什么区别?C++更推荐哪种方式?
简答:
声明时初始化更安全(C++11 {}最安全)。先声明后赋值有中间未初始化的风险窗口。现代C++强烈推荐声明时初始化。
Q02.3 在实际大型项目中,命名空间该如何组织?请思考一个包含UI、网络、数据库三个模块的项目的命名空间设计。
简答:
局部变量随代码块结束销毁,全局/静态变量程序结束后系统回收。理解这点才能避免悬空引用——永远不要返回局部变量的引用。
Q02.1 #pragma once 和 #ifndef/#define/#endif 两种头文件保护方式各有什么优缺点?在实际项目中你更倾向于使用哪种?
简答:
声明告诉编译器类型和名字(可多次),定义分配存储(只能一次)。头文件声明+源文件定义=修改实现不需重编译所有使用者。
Q02.2 为什么模板类的实现不能放在 .cpp 文件中?如果强行这样做会出现什么错误?
简答:
声明时初始化更安全(C++11 {}最安全)。先声明后赋值有中间未初始化的风险窗口。现代C++强烈推荐声明时初始化。
Q02.3 "最小化头文件包含"原则要求尽量使用前置声明替代 #include,哪些场景下可以用前置声明,哪些场景下必须用 #include?
简答:
局部变量随代码块结束销毁,全局/静态变量程序结束后系统回收。理解这点才能避免悬空引用——永远不要返回局部变量的引用。
Q02.1 const 成员函数中为什么不能修改成员变量?mutable 关键字的存在意义是什么?请举一个 mutable 的实际应用场景。
简答:
声明告诉编译器类型和名字(可多次),定义分配存储(只能一次)。头文件声明+源文件定义=修改实现不需重编译所有使用者。
Q02.2 explicit 关键字防止了什么问题?如果不使用 explicit,Animal a = "test"; 这行代码会发生什么?
简答:
声明时初始化更安全(C++11 {}最安全)。先声明后赋值有中间未初始化的风险窗口。现代C++强烈推荐声明时初始化。
Q02.3 final 关键字可以修饰类和虚函数,两种用法分别在什么场景下有用?过度使用 final 会带来什么问题?
简答:
局部变量随代码块结束销毁,全局/静态变量程序结束后系统回收。理解这点才能避免悬空引用——永远不要返回局部变量的引用。
Q02.1 C++有60多个关键字,而C语言只有32个。你认为关键字越多代表语言越强大还是越复杂?这种复杂性对学习和开发有什么影响?
简答:
声明告诉编译器类型和名字(可多次),定义分配存储(只能一次)。头文件声明+源文件定义=修改实现不需重编译所有使用者。
Q02.2 auto 关键字在C++11中被重新定义为自动类型推导,它在C语言中的含义是什么?为什么C++可以安全地改变这个关键字的含义?
简答:
声明时初始化更安全(C++11 {}最安全)。先声明后赋值有中间未初始化的风险窗口。现代C++强烈推荐声明时初始化。
Q02.3 C++20引入了 concept、co_await、co_yield 等新关键字,你觉得C++语言未来的发展方向是什么?新特性的引入应该遵循什么原则?
简答:
局部变量随代码块结束销毁,全局/静态变量程序结束后系统回收。理解这点才能避免悬空引用——永远不要返回局部变量的引用。
# 03. 数据类型
Q03.1 int在不同平台上大小可能不同(16位或32位),这对可移植性有什么影响?什么时候应该使用int32_t替代int?
简答:
int在16位/32位/64位平台上大小不同。跨平台用int32_t确保一致性,特别是文件格式和网络协议场景。
Q03.2 void*指针可以指向任何类型,那为什么不能直接解引用?这种设计背后的安全考量是什么?
简答:
void*是类型擦除——编译器不知道指向什么类型,禁止解引用。这是类型安全设计。必须先static_cast回具体类型再使用。
Q03.3 numeric_limits
::digits10返回6-7,这意味着什么?为什么说float"只有6-9位有效数字"?
简答:
digits10=6表示float精确6位十进制数,第7位可能有误差。这是IEEE 754单精度的数学极限,不是bug。
Q03.1 为什么C++标准规定有符号整数溢出是"未定义行为",而无符号整数溢出是"定义良好的"(模运算)?
简答:
int在16位/32位/64位平台上大小不同。跨平台用int32_t确保一致性,特别是文件格式和网络协议场景。
Q03.2 在比较 int 和 unsigned int 时,编译器会进行隐式转换导致意外结果。如何避免这类bug?
简答:
void*是类型擦除——编译器不知道指向什么类型,禁止解引用。这是类型安全设计。必须先static_cast回具体类型再使用。
Q03.3 为什么网络编程中通常使用 uint32_t 而不是 int?字节序(大端/小端)又会带来什么问题?
简答:
digits10=6表示float精确6位十进制数,第7位可能有误差。这是IEEE 754单精度的数学极限,不是bug。
Q03.1 char类型的符号性是"实现定义的",这意味着同一段代码在不同平台上可能有不同行为。如何编写可移植的字符处理代码?
简答:
int在16位/32位/64位平台上大小不同。跨平台用int32_t确保一致性,特别是文件格式和网络协议场景。
Q03.2 大小写转换时为什么 'A' + 32 == 'a'?如果ASCII编码表的设计不是这样排列的,会对编程产生什么影响?
简答:
void*是类型擦除——编译器不知道指向什么类型,禁止解引用。这是类型安全设计。必须先static_cast回具体类型再使用。
Q03.3 C++11引入了char16_t和char32_t,而之前只有wchar_t。为什么需要三种宽字符类型?它们各自适用什么场景?
简答:
digits10=6表示float精确6位十进制数,第7位可能有误差。这是IEEE 754单精度的数学极限,不是bug。
Q03.1 为什么 0.1 + 0.2 != 0.3?这和IEEE 754标准的二进制浮点表示有什么关系?
简答:
int在16位/32位/64位平台上大小不同。跨平台用int32_t确保一致性,特别是文件格式和网络协议场景。
Q03.2 金融计算为什么不能直接使用 float 或 double?实际项目中如何解决货币精度问题?
简答:
void*是类型擦除——编译器不知道指向什么类型,禁止解引用。这是类型安全设计。必须先static_cast回具体类型再使用。
Q03.3 NaN != NaN 为 true,这看起来很反直觉。这种设计的理由是什么?如何正确判断一个值是否为NaN?
简答:
digits10=6表示float精确6位十进制数,第7位可能有误差。这是IEEE 754单精度的数学极限,不是bug。
Q03.1 bool类型在内存中占1个字节,但只需要1个bit就够了。为什么C++标准不让bool只占1bit?
简答:
int在16位/32位/64位平台上大小不同。跨平台用int32_t确保一致性,特别是文件格式和网络协议场景。
Q03.2 C语言没有原生的bool类型(C99引入了_Bool),早期C程序是如何模拟布尔逻辑的?
简答:
void*是类型擦除——编译器不知道指向什么类型,禁止解引用。这是类型安全设计。必须先static_cast回具体类型再使用。
Q03.3 if (ptr) 和 if (ptr != nullptr) 效果一样吗?你更推荐哪种写法?为什么?
简答:
digits10=6表示float精确6位十进制数,第7位可能有误差。这是IEEE 754单精度的数学极限,不是bug。
Q03.1 #define 宏常量和 const 常量各有什么优缺点?Effective C++为什么建议"尽量使用const替代#define"?
简答:
int在16位/32位/64位平台上大小不同。跨平台用int32_t确保一致性,特别是文件格式和网络协议场景。
Q03.2 C++11的统一初始化(花括号{})相比传统初始化有什么优势?它能防止什么类型的错误?
简答:
void*是类型擦除——编译器不知道指向什么类型,禁止解引用。这是类型安全设计。必须先static_cast回具体类型再使用。
Q03.3 const 和 constexpr 都表示"常量",它们的区别是什么?什么时候必须用 constexpr?
简答:
digits10=6表示float精确6位十进制数,第7位可能有误差。这是IEEE 754单精度的数学极限,不是bug。
Q03.1 C风格的强制转换 (int)x 和C++的 static_cast
(x) 有什么本质区别?为什么C++推荐使用后者?
简答:
int在16位/32位/64位平台上大小不同。跨平台用int32_t确保一致性,特别是文件格式和网络协议场景。
Q03.2 dynamic_cast 失败时返回 nullptr(指针)或抛出异常(引用),它的运行时开销来自哪里?什么时候可以用 static_cast 替代 dynamic_cast?
简答:
void*是类型擦除——编译器不知道指向什么类型,禁止解引用。这是类型安全设计。必须先static_cast回具体类型再使用。
Q03.3 如果对一个真正的 const 对象使用 const_cast 去掉 const 后修改它的值,会发生什么?为什么这是"未定义行为"?
简答:
digits10=6表示float精确6位十进制数,第7位可能有误差。这是IEEE 754单精度的数学极限,不是bug。
# 04. 运算符
Q04.1 运算符本质上是编译器的内置函数,那么a + b和operator+(a, b)在编译后有区别吗?
简答:
/对整数截断(5/2=2),%只对整数有效。负数取模结果符号C++11前实现定义,现在与被除数同号。
Q04.2 C++允许重载大部分运算符,但有哪些运算符不能重载?为什么?
简答:
i++先返回再自增(需副本),++i直接返回新值。非内置类型中i++有额外拷贝。循环中优先++i。
Q04.3 运算符重载使代码更直观(如向量用+相加),但过度使用也可能降低可读性。你认为什么样的运算符重载是合理的?
简答:
按位操作只在整数类型有效。应用:权限标志组合(flag|READ|WRITE)、硬件寄存器、位域打包。
Q04.1 为什么 i++ + ++i 是未定义行为?C++标准中的"序列点"规则是怎么定义的?
简答:
/对整数截断(5/2=2),%只对整数有效。负数取模结果符号C++11前实现定义,现在与被除数同号。
Q04.2 负数取模时,C++11规定结果的符号与被除数相同。如果你需要一个结果总是非负的取模操作,应该如何实现?
简答:
i++先返回再自增(需副本),++i直接返回新值。非内置类型中i++有额外拷贝。循环中优先++i。
Q04.3 在性能敏感的代码中,++i 和 i++ 哪个更快?对于基本类型和自定义类型(如迭代器),答案一样吗?
简答:
按位操作只在整数类型有效。应用:权限标志组合(flag|READ|WRITE)、硬件寄存器、位域打包。
Q04.1 链式赋值 a = b = c = 10 的求值顺序是从右到左还是从左到右?为什么赋值运算符要返回引用?
简答:
/对整数截断(5/2=2),%只对整数有效。负数取模结果符号C++11前实现定义,现在与被除数同号。
Q04.2 a += b 和 a = a + b 效果一样吗?对于内置类型没区别,但对于自定义类型(如大数类),性能上可能有差异,为什么?
简答:
i++先返回再自增(需副本),++i直接返回新值。非内置类型中i++有额外拷贝。循环中优先++i。
Q04.3 赋值运算符 = 和初始化看起来很像,但对于类对象来说,调用的是不同的函数(赋值运算符 vs 拷贝构造函数)。这两者有什么性能差异?
简答:
按位操作只在整数类型有效。应用:权限标志组合(flag|READ|WRITE)、硬件寄存器、位域打包。
Q04.1 为什么 if (x = 0) 不会报错而是正常执行?编译器有什么方法可以帮我们发现这类错误?
简答:
/对整数截断(5/2=2),%只对整数有效。负数取模结果符号C++11前实现定义,现在与被除数同号。
Q04.2 C++20引入了三路比较运算符<=>(太空船运算符),它解决了什么问题?与传统的6个比较运算符相比有什么优势?
简答:
i++先返回再自增(需副本),++i直接返回新值。非内置类型中i++有额外拷贝。循环中优先++i。
Q04.3 比较运算的底层通过CPU的CMP指令实现减法并设置标志位,那么比较两个浮点数时底层也是做减法吗?和整数比较有什么不同?
简答:
按位操作只在整数类型有效。应用:权限标志组合(flag|READ|WRITE)、硬件寄存器、位域打包。
Q04.1 短路求值除了提高效率外,还能保证程序安全(如先判空再解引用)。你能想到其他利用短路求值的实际场景吗?
简答:
/对整数截断(5/2=2),%只对整数有效。负数取模结果符号C++11前实现定义,现在与被除数同号。
Q04.2 如果需要确保两个函数都被调用(即使第一个的结果已经能确定整体结果),应该怎么做?
简答:
i++先返回再自增(需副本),++i直接返回新值。非内置类型中i++有额外拷贝。循环中优先++i。
Q04.3 逻辑运算符 && 和位运算符 & 看起来很像,它们有什么区别?混用会导致什么问题?
简答:
按位操作只在整数类型有效。应用:权限标志组合(flag|READ|WRITE)、硬件寄存器、位域打包。
Q04.1 为什么操作系统的文件权限(如Linux的rwx)通常用位标志而不是多个bool变量来实现?有什么优势?
简答:
/对整数截断(5/2=2),%只对整数有效。负数取模结果符号C++11前实现定义,现在与被除数同号。
Q04.2 异或运算有一个著名的应用:不用临时变量交换两个数(a^=b; b^=a; a^=b;)。请验证这个算法的正确性,它有什么限制?
简答:
i++先返回再自增(需副本),++i直接返回新值。非内置类型中i++有额外拷贝。循环中优先++i。
Q04.3 左移操作 x << n 等价于 x * 2^n,但如果左移导致溢出会怎样?有符号数和无符号数的行为一样吗?
简答:
按位操作只在整数类型有效。应用:权限标志组合(flag|READ|WRITE)、硬件寄存器、位域打包。
Q04.1 C++有十几个优先级级别,全部记住很难。实际编程中有什么简单的策略可以避免优先级错误?
简答:
/对整数截断(5/2=2),%只对整数有效。负数取模结果符号C++11前实现定义,现在与被除数同号。
Q04.2 a && b || c 等价于 (a && b) || c 还是 a && (b || c)?为什么?
简答:
i++先返回再自增(需副本),++i直接返回新值。非内置类型中i++有额外拷贝。循环中优先++i。
Q04.3 为什么说"当不确定优先级时,加括号永远是最安全的做法"?加括号会影响编译后的性能吗?
简答:
按位操作只在整数类型有效。应用:权限标志组合(flag|READ|WRITE)、硬件寄存器、位域打包。
Q04.1 为什么 struct D{char,int,char} 占12字节而 struct E{char,char,int} 只占8字节?内存对齐的规则是什么?
简答:
/对整数截断(5/2=2),%只对整数有效。负数取模结果符号C++11前实现定义,现在与被除数同号。
Q04.2 空类的 sizeof 为什么是1而不是0?如果是0会导致什么问题?
简答:
i++先返回再自增(需副本),++i直接返回新值。非内置类型中i++有额外拷贝。循环中优先++i。
Q04.3 sizeof(x++) 中的 x++ 不会执行,这是因为sizeof在编译时计算。那么对于C++11的变长数组(VLA),sizeof还是编译时计算吗?
简答:
按位操作只在整数类型有效。应用:权限标志组合(flag|READ|WRITE)、硬件寄存器、位域打包。
# 05. 复合类型
Q05.1 复合类型和基本类型的本质区别是什么?编译器如何在内存中布局复合类型?
简答:
复合类型在内存中按声明顺序布局(可能有padding)。编译器通过偏移量访问成员。struct和class在C++中几乎相同(默认权限不同)。
Q05.2 struct和class在C++中几乎相同(默认访问权限不同),为什么C++同时保留了这两个关键字?
简答:
历史:C++继承C的struct同时引入class。两者都保留让C代码可编译。用struct暗示POD数据容器,class暗示封装行为。
Q05.3 在实际项目中,你会在什么场景下选择使用结构体而不是类?
简答:
struct:纯数据容器、POD类型、与C交互。class:封装行为、继承层次。现代C++区分在模糊。
Q05.1 C风格字符串使用\0作为结束符,如果忘记了这个结束符会发生什么问题?
简答:
复合类型在内存中按声明顺序布局(可能有padding)。编译器通过偏移量访问成员。struct和class在C++中几乎相同(默认权限不同)。
Q05.2 string s = "Hello" 中,"Hello" 本身是C风格字符串还是C++字符串?转换过程是怎样的?
简答:
历史:C++继承C的struct同时引入class。两者都保留让C代码可编译。用struct暗示POD数据容器,class暗示封装行为。
Q05.3 为什么某些底层API(如操作系统API)仍然使用C风格字符串?什么时候必须使用c_str()转换?
简答:
struct:纯数据容器、POD类型、与C交互。class:封装行为、继承层次。现代C++区分在模糊。
Q05.1 string::find返回string::npos表示未找到,npos的值是什么?为什么不返回-1?
简答:
复合类型在内存中按声明顺序布局(可能有padding)。编译器通过偏移量访问成员。struct和class在C++中几乎相同(默认权限不同)。
Q05.2 cin >> str和getline(cin, str)读取字符串有什么区别?在什么场景下必须用getline?
简答:
历史:C++继承C的struct同时引入class。两者都保留让C代码可编译。用struct暗示POD数据容器,class暗示封装行为。
Q05.3 string的底层是如何管理内存的?频繁的字符串拼接(如循环中 s += "x")效率如何?有什么优化方法?
简答:
struct:纯数据容器、POD类型、与C交互。class:封装行为、继承层次。现代C++区分在模糊。
Q05.1 数组名在大多数情况下会"退化"为指针,那为什么在sizeof(arr)中数组名不退化?还有哪些场景下数组名不退化?
简答:
复合类型在内存中按声明顺序布局(可能有padding)。编译器通过偏移量访问成员。struct和class在C++中几乎相同(默认权限不同)。
Q05.2 动态数组用new[]分配后必须用delete[]释放,如果误用delete(不带[])会怎样?
简答:
历史:C++继承C的struct同时引入class。两者都保留让C代码可编译。用struct暗示POD数据容器,class暗示封装行为。
Q05.3 C++中有原生数组、std::array、std::vector三种"数组",它们各自的优缺点和适用场景是什么?
简答:
struct:纯数据容器、POD类型、与C交互。class:封装行为、继承层次。现代C++区分在模糊。
Q05.1 C++17引入了std::variant作为类型安全的联合体替代品,它和传统联合体相比有什么优势?
简答:
复合类型在内存中按声明顺序布局(可能有padding)。编译器通过偏移量访问成员。struct和class在C++中几乎相同(默认权限不同)。
Q05.2 用联合体进行类型双关(type punning,如将int的字节解释为float)在C++中是否是未定义行为?有什么安全的替代方式?
简答:
历史:C++继承C的struct同时引入class。两者都保留让C代码可编译。用struct暗示POD数据容器,class暗示封装行为。
Q05.3 联合体中可以包含有构造函数的类型吗(如std::string)?C++11对此有什么变化?
简答:
struct:纯数据容器、POD类型、与C交互。class:封装行为、继承层次。现代C++区分在模糊。
Q05.1 enum class不能隐式转换为整数,这带来了类型安全但有时也不方便。有没有优雅的方式让enum class支持位运算(如权限标志)?
简答:
复合类型在内存中按声明顺序布局(可能有padding)。编译器通过偏移量访问成员。struct和class在C++中几乎相同(默认权限不同)。
Q05.2 枚举的switch语句如果漏掉了某个case,编译器能发出警告吗?如何利用这个特性?
简答:
历史:C++继承C的struct同时引入class。两者都保留让C代码可编译。用struct暗示POD数据容器,class暗示封装行为。
Q05.3 C++中没有内置的"枚举转字符串"功能,你知道有哪些方法可以实现枚举和字符串的自动映射?
简答:
struct:纯数据容器、POD类型、与C交互。class:封装行为、继承层次。现代C++区分在模糊。
Q05.1 C++中struct可以包含成员函数,这和class有什么区别?在什么情况下你会选择用struct而不是class?
简答:
复合类型在内存中按声明顺序布局(可能有padding)。编译器通过偏移量访问成员。struct和class在C++中几乎相同(默认权限不同)。
Q05.2 结构体作为函数参数时,值传递和引用传递(const &)有什么性能差异?什么时候应该用引用传递?
简答:
历史:C++继承C的struct同时引入class。两者都保留让C代码可编译。用struct暗示POD数据容器,class暗示封装行为。
Q05.3 这个通讯录案例中,删除操作用"最后一个元素填充"的方式,这种方法有什么优缺点?有更好的删除策略吗?
简答:
struct:纯数据容器、POD类型、与C交互。class:封装行为、继承层次。现代C++区分在模糊。
Q05.1 vector和list都能存储一组元素,但性能特点完全不同。在什么场景下list比vector更合适?
简答:
复合类型在内存中按声明顺序布局(可能有padding)。编译器通过偏移量访问成员。struct和class在C++中几乎相同(默认权限不同)。
Q05.2 map和unordered_map都是键值对容器,底层分别用红黑树和哈希表实现。它们各自的时间复杂度和适用场景是什么?
简答:
历史:C++继承C的struct同时引入class。两者都保留让C代码可编译。用struct暗示POD数据容器,class暗示封装行为。
Q05.3 为什么C++11推荐用std::array替代原生数组?std::array有哪些原生数组不具备的安全特性?
简答:
struct:纯数据容器、POD类型、与C交互。class:封装行为、继承层次。现代C++区分在模糊。
# 06. 流程语句
Q06.1 理论上只用"顺序"和"选择"就能实现循环(通过goto或递归),那为什么还需要专门的循环结构?
简答:
无else:可以多个。if-else if-else:互斥分支。switch:整数/枚举多分支选择,比连续if更清晰。
Q06.2 任何程序都可以用这三种基本结构实现(结构化定理),你能解释为什么吗?
简答:
if-else本质是条件跳转(可嵌套),switch是查表跳转(整数/枚举)。switch对密集case编译器优化为跳转表。
Q06.3 函数调用也会改变执行流程,它属于三种基本结构中的哪一种?还是一种独立的流程控制方式?
简答:
三种分别是前测试/入口判断。for适合计次循环,while适合条件循环。嵌套循环注意复杂度。
Q06.1 if-else链和switch语句都能实现多分支选择,它们在性能上有差异吗?编译器对switch做了什么优化?
简答:
无else:可以多个。if-else if-else:互斥分支。switch:整数/枚举多分支选择,比连续if更清晰。
Q06.2 C++17引入了if语句的初始化器(如if (int x = getValue(); x > 0)),这种语法有什么好处?
简答:
if-else本质是条件跳转(可嵌套),switch是查表跳转(整数/枚举)。switch对密集case编译器优化为跳转表。
Q06.3 在实际项目中,当分支非常多时(如几十个case),除了switch还有什么更好的设计方式?(提示:考虑函数指针表或策略模式)
简答:
三种分别是前测试/入口判断。for适合计次循环,while适合条件循环。嵌套循环注意复杂度。
Q06.1 while(true)无限循环+内部break退出 vs do-while(condition) 循环,你更倾向于用哪种方式?各有什么优缺点?
简答:
无else:可以多个。if-else if-else:互斥分支。switch:整数/枚举多分支选择,比连续if更清晰。
Q06.2 C++11的范围for循环(for (auto x : container))和传统for循环相比有什么优势?什么场景下不能使用范围for?
简答:
if-else本质是条件跳转(可嵌套),switch是查表跳转(整数/枚举)。switch对密集case编译器优化为跳转表。
Q06.3 循环中应该把不变的计算放在循环外(循环不变量外提),编译器的-O2优化能自动做到这点吗?
简答:
三种分别是前测试/入口判断。for适合计次循环,while适合条件循环。嵌套循环注意复杂度。
Q06.1 break只能跳出最内层循环,如果需要跳出多层嵌套循环,除了goto和标志变量,还有什么方法?(提示:可以把循环封装成函数)
简答:
无else:可以多个。if-else if-else:互斥分支。switch:整数/枚举多分支选择,比连续if更清晰。
Q06.2 goto在C++中被认为是"不好的实践",但Linux内核代码中大量使用了goto做错误处理。你如何看待这个矛盾?
简答:
if-else本质是条件跳转(可嵌套),switch是查表跳转(整数/枚举)。switch对密集case编译器优化为跳转表。
Q06.3 continue和break看似简单,但在复杂循环中使用不当可能导致逻辑混乱。有什么最佳实践可以避免这种问题?
简答:
三种分别是前测试/入口判断。for适合计次循环,while适合条件循环。嵌套循环注意复杂度。
# 07. 函数
Q07.1 函数声明和定义为什么要分离?在什么情况下函数必须先声明后使用?
简答:
声明告诉编译器函数签名,定义给出函数体。声明放头文件,定义放源文件——接口与实现分离。
Q07.2 Lambda表达式中的捕获列表[=]、[&]、[this]分别是什么含义?值捕获和引用捕获各有什么风险?
简答:
传值:拷贝(大对象开销大)。传引用:别名操作(高效但可能修改原值)。传const引用:只读访问+高效。
Q07.3 递归函数的栈空间有限制吗?如果递归层数太深会发生什么?如何用迭代替代递归?
简答:
按值:拷贝传入。按引用:可修改原始变量。按指针:和引用类似但可为空,语义更灵活。
Q07.1 对于大型对象(如包含1000个int的结构体),值传递的性能开销有多大?在什么场景下值传递反而更合适?
简答:
声明告诉编译器函数签名,定义给出函数体。声明放头文件,定义放源文件——接口与实现分离。
Q07.2 const T&和T作为函数参数的选择标准是什么?对于基本类型(如int)推荐哪种?
简答:
传值:拷贝(大对象开销大)。传引用:别名操作(高效但可能修改原值)。传const引用:只读访问+高效。
Q07.3 C++11引入了"右值引用"(T&&)和"移动语义",它提供了第四种传递方式。它解决了什么问题?
简答:
按值:拷贝传入。按引用:可修改原始变量。按指针:和引用类似但可为空,语义更灵活。
Q07.1 函数返回局部变量时,C++编译器会执行"返回值优化"(RVO/NRVO),这是什么意思?它如何避免不必要的拷贝?
简答:
声明告诉编译器函数签名,定义给出函数体。声明放头文件,定义放源文件——接口与实现分离。
Q07.2 返回tuple和返回结构体,哪种方式更好?各有什么优缺点?
简答:
传值:拷贝(大对象开销大)。传引用:别名操作(高效但可能修改原值)。传const引用:只读访问+高效。
Q07.3 为什么返回局部变量的引用是危险的?编译器能检测到这个错误吗?
简答:
按值:拷贝传入。按引用:可修改原始变量。按指针:和引用类似但可为空,语义更灵活。
Q07.1 如果调用print(42.0f)会匹配到哪个重载?为什么?如果同时存在print(int)和print(double),float参数会怎样?
简答:
声明告诉编译器函数签名,定义给出函数体。声明放头文件,定义放源文件——接口与实现分离。
Q07.2 为什么C++不允许仅通过返回类型来区分重载函数?从编译器的角度思考这个问题。
简答:
传值:拷贝(大对象开销大)。传引用:别名操作(高效但可能修改原值)。传const引用:只读访问+高效。
Q07.3 C++的函数重载底层是通过"名称修饰"(Name Mangling)实现的。extern "C"为什么能禁止名称修饰?这对与C语言库交互有什么影响?
简答:
按值:拷贝传入。按引用:可修改原始变量。按指针:和引用类似但可为空,语义更灵活。
Q07.1 inline关键字只是对编译器的"建议",编译器可以忽略。那么在什么条件下编译器会拒绝内联?
简答:
声明告诉编译器函数签名,定义给出函数体。声明放头文件,定义放源文件——接口与实现分离。
Q07.2 现代编译器(如GCC -O2)即使没有inline关键字也会自动内联小函数,那么inline关键字还有什么实际作用?(提示:考虑链接问题)
简答:
传值:拷贝(大对象开销大)。传引用:别名操作(高效但可能修改原值)。传const引用:只读访问+高效。
Q07.3 为什么虚函数不能内联?如果编译器能确定虚函数调用的具体对象,是否可以内联?
简答:
按值:拷贝传入。按引用:可修改原始变量。按指针:和引用类似但可为空,语义更灵活。
Q07.1 默认参数必须从右到左设置,不能跳过中间的参数。如果需要只覆盖最后一个参数(跳过中间的),有什么替代方案?
简答:
声明告诉编译器函数签名,定义给出函数体。声明放头文件,定义放源文件——接口与实现分离。
Q07.2 默认参数应该写在声明中还是定义中?如果两处都写会怎样?
简答:
传值:拷贝(大对象开销大)。传引用:别名操作(高效但可能修改原值)。传const引用:只读访问+高效。
Q07.3 函数重载和默认参数都能实现"少传参数也能调用"的效果,它们的适用场景有何不同?如何避免二者之间的歧义?
简答:
按值:拷贝传入。按引用:可修改原始变量。按指针:和引用类似但可为空,语义更灵活。
Q07.1 结构体作为函数返回值时,现代编译器会进行RVO优化避免拷贝。那如果返回的是一个很大的结构体,是否还需要担心性能?
简答:
声明告诉编译器函数签名,定义给出函数体。声明放头文件,定义放源文件——接口与实现分离。
Q07.2 const Employee&和const Employee*都能表示"只读访问",哪种方式在语义上更好?如何选择?
简答:
传值:拷贝(大对象开销大)。传引用:别名操作(高效但可能修改原值)。传const引用:只读访问+高效。
Q07.3 这个案例中Employee是一个简单的数据容器(POD类型),如果它有复杂的构造函数和析构函数,传递方式的选择会有什么变化?
简答:
按值:拷贝传入。按引用:可修改原始变量。按指针:和引用类似但可为空,语义更灵活。
Q07.1 函数指针 vs Lambda vs std::function:C++ 中有三种"可调用对象"的表示方式。函数指针最轻量但不能捕获状态;Lambda可以捕获但每个Lambda类型唯一;std::function最灵活但有运行时开销。你在什么场景下会选择哪种?
简答:
声明告诉编译器函数签名,定义给出函数体。声明放头文件,定义放源文件——接口与实现分离。
# 08. 指针引用
Q08.1 int* p 和 int p 和 int * p 三种写法有区别吗?在多变量声明int p, q;中,q是什么类型?
简答:
指针存内存地址,引用是别名(本质是指针的语法糖)。引用不可为null、不可重新绑定、更安全。
Q08.2 nullptr(C++11)比NULL和0有什么优势?为什么C++11要引入新关键字?
简答:
解除野指针导致UB(可能崩溃/改随机内存)。悬空指针指向已释放对象。用智能指针/RAII彻底解决。
Q08.3 野指针和空指针哪个更危险?为什么?如何养成良好的指针使用习惯?
简答:
空指针检查是防御型编程的基础。用if(ptr)或assert处理。现代C++用optional表示可能为空的值。
Q08.1 p + 1实际上在地址上增加了sizeof(int)字节,这种"类型感知"的指针算术是如何实现的?如果p是void*能做算术吗?
简答:
指针存内存地址,引用是别名(本质是指针的语法糖)。引用不可为null、不可重新绑定、更安全。
Q08.2 指向数组尾后位置的指针arr + n是合法的,但解引用它是未定义行为。为什么C++允许创建这样的指针?
简答:
解除野指针导致UB(可能崩溃/改随机内存)。悬空指针指向已释放对象。用智能指针/RAII彻底解决。
Q08.3 两个不相关的指针(指向不同数组)可以比较大小吗?C++标准对此怎么说?
简答:
空指针检查是防御型编程的基础。用if(ptr)或assert处理。现代C++用optional表示可能为空的值。
Q08.1 const int* 和 int* const 的区别是什么?有一个记忆技巧叫"const在左是底层const,在右是顶层const",请解释这个规则。
简答:
指针存内存地址,引用是别名(本质是指针的语法糖)。引用不可为null、不可重新绑定、更安全。
Q08.2 数组作为函数参数时会退化为指针,丢失了长度信息。有什么方法可以保留数组长度信息?(提示:考虑模板或std::array)
简答:
解除野指针导致UB(可能崩溃/改随机内存)。悬空指针指向已释放对象。用智能指针/RAII彻底解决。
Q08.3 通过指针实现的swap函数和通过引用实现的swap函数有什么区别?C++标准库的std::swap用的是哪种方式?
简答:
空指针检查是防御型编程的基础。用if(ptr)或assert处理。现代C++用optional表示可能为空的值。
Q08.1 函数指针数组(跳转表)在性能上比switch语句有什么优势?游戏引擎和解释器中为什么常用这种模式?
简答:
指针存内存地址,引用是别名(本质是指针的语法糖)。引用不可为null、不可重新绑定、更安全。
Q08.2 C++11的std::function和传统函数指针相比有什么优势?它能存储哪些函数指针不能存储的可调用对象?
简答:
解除野指针导致UB(可能崩溃/改随机内存)。悬空指针指向已释放对象。用智能指针/RAII彻底解决。
Q08.3 二级指针(指针的指针)在实际编程中有哪些应用场景?为什么C函数有时需要char**参数?
简答:
空指针检查是防御型编程的基础。用if(ptr)或assert处理。现代C++用optional表示可能为空的值。
Q08.1 const int& ref = 42; 是合法的,编译器在背后做了什么?为什么非const引用不能绑定字面量?
简答:
指针存内存地址,引用是别名(本质是指针的语法糖)。引用不可为null、不可重新绑定、更安全。
Q08.2 引用在底层通常是用指针实现的,那为什么说"引用不占额外内存"?这是否矛盾?
简答:
解除野指针导致UB(可能崩溃/改随机内存)。悬空指针指向已释放对象。用智能指针/RAII彻底解决。
Q08.3 C++11引入了右值引用(int&&),它和左值引用(int&)有什么区别?为什么需要右值引用?
简答:
空指针检查是防御型编程的基础。用if(ptr)或assert处理。现代C++用optional表示可能为空的值。
Q08.1 Google C++风格指南建议"输入参数用const引用,输出参数用指针",这个规则背后的理由是什么?你同意吗?
简答:
指针存内存地址,引用是别名(本质是指针的语法糖)。引用不可为null、不可重新绑定、更安全。
Q08.2 在哪些C++特性中必须使用引用(不能用指针替代)?(提示:考虑运算符重载、拷贝构造函数等)
简答:
解除野指针导致UB(可能崩溃/改随机内存)。悬空指针指向已释放对象。用智能指针/RAII彻底解决。
Q08.3 C++的智能指针(unique_ptr、shared_ptr)结合了指针的灵活性和引用的安全性,它们是否完全替代了裸指针?什么场景下仍需要使用裸指针?
简答:
空指针检查是防御型编程的基础。用if(ptr)或assert处理。现代C++用optional表示可能为空的值。
Q08.1 指针与虚拟内存:程序中的指针存储的是虚拟地址而非物理地址。同一个指针值0x7ffd1234在不同进程中指向不同的物理内存。请思考:如果两个进程中的指针值相同,它们指向的数据相同吗?操作系统是如何通过**页表(Page Table)**实现虚拟地址到物理地址的映射的?
简答:
指针存内存地址,引用是别名(本质是指针的语法糖)。引用不可为null、不可重新绑定、更安全。
# 09. 类和对象
Q09.1 如果把 Circle 的 radius 改为 public,代码功能不变,但设计上有什么隐患?
简答:
类封装数据和行为(数据隐藏),对象是类的实例。类=蓝图,对象=房子。
Q09.2 sizeof(Circle) 的值是多少?如果给 Circle 添加一个虚函数,sizeof 会如何变化?为什么?
简答:
public:外部可访问。protected:派生类可访问。private:只有本类和友元可访问。默认(class)private。
Q09.3 struct 和 class 除了默认访问权限不同,在继承时的默认访问方式有何区别?
简答:
构造函数初始化对象,析构函数清理资源。规则:资源获取即初始化(RAII),析构函数不应抛出异常。
Q09.1 栈上对象和堆上对象的生命周期有何不同?什么时候应该用 new 创建对象?
简答:
类封装数据和行为(数据隐藏),对象是类的实例。类=蓝图,对象=房子。
Q09.2 为什么 nextId 要声明为 static?如果去掉 static 会有什么问题?
简答:
public:外部可访问。protected:派生类可访问。private:只有本类和友元可访问。默认(class)private。
Q09.3 display() 函数后面的 const 有什么作用?如果去掉会影响哪些调用场景?
简答:
构造函数初始化对象,析构函数清理资源。规则:资源获取即初始化(RAII),析构函数不应抛出异常。
Q09.1 如果去掉 explicit,Employee e = "张三" 能编译通过吗?这种隐式转换可能带来什么问题?
简答:
类封装数据和行为(数据隐藏),对象是类的实例。类=蓝图,对象=房子。
Q09.2 静态成员函数为什么不能访问非静态成员变量?从 this 指针的角度解释。
简答:
public:外部可访问。protected:派生类可访问。private:只有本类和友元可访问。默认(class)private。
Q09.3 如果 salary 是 public 的,不使用 setter,如何保证数据一致性(如 totalSalary 的正确性)?
简答:
构造函数初始化对象,析构函数清理资源。规则:资源获取即初始化(RAII),析构函数不应抛出异常。
Q09.1 如果去掉自定义拷贝构造函数,使用编译器默认的浅拷贝,s3 析构时会发生什么?
简答:
类封装数据和行为(数据隐藏),对象是类的实例。类=蓝图,对象=房子。
Q09.2 passByValue(s2) 改为引用传递 passByValue(const MyString& s) 后,拷贝构造还会被调用吗?性能有何差异?
简答:
public:外部可访问。protected:派生类可访问。private:只有本类和友元可访问。默认(class)private。
Q09.3 委托构造函数中,能否在委托调用之前执行其他初始化代码?为什么?
简答:
构造函数初始化对象,析构函数清理资源。规则:资源获取即初始化(RAII),析构函数不应抛出异常。
Q09.1 如果 Car 的构造函数中不使用初始化列表,而在函数体内给 engine 和 tire 赋值,会发生什么?
简答:
类封装数据和行为(数据隐藏),对象是类的实例。类=蓝图,对象=房子。
Q09.2 对象成员的构造顺序是由初始化列表中的顺序决定的,还是由声明顺序决定的?
简答:
public:外部可访问。protected:派生类可访问。private:只有本类和友元可访问。默认(class)private。
Q09.3 如果把 Engine engine 改为 Engine* engine(指针),构造和析构顺序会如何变化?需要注意什么?
简答:
构造函数初始化对象,析构函数清理资源。规则:资源获取即初始化(RAII),析构函数不应抛出异常。
Q09.1 链式调用 house.setFloors(3).addGarage() 中,如果 setFloors 返回的是值而非引用(Builder 而非 Builder&),链式调用还能正常工作吗?结果有什么不同?
简答:
类封装数据和行为(数据隐藏),对象是类的实例。类=蓝图,对象=房子。
Q09.2 为什么空类的 sizeof 是1而不是0?如果给空类加一个虚函数,sizeof 变为多少?
简答:
public:外部可访问。protected:派生类可访问。private:只有本类和友元可访问。默认(class)private。
Q09.3 mutable 关键字的典型使用场景是什么?滥用它会有什么风险?
简答:
构造函数初始化对象,析构函数清理资源。规则:资源获取即初始化(RAII),析构函数不应抛出异常。
Q09.1 友元关系是单向的还是双向的?如果 A 是 B 的友元,B 是否自动成为 A 的友元?
简答:
类封装数据和行为(数据隐藏),对象是类的实例。类=蓝图,对象=房子。
Q09.2 友元关系能否被继承?如果 BankAuditor 有派生类,派生类是否也是 BankAccount 的友元?
简答:
public:外部可访问。protected:派生类可访问。private:只有本类和友元可访问。默认(class)private。
Q09.3 除了友元,还有什么方式可以让外部访问类的私有成员?各有什么优缺点?
简答:
构造函数初始化对象,析构函数清理资源。规则:资源获取即初始化(RAII),析构函数不应抛出异常。
Q09.1 C++对象模型与C结构体的差异:C++的class在底层和C的struct几乎相同(只有非静态数据成员存储在对象中)。那么成员函数存储在哪里?编译器是如何将obj.func()翻译为普通函数调用的?virtual函数又如何改变了这一机制?
简答:
类封装数据和行为(数据隐藏),对象是类的实例。类=蓝图,对象=房子。
# 10. 继承多态
Q10.1 如果把 class Student : public Person 改为 class Student : private Person,外部还能通过 stu.name 访问吗?protected 继承呢?
简答:
继承复用接口和实现,多态允许同一接口不同行为。组合一:基类指针/引用调用派生类方法。
Q10.2 子类 introduce() 隐藏了父类所有同名函数(包括重载版本),这是设计还是缺陷?如何用 using 声明解决?
简答:
公有继承表示is-a关系。私有继承是implemented-in-terms-of(实现继承),通常可以被组合替代。
Q10.3 父类的 private 成员在子类中是"不可访问"还是"不存在"?通过 sizeof 如何验证?
简答:
虚函数用vtable实现运行时绑定。析构函数声明virtual避免通过基类指针delete子类时UB。
Q10.1 如果去掉 virtual 关键字,mfd.id 的访问会出现什么错误?为什么?
简答:
继承复用接口和实现,多态允许同一接口不同行为。组合一:基类指针/引用调用派生类方法。
Q10.2 虚继承有性能开销吗?底层是通过什么机制(虚基类指针/虚基类表)实现的?
简答:
公有继承表示is-a关系。私有继承是implemented-in-terms-of(实现继承),通常可以被组合替代。
Q10.3 实际开发中,除了虚继承,还有什么设计方式可以避免菱形继承问题?
简答:
虚函数用vtable实现运行时绑定。析构函数声明virtual避免通过基类指针delete子类时UB。
Q10.1 如果 Shape 的析构函数不是虚函数,delete p 会发生什么?为什么多态基类必须有虚析构?
简答:
继承复用接口和实现,多态允许同一接口不同行为。组合一:基类指针/引用调用派生类方法。
Q10.2 override 关键字的作用是什么?如果派生类的函数签名和基类不一致(如忘了 const),不加 override 会怎样?
简答:
公有继承表示is-a关系。私有继承是implemented-in-terms-of(实现继承),通常可以被组合替代。
Q10.3 虚函数的底层实现机制是什么(虚函数表vptr/vtable)?每个对象的 vptr 占多少空间?
简答:
虚函数用vtable实现运行时绑定。析构函数声明virtual避免通过基类指针delete子类时UB。
Q10.1 如果 PDFDocument 没有实现 serialize(),会发生什么?它自己能被实例化吗?
简答:
继承复用接口和实现,多态允许同一接口不同行为。组合一:基类指针/引用调用派生类方法。
Q10.2 C++ 没有 interface 关键字,但可以用纯抽象类模拟接口。与 Java 的 interface 相比,C++ 的方式有什么优势和劣势?
简答:
公有继承表示is-a关系。私有继承是implemented-in-terms-of(实现继承),通常可以被组合替代。
Q10.3 虚析构函数写成 virtual ~ISerializable() = default; 和写成 virtual ~ISerializable() {} 有什么区别?
简答:
虚函数用vtable实现运行时绑定。析构函数声明virtual避免通过基类指针delete子类时UB。
Q10.1 虚函数的性能代价:虚函数调用比普通函数调用多了两次内存间接访问(读vptr + 读vtable slot)。在现代CPU上,这通常只有1-2个时钟周期的额外开销,但虚函数调用无法被内联,这才是真正的性能瓶颈。请思考:为什么虚函数不能内联?编译器的"去虚拟化(devirtualization)"优化是如何在某些场景下消除虚函数开销的?
简答:
继承复用接口和实现,多态允许同一接口不同行为。组合一:基类指针/引用调用派生类方法。
# 11. 内存模型
Q11.1 写一段代码,分别打印一个全局变量、一个静态局部变量、一个堆变量、一个栈变量的地址,画出它们在地址空间中的相对位置。
简答:
程序内存:栈(局部变量)、堆(动态分配)、全局区(全局/静态变量)、代码段(指令)。每个区域有不同生命周期和访问速度。
Q11.2 static 关键字在 C 和 C++ 中分别有几种含义?
简答:
对齐是CPU访问效率要求(某些架构必须对齐)。默认对齐=类型大小。alignas指定更大对齐,用alignof查询。
Q11.3 说出 5 种"无链接"的变量场景。
简答:
字节序影响整数/浮点存储。大端=高字节低地址(网络序),小端=高字节高地址(x86)。网络传输用htonl统一。
Q11.4 解释为什么"递归过深"会导致 SIGSEGV 而不是程序"慢死"。
简答:
Q11.5 int* p = new int[5]; 后,调用 delete p; 而不是 delete[] p; 会发生什么?
简答:
Q11.6 一个 64 位 Linux 进程理论上能用多少地址空间?实际能 new 多少?
简答:
Q11.7 写一段代码,故意制造内存泄漏,然后用 valgrind --tool=memcheck ./a.out 验证。
简答:
Q11.8 为什么栈分配比堆快?给出三条理由。
简答:
Q11.9 thread_local 变量与 static 变量在内存中的区别是什么?
简答:
Q11.10 如果你要实现一个 1ms 内必须返回的实时函数,你会怎样避免里面任何 new / malloc?
简答:
# 12. 动态内存
Q12.1 观察四个变量的地址,栈和堆的地址通常差异很大,这反映了什么内存布局特点?
简答:
new分配堆内存+调用构造,delete调用析构+释放。必须配对——new用delete,new[]用delete[]。
Q12.2 如果忘记 delete[] dynamicArr,程序短期内能正常运行吗?长期运行会怎样?
简答:
内存泄漏=分配未释放。规避:智能指针(自动管理)、RAII、代码审查工具(valgrind/ASan)。
Q12.3 new int(40) 和 new int[40] 有什么区别?对应的释放方式分别是什么?
简答:
new失败抛std::bad_alloc异常。nothrow new失败返回nullptr。大多数情况用异常处理。
Q12.1 如果对 new Student[2] 使用 delete 而非 delete[],会发生什么?析构函数会被正确调用吗?
简答:
new分配堆内存+调用构造,delete调用析构+释放。必须配对——new用delete,new[]用delete[]。
Q12.2 new intcount 末尾的 () 是什么意思?去掉后数组元素的初始值是什么?
简答:
内存泄漏=分配未释放。规避:智能指针(自动管理)、RAII、代码审查工具(valgrind/ASan)。
Q12.3 在什么情况下必须用 new 而不能用 vector?
简答:
new失败抛std::bad_alloc异常。nothrow new失败返回nullptr。大多数情况用异常处理。
Q12.1 如何检测程序中的内存泄漏?常用的工具有哪些(如Valgrind、AddressSanitizer)?
简答:
new分配堆内存+调用构造,delete调用析构+释放。必须配对——new用delete,new[]用delete[]。
Q12.2 为什么 delete nullptr 是安全的?C++标准是如何规定的?
简答:
内存泄漏=分配未释放。规避:智能指针(自动管理)、RAII、代码审查工具(valgrind/ASan)。
Q12.3 如果一个函数可能在中间 throw 异常,如何确保已分配的内存不会泄漏?(提示:RAII)
简答:
new失败抛std::bad_alloc异常。nothrow new失败返回nullptr。大多数情况用异常处理。
Q12.1 make_unique 和 make_shared 相比直接 new 有什么优势?(提示:异常安全、性能)
简答:
new分配堆内存+调用构造,delete调用析构+释放。必须配对——new用delete,new[]用delete[]。
Q12.2 shared_ptr 的引用计数本身是线程安全的吗?shared_ptr 指向的对象是线程安全的吗?
简答:
内存泄漏=分配未释放。规避:智能指针(自动管理)、RAII、代码审查工具(valgrind/ASan)。
Q12.3 什么场景下必须用 shared_ptr 而不能用 unique_ptr?
简答:
new失败抛std::bad_alloc异常。nothrow new失败返回nullptr。大多数情况用异常处理。
Q12.1 malloc(0) 会返回什么?new int[0] 呢?行为是否一致?
简答:
new分配堆内存+调用构造,delete调用析构+释放。必须配对——new用delete,new[]用delete[]。
Q12.2 realloc 如果扩展失败会返回 NULL,但原指针仍然有效。如果直接写 p = realloc(p, newsize) 而 realloc 失败了,会发生什么?
简答:
内存泄漏=分配未释放。规避:智能指针(自动管理)、RAII、代码审查工具(valgrind/ASan)。
Q12.3 为什么不能用 free 释放 new 分配的对象?delete 比 free 多做了什么?
简答:
new失败抛std::bad_alloc异常。nothrow new失败返回nullptr。大多数情况用异常处理。
Q12.1 上面的对象池在多线程环境中安全吗?如何添加线程安全支持?
简答:
new分配堆内存+调用构造,delete调用析构+释放。必须配对——new用delete,new[]用delete[]。
Q12.2 对象池中 release 的对象并没有调用析构函数,这在什么情况下可能有问题?
简答:
内存泄漏=分配未释放。规避:智能指针(自动管理)、RAII、代码审查工具(valgrind/ASan)。
Q12.3 标准库中的 std::allocator 和本案例的对象池有什么异同?
简答:
new失败抛std::bad_alloc异常。nothrow new失败返回nullptr。大多数情况用异常处理。
Q12.1 内存碎片问题:频繁的小块malloc/free会导致外部碎片(空闲内存总量足够但不连续)。内存分配器如何缓解碎片?jemalloc和tcmalloc相比glibc的ptmalloc2有什么优化?
简答:
new分配堆内存+调用构造,delete调用析构+释放。必须配对——new用delete,new[]用delete[]。
# 13. IO和文件
Q13.1 cerr 和 clog 都输出到标准错误流,它们的主要区别(缓冲)在什么场景下有实际影响?
简答:
程序通过流对象(stream)与外部设备交互。cin/cout是标准输入输出流,fstream操作文件。
Q13.2 cin >> name 遇到空格就停止了,如何读取带空格的整行输入?
简答:
文本模式会转换换行符(Windows:\r\n→\n)。二进制模式原样读写。跨平台数据用二进制。
Q13.3 stringstream 在类型转换(如字符串转数字)中非常方便,但C++11后有更好的替代方案吗?
简答:
Q13.1 二进制写入的文件在不同平台(Windows/Mac/Linux)间传输可能有什么兼容性问题?(提示:字节序、结构体对齐)
简答:
程序通过流对象(stream)与外部设备交互。cin/cout是标准输入输出流,fstream操作文件。
Q13.2 如果文件打开失败,程序应该如何优雅地处理?除了检查 is_open(),还有什么方式?
简答:
文本模式会转换换行符(Windows:\r\n→\n)。二进制模式原样读写。跨平台数据用二进制。
Q13.3 ios::out 和 ios::trunc 的关系是什么?直接用 ios::out 打开已有文件会发生什么?
简答:
Q13.1 seekg 和 seekp 分别控制读指针和写指针,在 fstream 中这两个指针是独立的还是共享的?
简答:
程序通过流对象(stream)与外部设备交互。cin/cout是标准输入输出流,fstream操作文件。
Q13.2 用 while(!file.eof()) 读文件有什么陷阱?为什么推荐 while(file >> data) 或 while(getline(file, line))?
简答:
文本模式会转换换行符(Windows:\r\n→\n)。二进制模式原样读写。跨平台数据用二进制。
Q13.3 如果要在文件的中间插入内容(而非覆盖),标准文件流能直接做到吗?需要怎样的策略?
简答:
Q13.1 fail() 和 bad() 有什么区别?什么错误会导致 bad() 为 true?
简答:
程序通过流对象(stream)与外部设备交互。cin/cout是标准输入输出流,fstream操作文件。
Q13.2 为什么 exceptions 设置了 failbit 后,正常读到文件末尾也会抛异常?
简答:
文本模式会转换换行符(Windows:\r\n→\n)。二进制模式原样读写。跨平台数据用二进制。
Q13.3 RAII 思想如何应用于文件操作?ifstream 析构时会自动关闭文件吗?
简答:
Q13.1 为什么要关闭sync_with_stdio:ios::sync_with_stdio(false)关闭了C++ IO流与C stdio的同步。同步保证了printf和cout交替使用时输出顺序正确。关闭后cout性能提升数倍,但混用printf和cout会导致输出乱序。请分析同步的底层实现原理。
简答:
程序通过流对象(stream)与外部设备交互。cin/cout是标准输入输出流,fstream操作文件。
# 14. 异常处理
Q14.1 如果在 calculate 函数中也加一层 try-catch 并重新抛出异常(throw;),对外部的调用者有什么影响?
简答:
throw抛出异常向上传播(栈展开),try-catch在调用链任一环节捕获处理。析构函数不应抛异常。
Q14.2 如果把 catch (const string& e) 改成 catch (string e)(值捕获),是否仍能正常工作?两者有什么区别?
简答:
标准异常层次:exception→runtime_error(+子类)/logic_error(+子类)。继承制设计,统一catch(exception&)。
Q14.3 五个测试用例中,如果去掉所有 try-catch,程序会在哪个测试点崩溃?为什么后面的测试不会执行?
简答:
Q14.1 如果将 catch (const ParseError& e) 移动到 catch (const SyntaxError& e) 前面,SyntaxError 异常还能被正确识别吗?为什么?
简答:
throw抛出异常向上传播(栈展开),try-catch在调用链任一环节捕获处理。析构函数不应抛异常。
Q14.2 如果抛出的是 double 类型(如 throw 3.14;),上面的 catch(int) 能否捕获?为什么?
简答:
标准异常层次:exception→runtime_error(+子类)/logic_error(+子类)。继承制设计,统一catch(exception&)。
Q14.3 在实际项目中,catch(...) 放在最后有什么好处?如果只写一个 catch(...) 不写其他 catch 会有什么问题?
简答:
Q14.1 validate 中如果把 throw; 改成 throw e;,异常类型会有什么变化?(提示:考虑对象切片问题)
简答:
throw抛出异常向上传播(栈展开),try-catch在调用链任一环节捕获处理。析构函数不应抛异常。
Q14.2 为什么 getInt 中选择捕获 invalid_argument 并转换为 ConfigError,而不是直接让 invalid_argument 传播出去?这样做的好处是什么?
简答:
标准异常层次:exception→runtime_error(+子类)/logic_error(+子类)。继承制设计,统一catch(exception&)。
Q14.3 如果在 load 函数中添加 noexcept 修饰,当传入空文件名时会发生什么?
简答:
Q14.1 transfer 中如果 withdraw 成功但 deposit 失败,会出现什么问题?如何实现异常安全的转账操作(提示:先检查再操作,或捕获异常后回滚)?
简答:
throw抛出异常向上传播(栈展开),try-catch在调用链任一环节捕获处理。析构函数不应抛异常。
Q14.2 noexcept 标记的函数如果意外抛出异常会怎样?为什么 printBalance 内部需要 try-catch 来保证 noexcept 承诺?
简答:
标准异常层次:exception→runtime_error(+子类)/logic_error(+子类)。继承制设计,统一catch(exception&)。
Q14.3 为什么自定义异常建议继承 runtime_error 而不是直接继承 exception?两种方式有什么区别?
简答:
Q14.1 异常 vs 错误码:C使用返回错误码(如errno),C++提供异常机制。Google C++风格指南曾禁止使用异常。请对比两种方式的优缺点。在什么场景下应该使用异常?什么场景下错误码更合适?C++23的std::expected<T,E>如何结合了两者的优点?
简答:
throw抛出异常向上传播(栈展开),try-catch在调用链任一环节捕获处理。析构函数不应抛异常。
# 15. 线程和锁
Q15.1 如果主线程在 detach 之后立即退出(不 sleep),后台日志线程的输出会怎样?为什么?
简答:
进程(独立资源空间,重)vs线程(共享地址空间,轻)。线程适合并行计算,创建销毁开销小。
Q15.2 如果忘记对某个 worker 线程调用 join() 或 detach(),程序会发生什么?
简答:
互斥量保护临界区(Mutex),条件变量用于线程间等待/通知(Condition Variable)。组合实现生产者-消费者。
Q15.3 使用 std::ref 传引用和直接传值给线程函数有什么区别?如果 downloadFile 的 filename 参数不是 const 引用会有什么问题?
简答:
Q15.1 为什么不加锁的结果总是小于期望值?++unsafeCounter 在 CPU 层面分为哪几步操作?
简答:
进程(独立资源空间,重)vs线程(共享地址空间,轻)。线程适合并行计算,创建销毁开销小。
Q15.2 mutex 方案比 atomic 慢很多倍,原因是什么?什么场景下必须用 mutex 而不能用 atomic?
简答:
互斥量保护临界区(Mutex),条件变量用于线程间等待/通知(Condition Variable)。组合实现生产者-消费者。
Q15.3 如果两个线程同时持有不同的 mutex 并试图获取对方的 mutex,会发生什么?如何避免?
简答:
Q15.1 logWithCheck 中使用 unique_lock + defer_lock 比直接使用 lock_guard 有什么优势?什么场景下这种延迟加锁策略能显著提升性能?
简答:
进程(独立资源空间,重)vs线程(共享地址空间,轻)。线程适合并行计算,创建销毁开销小。
Q15.2 如果把 logRecursive 中的 recursive_mutex 换成普通 mutex,会发生什么?为什么?
简答:
互斥量保护临界区(Mutex),条件变量用于线程间等待/通知(Condition Variable)。组合实现生产者-消费者。
Q15.3 mergeFrom 中如果不用 std::lock 同时锁定两个 mutex,而是先 lock(mtx_) 再 lock(other.mtx_),在什么情况下会导致死锁?
简答:
Q15.1 为什么 wait 必须使用 unique_lock 而不能使用 lock_guard?(提示:wait 内部需要临时释放锁)
简答:
进程(独立资源空间,重)vs线程(共享地址空间,轻)。线程适合并行计算,创建销毁开销小。
Q15.2 如果 push 中不使用谓词(cv.wait(lock) 而非 cv.wait(lock, pred)),在什么情况下会出现问题?
简答:
互斥量保护临界区(Mutex),条件变量用于线程间等待/通知(Condition Variable)。组合实现生产者-消费者。
Q15.3 close() 中为什么要用 notify_all 而不是 notify_one?如果改成 notify_one 会有什么后果?
简答:
Q15.1 compare_exchange_weak 和 compare_exchange_strong 有什么区别?为什么循环中一般用 weak 版本?
简答:
进程(独立资源空间,重)vs线程(共享地址空间,轻)。线程适合并行计算,创建销毁开销小。
Q15.2 自旋锁在什么场景下比 mutex 更高效?什么场景下反而更差?
简答:
互斥量保护临界区(Mutex),条件变量用于线程间等待/通知(Condition Variable)。组合实现生产者-消费者。
Q15.3 为什么 completedCount_ 的 fetch_add 使用 memory_order_release,而 getCompletedCount 使用 memory_order_acquire?如果都用 relaxed 会有什么问题?
简答:
Q15.1 如果把所有 shared_lock 都换成 unique_lock(即所有操作都用独占锁),性能会有什么变化?在读写比为 50:1 的场景下差距有多大?
简答:
进程(独立资源空间,重)vs线程(共享地址空间,轻)。线程适合并行计算,创建销毁开销小。
Q15.2 写操作执行时,正在读的线程会怎样?新的读请求会怎样?这体现了 shared_mutex 的什么特性?
简答:
互斥量保护临界区(Mutex),条件变量用于线程间等待/通知(Condition Variable)。组合实现生产者-消费者。
Q15.3 shared_mutex 是否存在写饥饿问题(即大量读操作导致写操作长时间无法获取锁)?如何解决?
简答:
Q15.1 锁的粒度:粗粒度锁(一把大锁保护所有数据)简单但并发度低;细粒度锁(每个数据一把锁)并发度高但容易死锁。Java的ConcurrentHashMap使用分段锁,请思考如何在C++中实现类似的设计。
简答:
进程(独立资源空间,重)vs线程(共享地址空间,轻)。线程适合并行计算,创建销毁开销小。
# 16. STL模版
Q16.1 调用 formatPair(string("age"), 25) 时,为什么匹配的是普通函数而非函数模板?如果把普通函数删掉会怎样?
简答:
STL基于容器/算法/迭代器三分离设计。容器存数据,算法操作数据,迭代器连接两者。编译时泛型——零运行时开销。
Q16.2 isEqual("hi", "hi") 如果没有特化版本,会发生什么?两个字符串字面量的地址是否相同?
简答:
vector栈上元数据+堆上连续存储。push_back超出capacity时重新分配(通常2倍增长)、移动元素、释放旧内存。
Q16.3 arraySum 中为什么用 T sum = T() 而不是 T sum = 0?对于非数值类型有什么影响?
简答:
Q16.1 sort 要求随机访问迭代器,如果把 vector 换成 list,还能直接用 std::sort 吗?应该怎么处理?
简答:
STL基于容器/算法/迭代器三分离设计。容器存数据,算法操作数据,迭代器连接两者。编译时泛型——零运行时开销。
Q16.2 map 中的键 "优秀"/"良好"/"及格" 的输出顺序为什么是按字典序而非插入序?如果需要保持插入顺序应该用什么容器?
简答:
vector栈上元数据+堆上连续存储。push_back超出capacity时重新分配(通常2倍增长)、移动元素、释放旧内存。
Q16.3 accumulate 的第三个参数为什么是 0 而不是 0.0?如果改成 0.0 并把 Lambda 的第一个参数改为 double,结果会有什么区别?
简答:
Q16.1 sort 默认是升序,partial_sort(begin, begin+3, end, greater<>()) 只排前3大的值,剩余元素的顺序是否确定?partial_sort 相比完整 sort 后取前3个有什么性能优势?
简答:
STL基于容器/算法/迭代器三分离设计。容器存数据,算法操作数据,迭代器连接两者。编译时泛型——零运行时开销。
Q16.2 binary_search 要求数据已经排序,如果对未排序的 temps 直接调用会怎样?返回值是否可靠?
简答:
vector栈上元数据+堆上连续存储。push_back超出capacity时重新分配(通常2倍增长)、移动元素、释放旧内存。
Q16.3 计算方差时用 inner_product(d.begin(), d.end(), d.begin(), 0.0) 巧妙地实现了"对自身做内积",这等价于什么数学运算?如果初始值用 0 而不是 0.0 会有什么问题?
简答:
Q16.1 模板元编程(TMP):C++模板系统是图灵完备的,可以在编译期计算任意结果(如编译期斐波那契数列)。C++17的constexpr if和if constexpr如何简化了TMP?C++20的Concepts如何替代了SFINAE(Substitution Failure Is Not An Error)技巧?
简答:
STL基于容器/算法/迭代器三分离设计。容器存数据,算法操作数据,迭代器连接两者。编译时泛型——零运行时开销。
# 17. 预处理器
Q17.1 #include
和 #include "iostream" 都能编译通过,它们的查找路径有什么区别?为什么标准库建议用 <>?
简答:
预处理在编译前执行:宏替换、头文件包含、条件编译。不参与实际编译(词法分析之前)。
Q17.2 如果 calculator.h 没有 #ifndef 保护,在 main.cpp 中两次 #include "calculator.h" 会发生什么?
简答:
Q17.3 什么情况下必须包含完整头文件而不能只用前置声明?(提示:考虑 sizeof、继承、成员变量等场景)
简答:
Q17.1 MAX(a++, 3) 展开后是什么?为什么 a 可能被递增两次?如何用 C++ 模板或 inline 函数避免这个问题?
简答:
预处理在编译前执行:宏替换、头文件包含、条件编译。不参与实际编译(词法分析之前)。
Q17.2 CLAMP 宏嵌套调用了 MIN 和 MAX,如果参数有副作用,问题会如何放大?
简答:
Q17.3 现代 C++ 中,哪些场景仍然必须用宏而无法用 constexpr、inline 或模板替代?
简答:
Q17.1 #pragma once 和 #ifndef/#define/#endif 各有什么优缺点?为什么一些项目仍然坚持使用传统的 #ifndef 方式?
简答:
预处理在编译前执行:宏替换、头文件包含、条件编译。不参与实际编译(词法分析之前)。
Q17.2 如果 math_utils.h 中没有头文件保护,但 Point 只在 shape.h 中通过前置声明 struct Point; 使用(不包含完整头文件),是否能避免重复定义问题?
简答:
Q17.3 头文件保护能防止同一翻译单元内的重复包含,但能防止不同 .cpp 文件中的重复定义吗?(提示:如果头文件中定义了非 inline 的全局函数会怎样?)
简答:
Q17.1 LINE 在宏定义中和在宏调用处展开的行号不同,为什么 MY_ASSERT 中的 LINE 显示的是调用处而非定义处的行号?
简答:
预处理在编译前执行:宏替换、头文件包含、条件编译。不参与实际编译(词法分析之前)。
Q17.2 func 和 FUNCTION 有什么区别?哪个是 C++ 标准规定的?
简答:
Q17.3 如果一个项目需要同时支持 GCC 和 MSVC,使用预定义宏做条件编译时需要注意什么?能否用 __has_include 来检测特性支持而非检测编译器?
简答:
Q17.1 #error 和 static_assert 都能在编译期阻止错误代码,它们分别在编译的哪个阶段工作?各适合检查什么类型的条件?
简答:
预处理在编译前执行:宏替换、头文件包含、条件编译。不参与实际编译(词法分析之前)。
Q17.2 #warning 在 MSVC 中不被支持,如何编写一个在所有编译器上都能产生警告的替代方案?
简答:
Q17.3 如果想检查"编译器是否支持某个头文件"(如
),应该用什么预定义宏?在 C++17 之前如何处理?
简答:
Q17.1 宏 vs constexpr vs inline:C++11之后,很多宏的使用场景可以被constexpr函数和inline函数替代。请对比三者在类型安全、调试支持、作用域控制方面的差异。哪些场景仍然必须使用宏?(提示:FILE、LINE、字符串化#)
简答:
预处理在编译前执行:宏替换、头文件包含、条件编译。不参与实际编译(词法分析之前)。
# 18. 特性图谱
Q18.1 C++11 中你认为最重要的 5 个特性是什么?为什么?
简答:
用-A标志查看当前编译器支持的C++特性版本。C++17完整支持std::optional/std::variant等现代特性。
Q18.2 假设你在一个仍用 C++11 的代码库工作,要补哪 3 个 Boost 特性可以"接近 C++17"?
简答:
特性表帮助判断新项目最低C++标准版本、评估升级风险、跨平台选择公共特性。长期维护项目尤其需要。
Q18.3 写一段代码,只用 C++17,实现"读文件、按行解析整数、过滤大于 0 的、求和"。
简答:
列c++17是因为:特性稳定(所有编译器完整支持)、有variant/optional等实用特性、向下兼容风险低。新项目建议最低c++17。
Q18.4 把上题用 C++20 Ranges 重写,对比可读性差异。
简答:
C++20的concept、ranges、coroutine改变编码范式但不是所有编译器完备。c++17更安全保守适合入门,c++20代表了未来方向。
Q18.5 std::expected<T, E> 与抛异常相比,分别在什么场景占优?
简答:
Q18.6 为什么 std::optional 在表达"可能没有值"时比"用 nullptr 哨兵"更好?
简答:
Q18.7 auto operator<=>(const Foo&) const = default; 究竟生成了几个运算符?
简答:
Q18.8 给一段 C++11 代码——把局部变量类型推导从 std::vector
::const_iterator 改成 auto,编译器还能正常推断吗?什么时候不能?
简答:
Q18.9 为什么协程被认为"难"?它的难点究竟在语法、还是在配套库?
简答:
Q18.10 假如让你今天给团队做 C++ 升级方案:从 C++11 → ?,理由是什么?
简答: