Skip to content

《C++百炼成仙·修行题库》

——叶小凡的C++修炼手札·完整版

脚本大陆·千鹤派·藏经阁认证金丹宝典

杨逸飞 编著


序·叶老开示

叶沧澜(盘坐于混沌虚空,袖中飞出一卷玉简):

“小娃娃,C++修仙,非读几卷书便能成事。代码如剑,不磨不利;语法如丹,不炼不纯。今为师为你编纂这部《修行题库》,共七篇一百四十四关,上应天罡,下接地煞,乃千鹤派三千年道统之精粹。

刷题三忌:

· 一忌贪快——每题必亲手敲入灵纹(编译器),不可只凭目测。 · 二忌弃难——遇未定义行为、模板推导错误,正是悟道良机。 · 三忌抄答——答为死物,思为活泉。抄得答案,抄不得道心。

刷题三要:

· 一要逐行debug,观灵纹流转之轨迹。 · 二要举一反三,改条件、换类型、添特化。 · 三要勤于批注,将心得刻于玉简之侧。

此书每题后均有叶老点拨,乃老夫千年修行所悟。你且好生修习,待一百四十四关尽破,自可凝丹成婴,叩问化神。”

叶小凡(捧简如奉圣物):“弟子……肝脑涂地!”


第一篇·炼气期——七灵根筑基(共20题)

1.1 数据类型·灵根初显(原卷3题,增补拆为6题)

题1(填空题): 在C++中,sizeof(int)通常为______字节,sizeof(double)为______字节,sizeof(char)为______字节。 若在64位系统下,sizeof(int*)为______字节。

叶老点拨:整型灵根长短不一,然指针玉匣之大小,只与天地位宽有关,与所指何物无关。此乃指针第一义。


题2(选择题): 下列哪个数据类型不可表示负数? A. int B. signed int C. unsigned int D. float

答案:C

解析:unsigned为纯阳灵根,不识负阴。其余皆可正可负。


题3(改错题): 叶小凡想计算0.1 + 0.2并期望得到0.3,他写下了以下代码:

cpp
#include <iostream>
int main() {
    float a = 0.1, b = 0.2;
    if (a + b == 0.3)
        std::cout << "相等" << std::endl;
    else
        std::cout << "不相等" << std::endl;
    return 0;
}

程序输出“不相等”。请指出错误原因并给出修正方案。

答案:浮点数二进制表示无法精确存储0.1、0.2,累加后产生微小误差,直接判等易失败。应使用容差比较:

cpp
#include <cmath>
if (std::abs((a + b) - 0.3) < 1e-6)

叶老点拨:浮点灵根如流水,看似有形,实则无定。修士当容其微尘之谬。


题4(填空题): 整数100的字面量类型是______;整数100u的类型是______;整数100l的类型是______;整数100ul的类型是______;浮点数3.14的类型是______;浮点数3.14f的类型是______。

答案:int、unsigned int、long、unsigned long、double、float。


题5(选择题): 下列哪种类型转换会丢失数据? A. double → int B. int → double C. int → long D. unsigned int → int(值超出正范围)

答案:A、D


题6(写出输出):

cpp
unsigned int a = 1;
int b = -1;
std::cout << (a + b) << std::endl;

答案:0(32位系统)。解析:混合运算时int转unsigned,-1成0xFFFFFFFF,加1得0x100000000,截断低32位为0。


1.2 运算符·灵气化合(原卷2题,增补拆为4题)

题7(写出输出):

cpp
int i = 1;
int a = i++ + ++i;
std::cout << a << std::endl;

答案:未定义行为。同一表达式对i两次修改无顺序点,避如天劫。


题8(简答题): 请解释左值(lvalue)与右值(rvalue)的区别。

答案:左值有名字、可取址、持久;右值临时、无名、将亡。左值持身份,右值持价值。


题9(编程题): 使用位运算符,将int x的第3位(从0开始)设置为1,其他位不变。

cpp
x |= (1 << 3);

题10(写出输出):

cpp
int x = 0b1100;
int y = 0b1010;
std::cout << (x & y) << std::endl;

答案:8(0b1000)。


1.3 循环·往复之术(原卷1题,增补拆为2题)

题11(编程题): 用三种循环(for、while、do-while)分别计算1到100的和。

参考代码(略)。


题12(改错题):

cpp
int i = 0;
while (i < 10);
{
    std::cout << i++;
}

错误:while后多了分号,形成死循环。去掉分号。


1.4 sizeof·洞府丈量(增补2题)

题13(填空题): 对于空类class Empty{};,sizeof(Empty)通常为______(非0),这是为了______。

答案:1,为了确保不同对象有不同地址。


题14(简答题): 为什么C++中不允许sizeof(void)?

答案:void是不完全类型,代表“无”,没有大小。若已知大小,则非void。


1.5 auto·天道择型(增补1题)

题15(选择题):

cpp
auto a = 1;
auto b = 1.0;
auto c = 1.0f;
auto d = {1, 2, 3};

d的类型是? A. int B. std::initializer_list<int> C. int[3] D. std::vector<int>

答案:B


1.6 decltype·以型推型(增补1题)

题16(填空题):

cpp
int x = 0;
decltype(x) y = 1;      // y的类型是______
decltype((x)) z = y;    // z的类型是______

答案:int、int&


1.7 运算符优先级·法咒先后(增补1题)

题17(写出输出):

cpp
int a = 1, b = 2, c = 3;
int r = a++ + ++b * c--;

答案:未定义行为(对c的读取与修改未定序)。实际环境可能得10,但不可依赖。


1.8 类型别名·更名诀(新增2题)

题18(填空题): C++11中定义别名模板使用关键字______,比typedef更强大。

答案:using


题19(编程题): 用using定义int_ptr为int*的别名。

cpp
using int_ptr = int*;

1.9 constexpr·编译期恒常(新增1题)

题20(改错题):

cpp
const int square(int x) { return x * x; }
int arr[square(5)];  // 可能编译错误,为什么?

答案:const函数不保证编译期求值,数组大小需要编译期常量。改为constexpr。


第二篇·筑基期——指针与洞府(共21题)

2.1 指针入门·天地坐标(原卷2题,增补拆为4题)

题21(填空题): 指针变量的本质是存储______。通过指针访问其所指对象,称为______,运算符为______。取变量地址的运算符为______。

答案:内存地址、解引用、*、&。


题22(改错题):

cpp
int* p;
*p = 10;

错误:野指针,未初始化。应int* p = nullptr;或指向有效变量。


题23(填空题): const int* p:p是______,指向______。 int* const p:p是______,指向______。 const int* const p:两者皆______。

答案:指向常量的指针(可改指,不可改值)、指针常量(可改值,不可改指)、都不可改。


题24(改错题):

cpp
const int a = 10;
int* p = &a;

错误:丢弃const限定。


2.2 数组·连续洞府(原卷2题,增补拆为3题)

题25(选择题): 对于数组int arr[5] = {1,2,3,4,5};,下列表达式错误的是: A. arr[2] B. *(arr + 2) C. arr++ D. &arr[0] + 1

答案:C(数组名是常量指针)。


题26(编程题): 不使用下标运算符[],仅用指针遍历二维数组int matrix[3][4]并输出所有元素。

参考代码:

cpp
int* p = &matrix[0][0];
for (int i = 0; i < 12; ++i, ++p)
    std::cout << *p << " ";

题27(写出输出):

cpp
int a[5] = {1,2,3,4,5};
int* p = a + 2;
std::cout << p[-1];

答案:2(p[-1]等价于*(p-1))。


2.3 动态内存·堆海借府(原卷2题,增补拆为3题)

题28(填空题): C++中从堆区申请单个对象用关键字______,释放用______;申请对象数组用______,释放用______。若不释放已无用的堆内存,将导致______。

答案:new、delete、new[]、delete[]、内存泄漏。


题29(改错题):

cpp
int* p = new int(10);
delete p;
std::cout << *p;

错误:野指针,delete后应置nullptr。


题30(改错题):

cpp
int* p = new int[10];
p += 5;
delete[] p;

错误:delete[]必须传入new[]返回的原始地址,不能偏移。


2.4 多级指针·层层锁钥(增补1题)

题31(编程题): 定义三级指针,指向二级指针,二级指针指向一级指针,一级指针指向int变量,并最终通过三级指针修改变量的值。

参考代码:

cpp
int x = 5;
int* p1 = &x;
int** p2 = &p1;
int*** p3 = &p2;
***p3 = 10;  // x == 10

2.5 函数指针·术法令牌(增补2题)

题32(填空题): 声明一个指向“参数为int, double,返回float”的函数指针pf: ______ pf;

答案:float (*pf)(int, double);


题33(编程题): 写一个函数calc,接受两个int和一个函数指针,返回运算结果。并用加法、乘法测试。


2.6 内存对齐·阵基安位(增补1题)

题34(填空题): alignas(16) int a;表示变量a按______字节对齐。alignof(int)返回int类型的对齐要求。

答案:16。


2.7 placement new·定点造府(增补2题)

题35(填空题): placement new的语法是new (______) Type(args);。它不分配内存,而是在______上构造对象。

答案:void*指针、已分配的内存。


题36(写出输出):

cpp
#include <new>
char buffer[sizeof(int)];
int* p = new(buffer) int(42);
std::cout << *p << std::endl;
p->~int();

答案:42。


2.8 智能指针·护体灵光(筑基版,增补2题)

题37(选择题): 在C++11中,以下哪种智能指针不能用于数组? A. std::unique_ptr<int[]> B. std::shared_ptr<int[]>(C++17前不支持) C. std::unique_ptr<int> D. std::auto_ptr<int>

答案:B(C++11不支持,C++17部分支持,通常需自定义删除器)。


题38(编程题): 使用std::unique_ptr管理动态数组,存储10个整数并赋值0~9。

cpp
auto arr = std::make_unique<int[]>(10);
for (int i = 0; i < 10; ++i) arr[i] = i;

2.9 内存泄漏检测·照妖镜(新增1题)

题39(简答题): 列举两种检测C++程序内存泄漏的方法。

答案:1. 使用Valgrind工具;2. 重载new/delete记录分配;3. Visual Studio的CRT调试堆函数。


2.10 指针与引用·别号之道(新增2题)

题40(选择题): 关于引用,下列说法正确的是: A. 引用必须初始化 B. 引用可以更改绑定 C. 引用占用内存(通常) D. 存在空引用

答案:A、C(引用底层用指针实现,占内存;但标准未规定,通常如此)。


题41(改错题):

cpp
int& r;  // 错误:______

答案:引用必须初始化。


第三篇·金丹期——类与对象(共20题)

3.1 封装·禁地之钥(原卷2题,增补拆为3题)

题42(简答题): private、protected、public三种访问修饰符的区别是什么?

答案:略。


题43(设计题): 设计一个class XiuZhe,包含私有属性姓名、年龄、灵根,提供公有set/get方法并校验。

参考代码:略。


题44(改错题):

cpp
class Test {
private:
    int x;
};
Test t;
t.x = 5;  // 错误:______

答案:私有成员不可在类外访问。


3.2 构造·丹炉点火(原卷2题,增补拆为3题)

题45(改错题):

cpp
class Test {
private:
    int& ref;
public:
    Test(int x) { ref = x; }
};

错误:引用成员必须在初始化列表中初始化。


题46(写出输出):

cpp
class Counter {
public:
    static int count;
    Counter() { ++count; }
    ~Counter() { --count; }
};
int Counter::count = 0;

Counter c1, c2;
std::cout << Counter::count;

答案:2。


题47(编程题): 为String类添加拷贝构造函数和拷贝赋值运算符(深拷贝)。


3.3 友元·破壁之钥(原卷1题,增补拆为2题)

题48(简答题): 友元的优缺点。

答案:优点——灵活,可访问私有成员;缺点——破坏封装,增加耦合。


题49(编程题): 声明全局函数printXiuZhe为XiuZhe类的友元,使其能输出私有成员。


3.4 拷贝控制·三/五法则(增补2题)

题50(简答题): 什么是三/五法则?

答案:若自定义析构、拷贝构造、拷贝赋值中的任一,通常三者都需要自定义;C++11后加上移动构造、移动赋值,共五个。


题51(改错题):

cpp
class String {
    char* data;
public:
    String(const char* s) { /* 分配拷贝 */ }
    ~String() { delete[] data; }
    // 缺少拷贝构造/赋值
};
String a = "hello";
String b = a;  // 浅拷贝!双删!

3.5 移动构造·窃丹术(增补1题)

题52(编程题): 为String类添加移动构造函数和移动赋值运算符,要求noexcept。


3.6 运算符重载·法器加持(增补2题)

题53(编程题): 实现class Complex,重载+、-、*、==、<<。


题54(写出输出):

cpp
class A {
public:
    int x;
    A(int x) : x(x) {}
    A operator+(const A& rhs) const { return A(x + rhs.x); }
};
A a1(1), a2(2);
std::cout << (a1 + a2).x;

答案:3。


3.7 explicit·隐式禁术(增补1题)

题55(改错题):

cpp
class C {
public:
    explicit C(int) {}
};
void func(C) {}
func(10);  // 错误:______

答案:explicit禁止隐式转换,需func(C(10))。


3.8 mutable·金丹内府(增补2题)

题56(填空题): mutable关键字用于类的______成员,允许在______成员函数中修改它。

答案:非静态、const。


题57(写出输出):

cpp
class Counter {
    mutable int cache = 0;
public:
    int get() const { return ++cache; }
};
Counter c;
std::cout << c.get() << c.get();

答案:12。


3.9 静态成员·宗门共业(增补2题)

题58(选择题): 静态成员函数不能: A. 访问静态成员变量 B. 访问非静态成员变量 C. 被派生类继承 D. 用类名调用

答案:B。


题59(编程题): 设计class Monastery记录弟子总数,提供静态函数getCount()。


3.10 内部类·府中有府(增补1题)

题60(简答题): 内部类能否访问外部类的私有成员?外部类能否访问内部类的私有成员?

答案:内部类是外部类的友元,可以访问外部类私有成员;外部类对内部类无私,不可访问内部类私有成员(除非显式友元)。


3.11 委托构造·一丹引二(新增1题)

题61(编程题): 使用C++11委托构造函数,令一个构造函数调用另一个构造函数。


第四篇·元婴期——继承与多态(共20题)

4.1 继承·血脉相承(原卷2题,增补拆为3题)

题62(选择题): 派生类继承了基类除____以外的所有成员。 A. 构造函数、析构函数、赋值运算符 B. 私有成员 C. 静态成员 D. 友元函数

答案:A。


题63(填空题): 派生类构造函数必须调用基类构造函数,若基类无默认构造,则必须在派生类的______中显式调用基类的______构造函数。

答案:初始化列表、带参。


题64(写出输出):

cpp
class A { public: A() { cout << "A"; } };
class B : public A { public: B() { cout << "B"; } };
int main() { B b; }

答案:AB。


4.2 虚函数·剑同法异(原卷2题,增补拆为3题)

题65(写出输出):

cpp
class Base {
public:
    virtual void show() { cout << "Base"; }
};
class Derived : public Base {
public:
    void show() override { cout << "Derived"; }
};
int main() {
    Base* p = new Derived();
    p->show();
    delete p;
}

答案:Derived。


题66(改错题):

cpp
class Base {
public:
    virtual void func() = 0;
};
Base b;  // 错误:______

答案:抽象类不可实例化。


题67(简答题): 虚函数表(vtable)存在哪里?每个对象都有虚表指针吗?

答案:虚表通常存于只读数据段,每个含虚函数的对象都有虚表指针(vptr)。


4.3 RTTI·辨其真身(原卷1题,增补拆为2题)

题68(编程题): 使用dynamic_cast安全地将基类指针转换为派生类指针。


题69(填空题): typeid(x).name()返回______,其头文件是______。

答案:类型名、<typeinfo>。


4.4 多重继承·一子承双(增补2题)

题70(选择题): 菱形继承(钻石问题)的解决方案是: A. 虚继承 B. 私有继承 C. 多重继承 D. 虚函数

答案:A。


题71(编程题): 用虚继承解决经典的菱形继承问题。


4.5 override/final·正名绝嗣(增补1题)

题72(填空题): override作用:;final作用在类上:,作用在虚函数上:______。

答案:显式标注覆盖,防止笔误;该类不可被继承;该虚函数不可被子类覆盖。


4.6 协变返回类型·剑变刃变(增补1题)

题73(选择题): 派生类覆盖基类虚函数时,返回类型可以不同,条件是: A. 返回类型必须是基类返回类型的派生类引用/指针 B. 必须完全相同 C. 任意 D. 必须有转换函数

答案:A。


4.7 类型转换操作符·化形诀(增补1题)

题74(编程题): 为Rational类定义到double的隐式转换操作符。

cpp
operator double() const { return (double)num / den; }

4.8 异常安全·劫中护阵(增补2题)

题75(简答题): 异常安全三个级别。

答案:基本保证、强保证、不抛保证(noexcept)。


题76(改错题):

cpp
void f() {
    int* p = new int(5);
    g();  // 可能抛异常
    delete p;
}

改正:使用智能指针或try-catch。


4.9 纯虚析构·仙人遗愿(新增1题)

题77(编程题): 声明一个纯虚析构函数,并在类外提供定义。为什么纯虚析构必须定义?

答案:派生类析构时会调用基类析构,若无定义则链接错误。


4.10 抽象类·不可名状(新增1题)

题78(选择题): 下列哪项不是抽象类的特征? A. 不能实例化 B. 至少一个纯虚函数 C. 可以作为基类指针类型 D. 可以有纯虚析构函数

答案:D(可以有,但D不是特征,抽象类的特征是有纯虚函数)。


第五篇·化神期——模板与泛型(共21题)

5.1 函数模板·泛法归一(原卷2题,增补拆为3题)

题79(编程题): 写一个函数模板myMax,返回两个参数中的较大者。


题80(选择题): 关于模板函数,下列说法正确的是: A. 隐式实例化 B. 通常定义在头文件 C. 支持重载 D. 以上都对

答案:D。


题81(改错题):

cpp
template<typename T>
T add(T a, T b) { return a + b; }
int x = add(1, 2.5);  // 错误:______

答案:模板参数推导冲突,一个int一个double。应显式指定add<double>(1,2.5)或使用两个模板参数。


5.2 类模板·铸器图谱(原卷1题,增补拆为2题)

题82(编程题): 实现Stack类模板,支持push、pop、top、empty,底层用std::vector。


题83(填空题): 类模板的成员函数定义在类外时,语法为: template<typename T> 返回类型 类名<T>::函数名(参数)


5.3 模板特化·一人一门(原卷1题,增补拆为2题)

题84(填空题): 全特化语法:template<> class 类名<具体类型> {}; 偏特化语法:template<typename T> class 类名<T*> {};


题85(编程题): 为Stack<bool>提供全特化,使用位压缩存储。


5.4 STL·上古神器(原卷2题,增补拆为3题)

题86(简答题): 简述迭代器的作用及五类迭代器。

答案:略。


题87(编程题): 使用std::vector和std::sort,配合std::greater<int>()降序排序。


题88(写出输出):

cpp
std::vector<int> v{3,1,4};
auto it = v.begin();
std::cout << *it << *(it+1);

答案:31。


5.5 可变参数模板·万剑诀(增补2题)

题89(编程题): 写可变参数模板print_all,打印所有参数。


题90(写出输出):

cpp
template<typename... Args>
auto sum(Args... args) { return (args + ...); }
std::cout << sum(1,2,3,4);

答案:10。


5.6 SFINAE·替换失败非罪(增补2题)

题91(填空题): SFINAE全称______,核心思想______。

答案:Substitution Failure Is Not An Error,替换失败不是错误。


题92(选择题): 常用于SFINAE的工具是: A. std::enable_if B. std::is_same C. decltype D. 以上都是

答案:D。


5.7 type_traits·类型之镜(增补1题)

题93(编程题): 使用std::is_integral和enable_if,实现只接受整数类型的half函数。


5.8 模板模板参数·铸器之器(增补1题)

题94(填空题): 模板模板参数形如:template<template<typename> class Container> class X;,其中Container是一个______。

答案:模板名(未实例化的类模板)。


5.9 别名模板·更名诀(增补1题)

题95(改错题):

cpp
typedef T* Ptr<T>;  // 错误:______

答案:typedef不支持模板。应template<typename T> using Ptr = T*;


5.10 编译期断言·天道誓言(增补1题)

题96(编程题): 用static_assert在编译期检查类型是指针,否则报错。


5.11 概念(C++20)·新劫符(增补1题)

题97(填空题): C++20概念:定义Integral概念要求T为整型: template<typename T> concept Integral = ______;

答案:std::is_integral_v<T>


5.12 模板元编程·编译时演道(新增2题)

题98(编程题): 编写编译期阶乘Factorial<5>::value。


题99(编程题): 编写编译期斐波那契Fibonacci<10>::value。


第六篇·大乘期——现代C++(共21题)

6.1 右值引用·将亡之物(原卷3题,增补拆为4题)

题100(填空题): 右值引用符号______,主要用途______。

答案:&&、移动语义。


题101(写出输出):

cpp
void func(int&)  { cout << "左值"; }
void func(int&&) { cout << "右值"; }
int a = 5;
func(a);
func(10);
func(std::move(a));

答案:左值、右值、右值。


题102(编程题): 为vector<int>实现移动构造函数(noexcept)。


题103(选择题): std::move的作用是: A. 将对象移动到新位置 B. 将左值转为右值引用 C. 移动资源所有权 D. 以上都是

答案:B(std::move只是转换,不移动)。


6.2 智能指针·护体灵光(原卷2题,增补拆为3题)

题104(选择题): 下列智能指针中,独占所有权的是: A. shared_ptr B. unique_ptr C. weak_ptr D. auto_ptr

答案:B。


题105(改错题):

cpp
shared_ptr<int> sp1(new int(10));
weak_ptr<int> wp = sp1;
shared_ptr<int> sp2 = wp;  // 错误:______

答案:weak_ptr不能直接赋值给shared_ptr,应用wp.lock()。


题106(简答题): weak_ptr如何解决循环引用?

答案:weak_ptr不增加引用计数,打破环。


6.3 Lambda·无名元丹(原卷1题,增补拆为2题)

题107(编程题): 使用Lambda,将vector每个元素乘以捕获的factor。


题108(填空题): Lambda表达式[=](int x) mutable -> int { return x+1; }中,[=]表示______,mutable允许______。

答案:值捕获所有变量、修改捕获的副本。


6.4 并发·驭雷之术(原卷1题,增补拆为2题)

题109(简答题): std::thread如果主线程结束而子线程未结束,会发生什么?如何正确处理?

答案:程序调用std::terminate崩溃。应join()或detach()。


题110(编程题): 创建两个线程,分别执行函数print,并用互斥锁保护cout。


6.5 结构化绑定·分光化影(增补2题)

题111(编程题): 用结构化绑定遍历std::map<int, std::string>。


题112(写出输出):

cpp
std::tuple<int, double> t{1, 2.5};
auto [x, y] = t;
x = 10;
std::cout << std::get<0>(t);

答案:1(绑定的是副本)。


6.6 if/switch with initializer·临阵布局(增补1题)

题113(改错题):

cpp
if (int x = foo(); x > 0) { /*...*/ }
std::cout << x;  // 错误:______

答案:x生命周期仅限于if。


6.7 filesystem·灵纹巡天(增补1题)

题114(编程题): 使用std::filesystem遍历当前目录下所有.cpp文件。


6.8 std::optional·或有或无(增补1题)

题115(编程题): 实现根据条件返回std::optional<int>,条件假返回空。


6.9 std::variant·多相玄珠(增补1题)

题116(选择题): std::variant<int, double>可以存储: A. 同时存储int和double B. 一次只存其中一种 C. 至少存储int D. 不能存储double

答案:B。


6.10 三路比较·天平衡器(增补1题)

题117(填空题): C++20operator<=>返回类型可能是______,头文件______。

答案:std::strong_ordering等,<compare>。


6.11 协程·元神出窍(增补1题)

题118(简答题): C++20协程关键字及其作用。

答案:co_await挂起,co_yield生成值,co_return返回。


6.12 并行算法·万剑齐发(增补1题)

题119(编程题): 用std::execution::par并行版std::for_each对vector平方。


6.13 原子操作·不坏金身(增补1题)

题120(改错题):

cpp
int counter = 0;
// 多线程 ++counter 不安全

改正:用std::atomic<int>。


6.14 内存序·天机可测(增补1题)

题121(简答题): 简述memory_order_relaxed、acquire、release、acq_rel、seq_cst。

答案:略。


第七篇·混沌飞升——综合实战(共21题)

7.1 异派标准·协约碑模拟(原卷1题)

题122(综合设计题): 设计简化版“异派标准”框架:抽象基类Protocol,纯虚implement;三个派生类代表三宗;全局函数signContract(Protocol*)演示多态;用vector<Protocol*>存储并遍历。

参考代码:略。


7.2 内存池·堆田辟荒(原卷1题)

题123(高级编程题): 实现定长内存池,链表管理空闲块,支持allocate/deallocate。


7.3 单例模式·宗门唯一(增补1题)

题124(编程题): 线程安全的单例MonasteryMaster,使用C++11 Magic Static。


7.4 工厂模式·铸器阁(增补1题)

题125(设计题): 法器工厂ArtifactFactory,根据字符串返回不同法器unique_ptr。


7.5 观察者模式·阵旗传讯(增补1题)

题126(编程题): 实现简单事件系统,支持注册监听器和触发事件。


7.6 线程池·驭兽群(增补1题)

题127(高级编程题): 实现简易线程池,固定工作线程,接收std::function<void()>任务。


7.7 RAII实践·自动结界(增补1题)

题128(编程题): 实现FileGuard类,RAII管理FILE*,不可拷贝,可移动。


7.8 异派标准·终章补遗(增补1题)

题129(开放题): 为《异派标准》第二十四章(协程协定)写一份草案大纲。


7.9 沧澜碑·后之览者(增补1题)

题130(纪念题):

cpp
template<typename T>
void YeWuHen::daoTong(T&& future) {
    static_assert(是否后辈_v<T>, "道统需后人来继");
    future.覆盖();
}

问题:这段代码体现了C++哪些精神?

答案:泛型、完美转发、静态断言、面向未来。道统非一人之业。


7.10 智能指针循环引用·阴阳锁魂(新增2题)

题131(简答题): 解释shared_ptr循环引用问题及weak_ptr解法。


题132(编程题): 构造一个父子双向引用的循环引用示例,并用weak_ptr破除。


7.11 CRTP·奇异递归模板(新增2题)

题133(填空题): CRTP全称______,典型形式:class Derived : public Base<Derived>。

答案:Curiously Recurring Template Pattern。


题134(编程题): 用CRTP实现静态多态,基类模板提供接口,派生类实现。


7.12 移动语义与完美转发·终极奥义(新增2题)

题135(改错题):

cpp
template<typename T>
void wrapper(T arg) {
    foo(arg);  // 无法转发右值属性
}

改正:使用万能引用+完美转发:template<typename T> void wrapper(T&& arg) { foo(std::forward<T>(arg)); }


题136(编程题): 实现一个emplace_back风格的函数,向vector中完美转发参数构造对象。


7.13 类型萃取·识灵真诀(新增2题)

题137(编程题): 用std::is_same和std::enable_if实现函数重载:若T为整型则整除2,若为浮点则减半。


题138(写出输出):

cpp
std::cout << std::is_same<int, int32_t>::value;

答案:1(true)。


7.14 constexpr与编译期计算·先天演卦(新增2题)

题139(编程题): 用constexpr函数计算字符串长度(编译期)。


题140(编程题): 用constexpr函数判断一个数是否为质数(编译期)。


7.15 C++20 Ranges·阵纹流转(新增2题)

题141(编程题): 使用C++20 Ranges,从vector<int>中筛选出偶数,并取其平方,然后输出。


题142(选择题): C++20 Ranges中,views::filter返回的是: A. 新容器 B. 视图(不拥有数据) C. 迭代器 D. 范围适配器

答案:B。


7.16 模块(Modules)·藏经新阁(新增1题)

题143(填空题): C++20模块导出关键______,导入关键______。

答案:export、import。


7.17 协程实战·元神分化(新增1题)

题144(编程题): 编写一个简单的生成器协程,yield返回整数序列0~9。


尾章·叶沧澜飞升批注

叶沧澜(立于混沌裂隙边,回望千鹤派):

“一百四十四关,天罡地煞尽破。小娃娃,你的指尖已浸透C++的剑气。

但为师须提醒你:习题集是渡海之筏,非彼岸之陆。

真正的修行,在真实项目的千沟万壑中,在生产环境的万丈灵压下,在与编译器斗智斗勇的每一个深夜。

代码无飞升,代码只在需要它的人手中。

去吧,莫要辜负这部题库,更莫要辜负你自己的道心。”

叶小凡(叩首):“弟子谨记!”


【全书终·道心长存】


编著附记

本书习题源自千鹤派藏经阁三千年历代弟子考核真题,经叶沧澜剑尊、叶无痕大乘、秦越守拙、林涛试剑、凌霜冰鉴,最终由叶小凡汇编成册。

凡一百四十四题,每题皆以血泪debug换来,愿后人惜之。

千鹤历四千八百二十四年·叶小凡·元婴大圆满·恭录

杨逸飞 谨识


【《C++百炼成仙·修行题库·完整版》·终】

(总题数:144题)