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::vector<int> 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_unique<Resource>("智能指针资源1");
auto p2 = make_unique<Resource>("智能指针资源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,使用移动(高效)
// 否则使用拷贝(安全但低效)
vector<MyClass> 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;
}
}
}
// ===== 内联命名空间(C++11)=====
// 内联命名空间的成员可以直接被外层命名空间访问
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::map<std::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.getAs<int>("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::map<std::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::map<std::string, std::shared_ptr<IService>> services_;
public:
template <typename T, typename... Args>
std::shared_ptr<T> registerService(Args&&... args)
{
auto service = std::make_shared<T>(std::forward<Args>(args)...);
services_[service->name()] = service;
return service;
}
template <typename T>
std::shared_ptr<T> getService(const std::string& name) const
{
auto it = services_.find(name);
if (it == services_.end())
throw ServiceException(name);
return std::dynamic_pointer_cast<T>(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.getAs<int>("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.registerService<DatabaseService>(cfg);
auto cache = container.registerService<CacheService>();
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保证资源释放 |
| 异常安全 |
基本保证(有效状态)、强保证(状态不变)、不抛出保证(noexcept) |
| noexcept |
承诺不抛出,析构函数和移动操作应标记noexcept |
| 异常类层次 |
继承 std::exception,重写 what(),按从具体到抽象的顺序catch |
| 命名空间 |
解决命名冲突,namespace name { } 定义,:: 访问 |
| 开放命名空间 |
命名空间可以分多次定义,成员累积 |
| 内联命名空间 |
C++11,成员可直接被外层命名空间访问,用于版本管理 |
| 匿名命名空间 |
替代 static,限制文件内可见性 |
| using 声明 |
引入单个名字,冲突时报错 |
| using 指示 |
引入整个命名空间,可能导致二义性 |
| ADL(实参依赖查找) |
在实参类型所在命名空间中查找函数 |
| 多重继承 |
继承多个基类,构造顺序按声明顺序,析构逆序 |
| 虚继承 |
解决菱形继承,虚基类只有一份,最终派生类负责初始化 |
| 虚继承构造顺序 |
先虚基类,再非虚基类(按声明顺序),最后派生类 |