C++启蒙课程:第18章  探讨C++新标准

欢迎来到“未来代码实验室”!我们将探索C++11及以后的最新魔法,包括 `auto`, `std::move`, `lambda` 和可变参数模板!

🕵️

未来代码基础

掌握“智能侦探” `auto` 和“万能礼盒” `{} ` 统一初始化。

⚡️

速度魔法:移动语义

学习“快速资源转移机器人” `std::move` 如何通过“偷走”资源来提速。

🎩

快捷函数与通用指令

掌握“快捷函数草图” `lambda` 和“魔术师的参数礼帽” `...Args`。

课时一:未来代码基础:新类型与智能声明

(覆盖知识点 18.1)

18.1.2 “万能礼盒” {}

C++11 使用大括号 {} 作为“万能礼盒”,可以统一初始化所有类型,并且更安全(禁止缩窄转换)。

{ ... }
// 18.1.2 统一初始化 (列表初始化) int x {5}; double y {3.14}; // 禁止缩窄转换 // int z {5.5}; // 编译错误! vector v {1, 2, 3, 4, 5};

18.1.3 “智能侦探” auto

`auto` 侦探 🕵️ 会自动扫描右侧的初始值,并推断出变量应有的正确类型,极大简化代码。

// 18.1.3 auto 自动推断 auto sum = 3.0 + 5.5; // sum 是 double auto name = "C++"; // name 是 const char* // 18.1.3 decltype 获取类型 decltype(sum) final_sum; // final_sum 是 double

课时二:速度魔法:右值引用与移动语义

(覆盖知识点 18.1.9, 18.2)

18.1.9 右值引用 (&&)

左值 (x) 是“有名字的家” 🏠。右值 (10) 是“临时礼物” 🎁。常规引用 (&) 只能绑定到“家”,右值引用 (&&) 专门用来捕获“临时礼物”。

🏠 (int x) 🎁 (10)
int x = 5; // 18.1.9 左值引用 (Lvalue) int& l_ref = x; // OK // int& r1 = 10; // 错误! // 18.1.9 右值引用 (Rvalue) int&& r_ref = 10; // OK // int&& r2 = x; // 错误!

18.2.3 “快速资源转移” (Move)

复制 (慢): 克隆一份新资源。移动 (快): 机器人 🤖 直接“偷走”临时对象的资源(指针),并把临时对象的指针设为 nullptr

Source (Temp)
Target
Heap Resource
// 18.2.3 移动构造函数 MyResource(MyResource&& source) { // 1. 偷走资源 (转移指针) data = source.data; // 2. 将源对象置空 source.data = nullptr; } // 18.2.5 强制移动 MyResource r2 = std::move(r1);

课时三:类工厂升级与快捷函数

(覆盖知识点 18.3, 18.4, 18.5)

18.3.2 “成员函数开关”

C++11 允许你使用 `default` (开启) 和 `delete` (关闭) 关键字,来精确控制编译器是否应自动生成特殊函数(如复制构造函数)。

class MyClass { public: // 18.3.2 开启: 告诉编译器 // "请帮我生成默认构造函数" MyClass() = default; // 18.3.2 关闭: 告诉编译器 // "禁止任何人复制这个对象" MyClass(const MyClass&) = delete; };

18.3.5 “方法护栏”

`override` 护栏 🚧 确保你“真的”重写了基类虚函数(拼写错误会报错)。`final` 护栏 🛑 阻止其他人“继续”继承或重写。

class Base { virtual void func() const; }; // 18.3.5 final 阻止 B 被继承 class B final : public Base { // 18.3.5 override 检查拼写 void func() const override; // void fune() const override; // 错误! };

18.4 “快捷函数草图” (Lambda)

Lambda 表达式 [](){} 是一种“快捷草图”,让你可以在需要的地方快速定义一个匿名函数,常用于 STL 算法。

vector v = {1, 2, 3};
#include <algorithm> vector v = {1, 2, 3}; // 18.4.1 Lambda 表达式 // [] 是捕获列表, (int n) 是参数 for_each(v.begin(), v.end(), [](int n) { cout << n * 2 << endl; } );

课时四:通用指令:可变参数模板与未来

(覆盖知识点 18.6 - 18.9)

18.6 “魔术师的参数礼帽”

可变参数模板 (...Args) 就像一个“魔术礼帽” 🎩,允许你定义一个能接受“任意数量、任意类型”参数的函数。

🎩
// 18.6.3 递归终止 void print_args() {} // 18.6.1 可变参数模板 template void print_args(T head, Args... rest) { cout << head << endl; // 18.6.3 递归解包 print_args(rest...); } // 调用: // print_args(10, 3.14, "Hello");

编程实践与作业

是时候检验你作为“未来代码大师”的实力了!

练习 1:智能声明与统一初始化 (18.1)

任务:

使用 auto 关键字声明一个 `double` 变量(通过计算),然后使用 decltype 关键字声明一个相同类型的新变量,并使用列表初始化 {} 进行赋值。

点击查看参考答案
#include <iostream> using namespace std; int main() { // 18.1.3 auto 自动推断为 double auto result = 100.5 / 2.0; // 18.1.3 decltype 获取 result 的类型 decltype(result) final_score {0.0}; // 18.1.2 列表初始化 final_score = result + 5.0; cout << "Final Score: " << final_score << endl; return 0; }
练习 2:移动构造函数 (18.2.3)

任务:

MyResource 类(使用动态内存 int* data)定义一个**移动构造函数**,并演示如何使用 std::move 强制进行移动操作。

点击查看参考答案
#include <iostream> #include <utility> // for std::move using namespace std; class MyResource { int * data; public: MyResource(int val=0) { data = new int(val); } ~MyResource() { delete data; } // 18.2.3 移动构造函数 MyResource(MyResource && source) noexcept { cout << "移动构造函数: 资源转移!" << endl; data = source.data; // 1. 偷走资源 source.data = nullptr; // 2. 源对象置空 } // (省略复制构造和赋值) }; int main() { MyResource r1(10); // 18.2.5 强制将 r1 视为右值 MyResource r2 = std::move(r1); // r1 此时已失效 return 0; }
练习 3:Lambda 与 override (18.3, 18.4)

任务:

部分A: 编写一个使用 override 关键字的虚函数重写。
部分B: 使用 Lambda 表达式对 vector 进行遍历并打印。

点击查看参考答案
#include <iostream> #include <vector> #include <algorithm> using namespace std; class Base { public: virtual void print() const { cout << "Base" << endl; } }; class Derived : public Base { public: // 18.3.5 override 确保正确重写 void print() const override { cout << "Derived" << endl; } }; int main() { vector numbers = {1, 2, 3}; // 18.4 Lambda 表达式 for_each(numbers.begin(), numbers.end(), [](int n) { cout << "Element: " << n << endl; } ); return 0; }
练习 4:可变参数模板 (18.6)

任务:

编写一个可变参数模板函数 print_args(),用于接收并打印任意数量的参数。使用递归展开来处理参数包。

点击查看参考答案
#include <iostream> using namespace std; // 18.6.3 递归的终止条件 void print_args() { // 参数包为空时调用 } // 18.6.1 可变参数模板 template void print_args(T head, Args... rest) { cout << head << endl; // 处理第一个 print_args(rest...); // 递归处理剩余的 } int main() { print_args(10, "Hello", 3.14); return 0; }

本章知识点总结与复习

核心概念 解释/功能 关键用法/示例
统一初始化使用大括号 {} 初始化所有类型,更安全。int x {5};
auto/decltype自动类型推断,简化声明。auto result = func();
右值引用 &&只能绑定到临时值(右值),实现移动语义。int&& r = 10;
移动语义转移资源(指针)所有权而非深复制。Class(Class && s);
default/delete控制编译器对特殊成员函数的生成或禁用。MyClass() = default;
override/final增强虚函数的安全管理。void func() override final;
Lambda 函数简洁的匿名函数对象。[](int x){ ... }
可变参数模板允许函数或类模板接受任意数量的参数。template