C++启蒙课程:第15章  友元、异常和其他

我们将掌握C++的高阶生存技能:`friend` 高级协作、`try-catch` 紧急警报系统,以及 `RTTI` 身份扫描仪!

🎟️

高级协作:友元与嵌套

学习“VIP信使”`friend class`和“套房设计”`nested class`。

🚨

应急响应系统:异常

掌握 `try-throw-catch` 紧急警报,和“自动清理机器人”栈解退。

🕵️

身份侦查与转换

使用 `dynamic_cast` 和 `typeid` 运行时“侦探扫描仪”来识别对象。

课时一:高级协作:共同友元与嵌套类

(覆盖知识点 15.1.4, 15.2)

15.1.4 “VIP信使” (友元类)

`friend class` 就像给 `Guest` 团队颁发了VIP通行证,允许 `Guest` 的所有成员访问 `Host` 的 `private` 私人数据区。

Host (Private)
"Treasure Map"
Guest (Public)
class Host { private: string secret = "Map"; public: // 15.1.4 授予 Guest 团队 VIP 通行证 friend class Guest; }; class Guest { public: void read(const Host& h) { // OK! Guest 是友元 cout << h.secret; } };

15.2 “类的套房” (嵌套类)

嵌套类就像一个“秘密小房间”,定义在外部类内部。它的作用域被限制在外部类,外界无法直接访问。

class Outer
class Inner (private)
class Outer { private: // 15.2 嵌套类, 作用域在 Outer 内 class Inner { public: int x; }; public: void func() { Inner i; // OK! Outer 可以访问 i.x = 10; } }; // main() { // Inner i; // 错误!'Inner' is private // }

课时二:程序应急响应系统:异常

(覆盖知识点 15.3)

15.3.3 “紧急警报” (try-catch)

`try` 是“安全监控区”。如果内部发生 `throw`(抛出警报 🚨),程序会立即跳到 `catch`(安全网)进行处理。

Try Block (监控区)
🚨
Catch Block (安全网)
try { if (size < 0) { // 1. 抛出警报 throw "Error: Negative size!"; } // ... 正常代码 ... } catch (const char* msg) { // 2. 捕获警报 cout << msg; }

15.3.6 “自动清理机器人”

当 `throw` 发生时,程序会“栈解退”:沿着函数调用链回退,并自动调用析构函数,清理所有局部对象(自动清理)。

main()
func_A()
func_B()
void func_B() { throw "Error!"; } void func_A() { MyObject obj_A; // 占内存 func_B(); // ~MyObject() 会被自动调用 } int main() { try { func_A(); } catch(...) { ... } }

课时三:身份侦查与强制转换安全

(覆盖知识点 15.4, 15.5)

15.4 “身份侦查扫描仪” (RTTI)

RTTI (运行时类型识别) 允许你在运行时检查一个基类指针的“真实身份”。dynamic_cast 是安全扫描仪:如果身份不符,它返回 `nullptr`。

Base* ptr = ?
...
// Base 必须有虚函数才能用 RTTI class Base { virtual void f(){} }; class Derived : public Base {}; void check(Base* b_ptr) { // 15.4.2 安全的向下转换 Derived* d_ptr = dynamic_cast(b_ptr); if (d_ptr) { // 转换成功 cout << "It's a Derived!"; } else { // 转换失败 cout << "It's just a Base."; } }

15.5 “安全验证门” (Casts)

C++ 提供了更安全的“验证门” (如 `static_cast`, `dynamic_cast`) 来替代 C 语言的“万能钥匙” (Type) 强制转换。

C-Style (危险): (int) 3.14
C++ (安全): static_cast(3.14)
C++ (运行时安全): dynamic_cast
// C-Style (不推荐) int i = (int) my_double; Hero* h = (Hero*) my_base_ptr; // 危险! // 15.5 C++ 风格 (推荐) // 编译时转换 int i = static_cast(my_double); // 运行时安全转换 (RTTI) Hero* h = dynamic_cast(my_base_ptr);

编程实践与作业

是时候检验你作为“高级生存专家”的实力了!

练习 1:友元类 (15.1.4)

任务:

设计 HostGuest 类。让 Guest 成为 Host 的友元类,并演示 Guest 的成员函数如何访问 Host 的私有数据。

点击查看参考答案
#include <iostream> #include <string> using namespace std; class Host { private: string secret_data = "Private Treasure Map"; public: // 15.1.4 声明 Guest 为友元类 friend class Guest; }; class Guest { public: void read_host_secret(const Host & h) const { // Guest 是友元, 可以访问私有成员 cout << "Guest 访问到: " << h.secret_data << endl; } };
练习 2:异常处理 (15.3.3)

任务:

编写一个程序,要求用户输入一个正数作为数组大小。如果用户输入负数,则抛出 int 类型异常。使用 `try-catch` 块捕获并处理该异常。

点击查看参考答案
#include <iostream> using namespace std; void check_size(int size) { if (size < 0) { // 15.3.3 抛出异常 throw size; } cout << "数组大小有效: " << size << endl; } int main() { int n; cout << "请输入数组大小: "; cin >> n; try { check_size(n); } catch (int bad_size) { // 捕获 int 异常 cout << "捕获到异常!大小 " << bad_size << " 无效。" << endl; } cout << "程序运行结束。" << endl; return 0; }
练习 3:RTTI 身份侦查 (15.4.2)

任务:

编写 Base 类和 Derived 类 (确保有虚函数)。使用 dynamic_cast 尝试将 Base* 转换为 Derived*,以判断指针的真实类型。

点击查看参考答案
#include <iostream> #include <typeinfo> using namespace std; class Base { virtual void dummy() {} }; // 必须有虚函数 class Derived : public Base {}; void check_type(Base * b_ptr) { // 15.4.2 使用 dynamic_cast Derived * d_ptr = dynamic_cast(b_ptr); if (d_ptr) { // 转换成功 cout << "扫描成功!对象是 Derived 类型。" << endl; } else { // 转换失败 cout << "扫描失败。对象不是 Derived 类型。" << endl; } } int main() { Derived d_obj; Base b_obj; check_type(&d_obj); // 成功 check_type(&b_obj); // 失败 return 0; }
练习 4:自定义异常类 (15.7 复习题 5)

任务:

假设 Exc 是一个继承自 std::exception 的异常类。编写一个函数 goes_wrong() 抛出该异常,并在 `main` 中捕获它。

点击查看参考答案
#include <iostream> #include <exception> // 15.3.8 异常基类 using namespace std; // 15.3.9 自定义派生异常类 class Exc : public exception { public: const char* what() const noexcept override { return "Custom Exc: Something went wrong!"; } }; void goes_wrong() { throw Exc(); // 15.3.4 抛出对象 } int main() { try { goes_wrong(); } // 15.3.9 捕获基类引用 catch (const exception & e) { cout << "捕获到异常!信息: " << e.what() << endl; } return 0; }

本章知识点总结与复习

核心概念 解释/功能 关键用法/运算符
友元类授予一个类访问另一个类私有数据的权限。friend class ClassB;
嵌套类在一个类内部定义的类,作用域被限制在外部类。class Outer { class Inner {}; };
异常 (Exception)C++的错误处理机制。try, throw, catch
异常对象可抛出类对象(如 exception 派生类)以传递错误信息。throw MyError();
栈解退抛出异常时,自动释放栈内存并调用析构函数。(自动发生)
RTTI运行阶段类型识别,在运行时确定对象类型。dynamic_cast, typeid
`dynamic_cast`安全的向下转换。如果失败,返回 `nullptr` (指针)。dynamic_cast(b_ptr)
`static_cast`C++ 风格的编译时类型转换。static_cast(my_double)