第24章 高级异常处理

异常安全保证

异常安全是指当异常发生时,程序能够保持一致的状态,不会泄漏资源,也不会破坏数据结构。C++中,异常安全保证通常分为三个级别:

1. 基本异常安全(Basic Exception Safety)

  • 保证:当异常发生时,程序不会泄漏资源,数据结构保持有效状态,但可能不是原始状态
  • 示例:使用智能指针管理内存,确保即使发生异常也能释放内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <memory>
#include <vector>

void processData(std::vector<int>& data) {
std::vector<int> temp;

// 复制数据到临时向量
for (int value : data) {
temp.push_back(value * 2);
}

// 如果上面的操作抛出异常,data不会被修改
data.swap(temp); // 无异常操作
}

2. 强异常安全(Strong Exception Safety)

  • 保证:当异常发生时,程序状态完全回滚到操作前的状态,就像操作从未发生过一样
  • 示例:使用复制-交换技术(Copy-and-Swap)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <algorithm>

class ResourceManager {
private:
int* data;
size_t size;

public:
ResourceManager(size_t s) : size(s), data(new int[s]()) {}

// 复制构造函数
ResourceManager(const ResourceManager& other)
: size(other.size), data(new int[other.size]) {
std::copy(other.data, other.data + other.size, data);
}

// 析构函数
~ResourceManager() {
delete[] data;
}

// 复制-交换赋值运算符
ResourceManager& operator=(ResourceManager other) {
swap(*this, other);
return *this;
}

// 交换函数
friend void swap(ResourceManager& a, ResourceManager& b) {
std::swap(a.data, b.data);
std::swap(a.size, b.size);
}

// 修改数据的操作(强异常安全)
void resize(size_t newSize) {
ResourceManager temp(newSize);
std::copy(data, data + std::min(size, newSize), temp.data);
swap(*this, temp); // 无异常操作
}
};

3. 无异常保证(No-Throw Guarantee)

  • 保证:操作永远不会抛出异常
  • 示例:使用noexcept说明符标记的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <vector>

// 无异常保证的交换函数
void swapValues(int& a, int& b) noexcept {
int temp = a;
a = b;
b = temp;
}

// 无异常保证的访问函数
template <typename T>
const T& getElement(const std::vector<T>& vec, size_t index) noexcept {
// 假设index已经被验证为有效
return vec[index];
}

异常传播和重新抛出

异常传播

当异常在函数中抛出但未被捕获时,它会沿着调用栈向上传播,直到找到匹配的catch块。这个过程称为栈展开(Stack Unwinding)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void func3() {
throw std::runtime_error("Error in func3");
}

void func2() {
func3(); // 异常传播到这里
}

void func1() {
func2(); // 异常传播到这里
}

int main() {
try {
func1(); // 异常传播到这里
} catch (const std::exception& e) {
std::cout << "Caught exception: " << e.what() << std::endl;
}

return 0;
}

重新抛出异常

有时我们需要捕获异常,做一些处理后,再将异常重新抛出,让上层代码继续处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void processFile(const std::string& filename) {
try {
// 打开文件并处理
std::ifstream file(filename);
if (!file) {
throw std::runtime_error("Failed to open file");
}
// 处理文件...
} catch (const std::exception& e) {
// 记录错误
std::cerr << "Error in processFile: " << e.what() << std::endl;
// 重新抛出异常
throw;
}
}

int main() {
try {
processFile("nonexistent.txt");
} catch (const std::exception& e) {
std::cout << "Main caught exception: " << e.what() << std::endl;
// 执行恢复操作
}

return 0;
}

带修饰的重新抛出

在C++11及以后,我们可以使用std::current_exception()捕获当前异常,然后在其他上下文中重新抛出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <exception>
#include <memory>

void processWithDelay() {
std::exception_ptr eptr;

try {
throw std::runtime_error("Original error");
} catch (...) {
// 捕获异常但不处理
eptr = std::current_exception();
}

// 做一些其他工作
std::cout << "Doing other work..." << std::endl;

// 稍后重新抛出异常
if (eptr) {
std::cout << "Rethrowing exception..." << std::endl;
std::rethrow_exception(eptr);
}
}

int main() {
try {
processWithDelay();
} catch (const std::exception& e) {
std::cout << "Caught exception: " << e.what() << std::endl;
}

return 0;
}

异常与多线程

线程中的异常

在C++中,如果线程函数抛出未捕获的异常,程序会调用std::terminate()终止执行。因此,我们需要在每个线程函数中捕获所有可能的异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <thread>
#include <iostream>
#include <stdexcept>

void threadFunction() {
try {
// 可能抛出异常的代码
throw std::runtime_error("Thread error");
} catch (const std::exception& e) {
std::cerr << "Thread caught exception: " << e.what() << std::endl;
} catch (...) {
std::cerr << "Thread caught unknown exception" << std::endl;
}
}

int main() {
std::thread t(threadFunction);
t.join();

std::cout << "Main thread continues" << std::endl;
return 0;
}

使用std::future传递异常

std::future提供了一种在不同线程之间传递异常的机制。当线程函数抛出异常时,异常会被存储在std::future中,当调用get()时会重新抛出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <future>
#include <iostream>
#include <stdexcept>

int divide(int a, int b) {
if (b == 0) {
throw std::runtime_error("Division by zero");
}
return a / b;
}

int main() {
std::future<int> result = std::async(std::launch::async, divide, 10, 0);

try {
int value = result.get(); // 这里会重新抛出异常
std::cout << "Result: " << value << std::endl;
} catch (const std::exception& e) {
std::cout << "Caught exception: " << e.what() << std::endl;
}

return 0;
}

多线程中的异常安全

在多线程环境中,异常安全变得更加复杂,因为需要考虑线程间的同步和共享数据的一致性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <thread>
#include <mutex>
#include <vector>
#include <memory>

class ThreadSafeQueue {
private:
std::vector<int> data;
std::mutex mtx;

public:
// 强异常安全的添加操作
void push(int value) {
std::lock_guard<std::mutex> lock(mtx);
data.push_back(value);
}

// 基本异常安全的弹出操作
bool pop(int& value) {
std::lock_guard<std::mutex> lock(mtx);
if (data.empty()) {
return false;
}
value = data.back();
data.pop_back();
return true;
}
};

异常与RAII的深度结合

RAII与异常安全

RAII(资源获取即初始化)是实现异常安全的关键技术,它通过对象的构造和析构来管理资源,确保无论是否发生异常,资源都能被正确释放。

自定义RAII包装器

对于需要手动管理的资源,我们可以创建自定义的RAII包装器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 互斥锁的RAII包装器
class LockGuard {
private:
std::mutex& mtx;

public:
explicit LockGuard(std::mutex& mutex) : mtx(mutex) {
mtx.lock();
}

~LockGuard() {
mtx.unlock();
}

// 禁止复制和移动
LockGuard(const LockGuard&) = delete;
LockGuard& operator=(const LockGuard&) = delete;
};

// 使用示例
void threadSafeOperation(std::mutex& mtx, int& sharedData, int value) {
LockGuard lock(mtx); // 自动加锁

// 即使这里抛出异常,析构函数也会自动解锁
sharedData = value;

// 函数结束时自动解锁
}

智能指针与异常

智能指针是RAII的典型应用,它们确保即使发生异常,动态分配的内存也能被正确释放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <memory>

void processData() {
// 使用智能指针管理内存
std::unique_ptr<int> data(new int[1000]);

// 可能抛出异常的操作
// ...

// 即使发生异常,智能指针也会自动释放内存
}

// C++14及以后,推荐使用make_unique
void processDataModern() {
auto data = std::make_unique<int[]>(1000);

// 可能抛出异常的操作
// ...
}

异常处理的设计模式

1. 异常转换器模式

将底层异常转换为更高级、更具语义的异常,同时保留原始异常信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdexcept>
#include <string>

// 底层异常
class DatabaseError : public std::runtime_error {
public:
DatabaseError(const std::string& message, int errorCode)
: std::runtime_error(message), errorCode(errorCode) {}

int getErrorCode() const { return errorCode; }

private:
int errorCode;
};

// 高级异常
class UserServiceError : public std::runtime_error {
public:
UserServiceError(const std::string& message, const std::exception& cause)
: std::runtime_error(message + ". Cause: " + cause.what()) {}
};

// 用户服务
class UserService {
private:
// 模拟数据库操作
void saveToDatabase(const std::string& userData) {
// 模拟数据库错误
throw DatabaseError("Failed to save user", 5001);
}

public:
void createUser(const std::string& userData) {
try {
saveToDatabase(userData);
} catch (const DatabaseError& e) {
// 转换异常
throw UserServiceError("Failed to create user", e);
}
}
};

2. 异常边界模式

在模块或系统边界捕获异常,记录错误信息,然后转换为更适合外部消费者的异常或错误码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 内部异常
class InternalError : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};

// API错误码
enum class ApiErrorCode {
SUCCESS = 0,
INTERNAL_ERROR = 1,
BAD_REQUEST = 2,
NOT_FOUND = 3
};

// API响应
struct ApiResponse {
ApiErrorCode code;
std::string message;
std::string data;
};

// API边界函数
ApiResponse processApiRequest(const std::string& request) {
try {
// 处理请求
// ...
return {ApiErrorCode::SUCCESS, "Success", "{\"result\": \"ok\"}"};
} catch (const InternalError& e) {
// 记录错误
std::cerr << "Internal error: " << e.what() << std::endl;
// 转换为API错误
return {ApiErrorCode::INTERNAL_ERROR, "Internal server error", ""};
} catch (const std::invalid_argument& e) {
// 记录错误
std::cerr << "Bad request: " << e.what() << std::endl;
// 转换为API错误
return {ApiErrorCode::BAD_REQUEST, "Invalid request", ""};
} catch (...) {
// 记录未知错误
std::cerr << "Unknown error" << std::endl;
// 转换为API错误
return {ApiErrorCode::INTERNAL_ERROR, "Internal server error", ""};
}
}

3. 异常恢复模式

对于可恢复的错误,捕获异常后执行恢复操作,然后继续执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <stdexcept>
#include <thread>

void connectToServer(const std::string& server) {
// 模拟连接失败
throw std::runtime_error("Connection failed");
}

void processWithRetry() {
const int maxRetries = 3;
int retryCount = 0;

while (retryCount < maxRetries) {
try {
std::cout << "Attempting to connect..." << std::endl;
connectToServer("example.com");
std::cout << "Connection successful!" << std::endl;
break; // 连接成功,退出循环
} catch (const std::runtime_error& e) {
std::cout << "Error: " << e.what() << std::endl;
retryCount++;

if (retryCount < maxRetries) {
std::cout << "Retrying in 1 second..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
} else {
std::cout << "Max retries reached. Giving up." << std::endl;
throw; // 重新抛出异常
}
}
}
}

实际项目中的异常处理策略

1. 分层异常处理

在大型项目中,通常采用分层的异常处理策略:

  • 底层:抛出具体的、详细的异常
  • 中层:捕获底层异常,转换为更具语义的异常,添加上下文信息
  • 顶层:捕获所有异常,记录错误,执行恢复操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 底层:数据库层
class DatabaseException : public std::runtime_error {
public:
DatabaseException(const std::string& sql, const std::string& error)
: std::runtime_error("Database error: " + error + " (SQL: " + sql + ")") {}
};

// 中层:业务逻辑层
class OrderProcessingException : public std::runtime_error {
public:
OrderProcessingException(const std::string& orderId, const std::exception& cause)
: std::runtime_error("Failed to process order " + orderId + ". Cause: " + cause.what()) {}
};

// 顶层:应用层
void processOrder(const std::string& orderId) {
try {
// 处理订单
// ...
} catch (const OrderProcessingException& e) {
// 记录错误
logError(e.what());
// 通知用户
notifyUser("Order processing failed: " + e.what());
// 执行恢复操作
scheduleRetry(orderId);
} catch (const std::exception& e) {
// 记录未预期的错误
logError("Unexpected error: " + e.what());
// 通知管理员
notifyAdmin("Unexpected error: " + e.what());
// 执行紧急恢复操作
emergencyRecovery();
}
}

2. 异常日志

良好的异常日志对于调试和监控至关重要。异常日志应该包含:

  • 异常类型和消息
  • 异常发生的位置(文件、行号、函数名)
  • 异常发生时的上下文信息(如用户ID、请求参数等)
  • 异常的调用栈
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdexcept>
#include <source_location>

void logException(const std::exception& e, const std::source_location& loc = std::source_location::current()) {
std::cerr << "Exception at " << loc.file_name() << ":" << loc.line()
<< " in " << loc.function_name() << std::endl;
std::cerr << "Error: " << e.what() << std::endl;
// 可以将日志写入文件或发送到日志系统
}

void processData(int value) {
try {
if (value < 0) {
throw std::invalid_argument("Value must be non-negative");
}
// 处理数据
} catch (const std::exception& e) {
logException(e);
throw;
}
}

3. 异常处理与错误码的结合

在某些情况下,特别是与C API交互或需要向后兼容时,可能需要结合使用异常和错误码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 错误码定义
enum class ErrorCode {
SUCCESS = 0,
INVALID_ARGUMENT = 1,
NETWORK_ERROR = 2,
DATABASE_ERROR = 3
};

// 异常类
class AppException : public std::runtime_error {
public:
AppException(ErrorCode code, const std::string& message)
: std::runtime_error(message), code(code) {}

ErrorCode getErrorCode() const { return code; }

private:
ErrorCode code;
};

// 同时支持异常和错误码的函数
ErrorCode processWithErrorCodes(int value, std::string& errorMessage) {
try {
if (value < 0) {
throw AppException(ErrorCode::INVALID_ARGUMENT, "Value must be non-negative");
}
// 处理数据
return ErrorCode::SUCCESS;
} catch (const AppException& e) {
errorMessage = e.what();
return e.getErrorCode();
} catch (const std::exception& e) {
errorMessage = e.what();
return ErrorCode::NETWORK_ERROR; // 默认为网络错误
}
}

// 只使用异常的函数
void processWithExceptions(int value) {
if (value < 0) {
throw AppException(ErrorCode::INVALID_ARGUMENT, "Value must be non-negative");
}
// 处理数据
}

异常处理的测试

1. 测试异常抛出

使用测试框架测试函数是否在预期条件下抛出正确的异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdexcept>

// 被测试的函数
int divide(int a, int b) {
if (b == 0) {
throw std::invalid_argument("Division by zero");
}
return a / b;
}

// 测试代码(使用Google Test框架示例)
TEST(DivideTest, ThrowsOnZeroDivision) {
EXPECT_THROW(divide(10, 0), std::invalid_argument);
}

TEST(DivideTest, ReturnsCorrectResult) {
EXPECT_EQ(divide(10, 2), 5);
EXPECT_EQ(divide(7, 3), 2);
}

2. 测试异常安全

测试函数在抛出异常时是否保持异常安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <vector>

// 被测试的函数(应该提供强异常安全)
void appendIfPositive(std::vector<int>& vec, int value) {
if (value > 0) {
vec.push_back(value);
}
}

// 测试代码
TEST(ExceptionSafetyTest, AppendIfPositiveIsStrongExceptionSafe) {
std::vector<int> original = {1, 2, 3};
std::vector<int> testVec = original;

// 测试正常情况
appendIfPositive(testVec, 4);
EXPECT_EQ(testVec, std::vector<int>({1, 2, 3, 4}));

// 测试边界情况
testVec = original;
appendIfPositive(testVec, 0);
EXPECT_EQ(testVec, original);

testVec = original;
appendIfPositive(testVec, -1);
EXPECT_EQ(testVec, original);
}

3. 模拟异常

在测试中模拟异常,验证系统的恢复能力。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdexcept>
#include <memory>

// 模拟接口
class Database {
public:
virtual ~Database() = default;
virtual void save(const std::string& data) = 0;
};

// 真实实现
class RealDatabase : public Database {
public:
void save(const std::string& data) override {
// 真实的保存操作
}
};

// 模拟实现(用于测试)
class MockDatabase : public Database {
public:
MockDatabase(bool throwOnSave) : throwOnSave(throwOnSave) {}

void save(const std::string& data) override {
if (throwOnSave) {
throw std::runtime_error("Simulated database error");
}
}

private:
bool throwOnSave;
};

// 被测试的服务
class UserService {
private:
std::unique_ptr<Database> db;

public:
UserService(std::unique_ptr<Database> db) : db(std::move(db)) {}

bool createUser(const std::string& userData) {
try {
db->save(userData);
return true;
} catch (const std::exception& e) {
// 记录错误
return false;
}
}
};

// 测试代码
TEST(UserServiceTest, HandlesDatabaseError) {
auto mockDb = std::make_unique<MockDatabase>(true); // 模拟异常
UserService service(std::move(mockDb));

bool result = service.createUser("{\"name\": \"John\"}");
EXPECT_FALSE(result);
}

TEST(UserServiceTest, CreatesUserSuccessfully) {
auto mockDb = std::make_unique<MockDatabase>(false); // 不模拟异常
UserService service(std::move(mockDb));

bool result = service.createUser("{\"name\": \"John\"}");
EXPECT_TRUE(result);
}

C++20/23中与异常相关的新特性

1. std::source_location(C++20)

std::source_location提供了一种获取源代码位置信息的标准方法,无需使用预处理宏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <source_location>
#include <string>
#include <iostream>

void log(const std::string& message,
const std::source_location& loc = std::source_location::current()) {
std::cout << "[" << loc.file_name() << ":" << loc.line()
<< " in " << loc.function_name() << "] "
<< message << std::endl;
}

void process(int value) {
if (value < 0) {
log("Invalid value: " + std::to_string(value));
}
}

int main() {
process(-1);
return 0;
}

2. std::stacktrace(C++23)

std::stacktrace提供了一种获取当前调用栈的标准方法,对于调试异常非常有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stacktrace>
#include <iostream>
#include <stdexcept>

void logException(const std::exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
std::cout << "Stack trace:" << std::endl;
std::cout << std::stacktrace::current() << std::endl;
}

void deepFunction() {
throw std::runtime_error("Deep error");
}

void intermediateFunction() {
deepFunction();
}

int main() {
try {
intermediateFunction();
} catch (const std::exception& e) {
logException(e);
}
return 0;
}

3. std::expected(C++23)

std::expected提供了一种返回值或错误的方式,是异常的一种替代方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <expected>
#include <string>
#include <iostream>

std::expected<int, std::string> divide(int a, int b) {
if (b == 0) {
return std::unexpected("Division by zero");
}
return a / b;
}

int main() {
auto result = divide(10, 2);
if (result) {
std::cout << "Result: " << *result << std::endl;
} else {
std::cout << "Error: " << result.error() << std::endl;
}

result = divide(10, 0);
if (result) {
std::cout << "Result: " << *result << std::endl;
} else {
std::cout << "Error: " << result.error() << std::endl;
}

return 0;
}

异常处理的性能优化

1. 异常处理的性能开销

异常处理的性能开销主要来自:

  • 异常表生成:编译器为每个函数生成异常表
  • 栈展开:当异常抛出时,需要沿着调用栈向上查找catch块
  • 异常对象的创建和销毁:异常对象通常在堆上分配

2. 性能优化策略

a. 只在特殊情况下使用异常

不要将异常用于常规的控制流,只在真正的异常情况下使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 不好的做法:使用异常进行控制流
int findElement(const std::vector<int>& vec, int value) {
for (size_t i = 0; i < vec.size(); i++) {
if (vec[i] == value) {
return i;
}
}
throw std::runtime_error("Element not found");
}

// 好的做法:使用返回值
std::optional<size_t> findElement(const std::vector<int>& vec, int value) {
for (size_t i = 0; i < vec.size(); i++) {
if (vec[i] == value) {
return i;
}
}
return std::nullopt;
}

b. 使用noexcept说明符

对于不会抛出异常的函数,使用noexcept说明符,这样编译器可以进行优化。

1
2
3
4
5
6
7
8
9
10
11
12
// 无异常保证的函数
void swap(int& a, int& b) noexcept {
int temp = a;
a = b;
b = temp;
}

// 条件noexcept
template <typename T>
void swapIfNoexcept(T& a, T& b) noexcept(noexcept(std::swap(a, b))) {
std::swap(a, b);
}

c. 优化异常对象

对于自定义异常类,实现移动构造函数和移动赋值运算符,减少异常对象的复制开销。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class MyException : public std::exception {
private:
std::string message;

public:
explicit MyException(std::string msg) : message(std::move(msg)) {}

// 移动构造函数
MyException(MyException&& other) noexcept : message(std::move(other.message)) {}

// 移动赋值运算符
MyException& operator=(MyException&& other) noexcept {
if (this != &other) {
message = std::move(other.message);
}
return *this;
}

// 禁止复制
MyException(const MyException&) = delete;
MyException& operator=(const MyException&) = delete;

const char* what() const noexcept override {
return message.c_str();
}
};

d. 使用小型异常对象

异常对象应该尽可能小,只包含必要的信息,以减少内存分配和复制的开销。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 好的做法:小型异常对象
class NetworkException : public std::runtime_error {
public:
NetworkException(int errorCode)
: std::runtime_error(getErrorMessage(errorCode)), errorCode(errorCode) {}

int getErrorCode() const { return errorCode; }

private:
int errorCode;

static std::string getErrorMessage(int code) {
// 根据错误码返回错误消息
switch (code) {
case 404: return "Not found";
case 500: return "Internal server error";
default: return "Network error";
}
}
};

总结

高级异常处理是C++编程中的重要课题,它涉及到异常安全、异常传播、多线程中的异常、异常处理模式等多个方面。通过本章的学习,你应该掌握:

  1. 异常安全保证:基本异常安全、强异常安全和无异常保证
  2. 异常传播和重新抛出:异常如何在调用栈中传播,以及如何重新抛出异常
  3. 多线程中的异常:如何在多线程环境中安全地处理异常
  4. 异常与RAII的深度结合:如何使用RAII确保异常安全
  5. 异常处理的设计模式:异常转换器模式、异常边界模式、异常恢复模式
  6. 实际项目中的异常处理策略:分层异常处理、异常日志、异常与错误码的结合
  7. 异常处理的测试:测试异常抛出、测试异常安全、模拟异常
  8. C++20/23中与异常相关的新特性:std::source_location、std::stacktrace、std::expected
  9. 异常处理的性能优化:减少异常使用、使用noexcept、优化异常对象

异常处理是一把双刃剑,它既可以使程序更加健壮和可靠,也可能导致性能下降和代码复杂性增加。在实际项目中,应该根据具体情况,合理使用异常处理机制,遵循最佳实践,以达到代码质量和性能的平衡。

通过不断学习和实践,你会逐渐掌握异常处理的精髓,成为一名更加优秀的C++程序员。