
C Primer 第18章用于大型程序的工具18.1 异常处理18.1.1 抛出异常// throw_exceptions.cpp -- 抛出异常 #include iostream #include stdexcept #include string // 异常对象在 throw 时被拷贝 // 异常对象的类型决定哪个 catch 子句被选中 class MyException : public std::runtime_error { public: MyException(const std::string msg, int code) : std::runtime_error(msg), errorCode_(code) {} int code() const { return errorCode_; } private: int errorCode_; }; void level3() { // throw 表达式创建异常对象并抛出 throw MyException(level3发生错误, 404); } void level2() { try { level3(); } catch (MyException e) { std::cout level2捕获并重新抛出 e.what() std::endl; throw; // 重新抛出当前异常保持原始类型 // throw e; // ⚠️ 这会切割异常对象 } } int main() { using namespace std; // 基本异常处理 try { level2(); } catch (const MyException e) { cout main捕获 e.what() 错误码 e.code() endl; } catch (const runtime_error e) { cout runtime_error e.what() endl; } catch (const exception e) { cout exception e.what() endl; } catch (...) { cout 未知异常 endl; } // 异常与析构函数 // 析构函数不应该抛出异常 // 如果析构函数抛出异常且此时已有异常在传播程序会调用 terminate() return 0; }18.1.2 栈展开与异常安全// stack_unwinding.cpp -- 栈展开 #include iostream #include memory #include string class Resource { public: Resource(const std::string name) : name_(name) { std::cout [获取] name_ std::endl; } ~Resource() { std::cout [释放] name_ std::endl; } private: std::string name_; }; // 异常安全的三个级别 // 1. 基本保证异常后对象处于有效状态可能改变 // 2. 强保证异常后对象状态不变事务性 // 3. 不抛出保证操作不会抛出异常noexcept class SafeVector { private: std::vectorint data_; public: // 强保证使用拷贝并交换 void push_back_safe(int val) { auto temp data_; // 拷贝 temp.push_back(val); // 可能抛出 data_.swap(temp); // noexcept不会失败 } // 基本保证可能部分完成 void push_back_basic(int val) { data_.push_back(val); // 可能抛出 } }; void demonstrateUnwinding() { Resource r1(资源A); { Resource r2(资源B); throw std::runtime_error(测试异常); // r2 在这里自动析构栈展开 } // r1 在这里自动析构栈展开 } int main() { using namespace std; cout 栈展开演示 endl; try { demonstrateUnwinding(); } catch (const exception e) { cout 捕获 e.what() endl; } // RAII 保证异常安全 cout \n RAII 异常安全 endl; try { auto p1 make_uniqueResource(智能指针资源1); auto p2 make_uniqueResource(智能指针资源2); throw runtime_error(RAII测试); // p1, p2 自动释放无内存泄漏 } catch (const exception e) { cout 捕获 e.what() endl; } return 0; }18.1.3 noexcept 异常说明// noexcept_demo.cpp -- noexcept #include iostream #include vector #include stdexcept // noexcept承诺函数不抛出异常 // 如果抛出程序调用 std::terminate() // 析构函数默认是 noexcept class MyClass { public: ~MyClass() noexcept // 析构函数应该是 noexcept { // 不应该在析构函数中抛出异常 } // 移动操作应该是 noexcept让容器使用移动而非拷贝 MyClass(MyClass) noexcept default; MyClass operator(MyClass) noexcept default; }; // noexcept 说明符 void safeFunc() noexcept { // 不会抛出异常 } // noexcept 运算符检查表达式是否会抛出 template typename T void swap(T a, T b) noexcept(noexcept(T(std::move(a)))) { T temp std::move(a); a std::move(b); b std::move(temp); } int main() { using namespace std; // 检查函数是否 noexcept cout boolalpha; cout safeFunc是否noexcept noexcept(safeFunc()) endl; // vector 在扩容时 // 如果移动构造是 noexcept使用移动高效 // 否则使用拷贝安全但低效 vectorMyClass v; v.push_back(MyClass{}); // 使用移动因为移动构造是noexcept return 0; }18.1.4 异常类层次结构// exception_hierarchy.cpp -- 异常类层次 #include iostream #include stdexcept #include string /* std::exception ├── std::bad_alloc new 失败 ├── std::bad_cast dynamic_cast 失败 ├── std::bad_typeid typeid 失败 ├── std::bad_exception └── std::logic_error 逻辑错误可预防 ├── std::domain_error 定义域错误 ├── std::invalid_argument 无效参数 ├── std::length_error 长度错误 └── std::out_of_range 越界 └── std::runtime_error 运行时错误难以预防 ├── std::overflow_error 上溢 ├── std::underflow_error 下溢 └── std::range_error 范围错误 */ // 自定义异常层次 class AppException : public std::exception { protected: std::string message_; int code_; public: AppException(const std::string msg, int code) : message_(msg), code_(code) {} const char* what() const noexcept override { return message_.c_str(); } int code() const { return code_; } }; class DatabaseException : public AppException { public: DatabaseException(const std::string msg) : AppException(数据库错误: msg, 1001) {} }; class NetworkException : public AppException { public: NetworkException(const std::string msg) : AppException(网络错误: msg, 2001) {} }; class ValidationException : public AppException { private: std::string field_; public: ValidationException(const std::string field, const std::string msg) : AppException(验证错误[ field ]: msg, 3001), field_(field) {} const std::string field() const { return field_; } }; void processRequest(int type) { switch (type) { case 1: throw DatabaseException(连接超时); case 2: throw NetworkException(DNS解析失败); case 3: throw ValidationException(email, 格式无效); default: throw AppException(未知错误, 9999); } } int main() { using namespace std; for (int i 1; i 4; i) { try { processRequest(i); } catch (const ValidationException e) { cout [验证] e.what() 字段 e.field() endl; } catch (const DatabaseException e) { cout [数据库] e.what() 代码 e.code() endl; } catch (const NetworkException e) { cout [网络] e.what() endl; } catch (const AppException e) { cout [应用] e.what() 代码 e.code() endl; } } return 0; }18.2 命名空间18.2.1 命名空间基础// namespace_basics.cpp -- 命名空间基础 #include iostream #include string // 定义命名空间 namespace cplusplus_primer { class Sales_data { /* ... */ }; Sales_data operator(const Sales_data, const Sales_data); class Query { /* ... */ }; class Query_base { /* ... */ }; } // 命名空间可以不连续开放性 namespace cplusplus_primer { // 继续添加成员 class QueryResult { /* ... */ }; } // 嵌套命名空间 namespace outer { int x 1; namespace inner { int x 2; // 与 outer::x 不冲突 void show() { std::cout inner::x x std::endl; std::cout outer::x outer::x std::endl; } } } // 内联命名空间C11 // 内联命名空间的成员可以直接被外层命名空间访问 namespace FifthEd { inline namespace FifthEdV1 { void func() { std::cout FifthEdV1::func std::endl; } } namespace FifthEdV2 { void func() { std::cout FifthEdV2::func std::endl; } } } // 匿名命名空间 // 等价于 static只在本文件可见 namespace { int localVar 42; // 只在本文件可见 void localFunc() { std::cout 局部函数 std::endl; } } int main() { using namespace std; // 使用嵌套命名空间 outer::inner::show(); // 内联命名空间 FifthEd::func(); // 调用 FifthEdV1::func内联 FifthEd::FifthEdV1::func(); // 显式指定 FifthEd::FifthEdV2::func(); // 显式指定 // 匿名命名空间 cout localVar endl; localFunc(); return 0; }18.2.2 using 声明和 using 指示// using_demo.cpp -- using声明和指示 #include iostream #include string namespace Lib1 { int x 1; void func() { std::cout Lib1::func std::endl; } class MyClass { public: void show() { std::cout Lib1::MyClass std::endl; } }; } namespace Lib2 { int x 2; void func() { std::cout Lib2::func std::endl; } } void demoUsingDeclaration() { // using 声明引入单个名字 using Lib1::func; using Lib1::MyClass; func(); // Lib1::func MyClass obj; obj.show(); // using Lib2::func; // ❌ 错误func已经被Lib1::func占用 } void demoUsingDirective() { // using 指示引入整个命名空间 using namespace Lib1; using namespace Lib2; // x; // ❌ 二义性Lib1::x 还是 Lib2::x Lib1::x; // 必须显式指定 Lib2::x; // func(); // ❌ 二义性 Lib1::func(); Lib2::func(); } // 命名空间与函数重载 namespace NS { void print(int i) { std::cout NS::print(int) i std::endl; } void print(double d) { std::cout NS::print(double) d std::endl; } } void print(std::string s) { std::cout ::print(string) s std::endl; } int main() { using namespace std; demoUsingDeclaration(); demoUsingDirective(); // 实参依赖查找ADL/Koenig查找 // 调用函数时编译器会在实参类型所在的命名空间中查找 using NS::print; print(42); // NS::print(int) print(3.14); // NS::print(double) print(string(hello)); // ::print(string) return 0; }18.2.3 命名空间与类// namespace_class.cpp -- 命名空间与类 #include iostream #include string namespace MyLib { // 类定义在命名空间中 class Screen { public: using pos std::string::size_type; Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {} char get() const { return contents[cursor]; } Screen move(pos r, pos c); private: pos cursor 0; pos height 0, width 0; std::string contents; }; // 在命名空间外定义成员函数 Screen Screen::move(pos r, pos c) { cursor r * width c; return *this; } // 友元函数自动成为命名空间的成员 class Window_mgr { public: void clear(Screen s) { s.contents std::string(s.height * s.width, ); } }; } int main() { MyLib::Screen s(5, 5, X); s.move(2, 2); std::cout s.get() std::endl; return 0; }18.3 多重继承与虚继承18.3.1 多重继承// multiple_inheritance.cpp -- 多重继承 #include iostream #include string class ZooAnimal { public: ZooAnimal() { std::cout [ZooAnimal构造] std::endl; } virtual ~ZooAnimal() { std::cout [ZooAnimal析构] std::endl; } virtual std::string name() const { return ZooAnimal; } void breathe() const { std::cout 呼吸 std::endl; } }; class Endangered { public: Endangered() { std::cout [Endangered构造] std::endl; } virtual ~Endangered() { std::cout [Endangered析构] std::endl; } virtual std::string status() const { return 濒危; } void protect() const { std::cout 保护中 std::endl; } }; // 多重继承 class Panda : public ZooAnimal, public Endangered { public: Panda() { std::cout [Panda构造] std::endl; } ~Panda() { std::cout [Panda析构] std::endl; } std::string name() const override { return 大熊猫; } std::string status() const override { return 极度濒危; } void eat() const { std::cout 吃竹子 std::endl; } }; int main() { using namespace std; cout 构造顺序 endl; Panda p; cout \n 使用 endl; p.breathe(); // ZooAnimal 的成员 p.protect(); // Endangered 的成员 p.eat(); // Panda 自己的成员 cout 名字 p.name() endl; cout 状态 p.status() endl; // 多态 ZooAnimal* za p; Endangered* en p; cout \n通过基类指针 endl; cout za-name() endl; // 大熊猫虚函数 cout en-status() endl; // 极度濒危虚函数 cout \n 析构顺序逆序 endl; return 0; }18.3.2 虚继承// virtual_inheritance.cpp -- 虚继承 #include iostream #include string // 菱形继承问题 // Animal // / \ // Tiger Lion // \ / // Liger狮虎兽 // 没有虚继承时Liger 会有两份 Animal 的数据 class Animal { public: Animal(const std::string name) : name_(name) { std::cout [Animal构造] name_ std::endl; } virtual ~Animal() { std::cout [Animal析构] std::endl; } virtual std::string sound() const { return ...; } std::string name() const { return name_; } protected: std::string name_; }; // 虚继承解决菱形继承问题 class Tiger : virtual public Animal { public: Tiger(const std::string name) : Animal(name) { std::cout [Tiger构造] std::endl; } ~Tiger() { std::cout [Tiger析构] std::endl; } std::string sound() const override { return 吼; } void hunt() const { std::cout 老虎在狩猎 std::endl; } }; class Lion : virtual public Animal { public: Lion(const std::string name) : Animal(name) { std::cout [Lion构造] std::endl; } ~Lion() { std::cout [Lion析构] std::endl; } std::string sound() const override { return 咆哮; } void roar() const { std::cout 狮子在咆哮 std::endl; } }; // 最终派生类必须显式初始化虚基类 class Liger : public Tiger, public Lion { public: // 必须显式调用 Animal 的构造函数 Liger(const std::string name) : Animal(name), // 虚基类构造函数 Tiger(name), Lion(name) { std::cout [Liger构造] std::endl; } ~Liger() { std::cout [Liger析构] std::endl; } // 必须重写 sound()否则二义性 std::string sound() const override { return 特殊叫声; } }; int main() { using namespace std; cout 虚继承 endl; Liger liger(狮虎兽); cout \n 使用 endl; cout 名字 liger.name() endl; // 只有一份 name_ cout 叫声 liger.sound() endl; liger.hunt(); liger.roar(); // 只有一份 Animal 数据 Animal* ap liger; cout 通过Animal* ap-name() endl; cout \n 析构 endl; return 0; }18.3.3 虚继承中的构造顺序// virtual_inheritance_order.cpp -- 虚继承构造顺序 #include iostream class Base { public: Base(int v) : val(v) { std::cout Base( v ) std::endl; } int val; }; class D1 : virtual public Base { public: D1(int v) : Base(v) { std::cout D1( v ) std::endl; } }; class D2 : virtual public Base { public: D2(int v) : Base(v) { std::cout D2( v ) std::endl; } }; class Final : public D1, public D2 { public: // 虚基类由最终派生类初始化 // D1 和 D2 中的 Base(v) 被忽略 Final(int v1, int v2, int vb) : Base(vb), // 虚基类 D1(v1), D2(v2) { std::cout Final std::endl; } }; int main() { // 构造顺序 // 1. 虚基类Base // 2. 非虚基类D1, D2按声明顺序 // 3. 最终派生类Final Final f(1, 2, 100); std::cout val f.val std::endl; // 100虚基类的值 return 0; }18.4 综合示例大型程序框架// large_program_framework.cpp -- 综合示例 #include iostream #include string #include memory #include vector #include map #include functional #include stdexcept #include sstream // 命名空间组织代码 namespace Framework { // 异常层次 class FrameworkException : public std::exception { protected: std::string message_; int code_; public: FrameworkException(const std::string msg, int code) : message_(msg), code_(code) {} const char* what() const noexcept override { return message_.c_str(); } int code() const { return code_; } }; class ConfigException : public FrameworkException { public: ConfigException(const std::string key) : FrameworkException(配置项不存在: key, 1001) {} }; class ServiceException : public FrameworkException { public: ServiceException(const std::string service) : FrameworkException(服务不可用: service, 2001) {} }; // 配置管理 class Config { private: std::mapstd::string, std::string data_; public: void set(const std::string key, const std::string val) { data_[key] val; } const std::string get(const std::string key) const { auto it data_.find(key); if (it data_.end()) throw ConfigException(key); return it-second; } template typename T T getAs(const std::string key) const { const std::string val get(key); std::istringstream iss(val); T result; if (!(iss result)) throw FrameworkException(类型转换失败: key, 1002); return result; } bool has(const std::string key) const { return data_.count(key) 0; } }; // 服务接口抽象基类 class IService { public: virtual ~IService() default; virtual void start() 0; virtual void stop() 0; virtual bool isRunning() const 0; virtual std::string name() const 0; }; // 具体服务 class DatabaseService : public IService { private: bool running_ false; std::string host_; int port_; public: DatabaseService(const Config cfg) { host_ cfg.get(db.host); port_ cfg.getAsint(db.port); } void start() override { std::cout [DB] 连接到 host_ : port_ std::endl; running_ true; } void stop() override { std::cout [DB] 断开连接 std::endl; running_ false; } bool isRunning() const override { return running_; } std::string name() const override { return DatabaseService; } void query(const std::string sql) const { if (!running_) throw ServiceException(name()); std::cout [DB] 执行 sql std::endl; } }; class CacheService : public IService { private: bool running_ false; std::mapstd::string, std::string cache_; public: void start() override { std::cout [Cache] 启动缓存服务 std::endl; running_ true; } void stop() override { std::cout [Cache] 停止缓存服务 std::endl; cache_.clear(); running_ false; } bool isRunning() const override { return running_; } std::string name() const override { return CacheService; } void set(const std::string key, const std::string val) { if (!running_) throw ServiceException(name()); cache_[key] val; } std::string get(const std::string key) const { if (!running_) throw ServiceException(name()); auto it cache_.find(key); if (it cache_.end()) throw FrameworkException(缓存未命中: key, 3001); return it-second; } }; // 服务容器 class ServiceContainer { private: std::mapstd::string, std::shared_ptrIService services_; public: template typename T, typename... Args std::shared_ptrT registerService(Args... args) { auto service std::make_sharedT(std::forwardArgs(args)...); services_[service-name()] service; return service; } template typename T std::shared_ptrT getService(const std::string name) const { auto it services_.find(name); if (it services_.end()) throw ServiceException(name); return std::dynamic_pointer_castT(it-second); } void startAll() { for (auto [name, service] : services_) { try { service-start(); std::cout name 启动成功 std::endl; } catch (const std::exception e) { std::cerr name 启动失败 e.what() std::endl; } } } void stopAll() { for (auto [name, service] : services_) { try { if (service-isRunning()) service-stop(); } catch (const std::exception e) { std::cerr name 停止失败 e.what() std::endl; } } } }; } // namespace Framework int main() { using namespace std; using namespace Framework; // 配置 Config cfg; cfg.set(db.host, localhost); cfg.set(db.port, 5432); cfg.set(app.name, MyApp); cout 配置读取 endl; try { cout db.host cfg.get(db.host) endl; cout db.port cfg.getAsint(db.port) endl; cout cfg.get(missing.key) endl; // 抛出异常 } catch (const ConfigException e) { cout 配置错误 e.what() endl; } // 服务容器 cout \n 服务启动 endl; ServiceContainer container; auto db container.registerServiceDatabaseService(cfg); auto cache container.registerServiceCacheService(); container.startAll(); // 使用服务 cout \n 使用服务 endl; try { db-query(SELECT * FROM users); cache-set(user:1, Alice); cout 缓存 cache-get(user:1) endl; cout 缓存 cache-get(user:999) endl; // 未命中 } catch (const ServiceException e) { cout 服务错误 e.what() endl; } catch (const FrameworkException e) { cout 框架错误[ e.code() ] e.what() endl; } // 停止服务 cout \n 服务停止 endl; container.stopAll(); return 0; } 第18章知识点总结知识点核心要点异常抛出throw创建异常对象throw;重新抛出保持原始类型栈展开异常传播时自动调用局部对象析构函数RAII保证资源释放异常安全基本保证有效状态、强保证状态不变、不抛出保证noexceptnoexcept承诺不抛出析构函数和移动操作应标记noexcept异常类层次继承std::exception重写what()按从具体到抽象的顺序catch命名空间解决命名冲突namespace name { }定义::访问开放命名空间命名空间可以分多次定义成员累积内联命名空间C11成员可直接被外层命名空间访问用于版本管理匿名命名空间替代static限制文件内可见性using 声明引入单个名字冲突时报错using 指示引入整个命名空间可能导致二义性ADL实参依赖查找在实参类型所在命名空间中查找函数多重继承继承多个基类构造顺序按声明顺序析构逆序虚继承解决菱形继承虚基类只有一份最终派生类负责初始化虚继承构造顺序先虚基类再非虚基类按声明顺序最后派生类