先说说throw关键字怎么用才稳妥。见过太多人随手一扔字符串就完事,比如throw "文件打开失败",这种写法在小型项目里可能还能混过去,但到了大型系统里简直就是灾难。正确的做法是继承exception类来自定义异常类型,这样既能保留错误信息,又能维护异常层次。比如:
class FileIOException : public std::exception {
private:
std::string msg;
public:
FileIOException(const std::string& filename) {
msg = "文件操作异常: " + filename;
}
const char* what() const noexcept override {
return msg.c_str();
}
};
// 使用时
if (!_open()) {
throw FileIOException("config.xml");
}
try-catch块的使用也得讲究策略。最忌讳的就是在系统关键路径上随意捕获所有异常。特别是在多线程环境里,某个线程突然吞掉异常会导致整个程序状态错乱。建议采用分层捕获策略,在底层捕获具体异常,在高层捕获通用异常。比如:
try {
// 系统核心逻辑
ProcessTransaction();
}
catch (const DatabaseException& e) {
// 数据库异常特殊处理
RollbackTransaction();
logger->error("数据库操作失败: {}", e.what());
}
catch (const NetworkException& e) {
// 网络异常特殊处理
RetryConnection();
logger->warn("网络连接异常: {}", e.what());
}
catch (const std::exception& e) {
// 通用异常处理
logger->error("系统异常: {}", e.what());
throw; // 重新抛出给上层
}
说到异常安全,这可是系统稳定性的生命线。RAII技术必须熟练掌握,这是C++异常安全的基石。举个例子,自己实现个智能指针其实不难:
template<typename T>
class SafePointer {
private:
T* ptr;
public:
explicit SafePointer(T* p) : ptr(p) {}
~SafePointer() { delete ptr; }
// 禁用拷贝构造和赋值
SafePointer(const SafePointer&) = delete;
SafePointer& operator=(const SafePointer&) = delete;
// 启用移动语义
SafePointer(SafePointer&& other) noexcept : ptr(other.ptr) {
other.ptr = nullptr;
}
T* operator->() const { return ptr; }
};
// 使用示例
void ProcessData() {
SafePointer<Data> data(new Data());
data->Parse(); // 如果这里抛出异常,SafePointer会自动释放内存
}
在系统开发中,还要特别注意析构函数不要抛出异常。这是铁律!因为如果栈展开过程中析构函数再抛出异常,程序直接就会终止。建议在析构函数里用try-catch块吞掉所有异常:
~ResourceHolder() {
try {
Release(); // 资源释放可能抛出异常
}
catch (...) {
// 记录日志但不要抛出
logger->error("资源释放异常");
}
}
对于性能敏感的系统,可以考虑使用noexcept关键字。但要注意,滥用noexcept比不用更危险。只有在确定函数绝对不会抛出异常时才使用,比如简单的getter方法:
const std::string& GetName() const noexcept {
return name;
}
现代C++还提供了异常规约机制,虽然语法有点复杂,但在大型项目里特别有用。比如用static_assert配合类型特征来检查异常安全性:
template<typename Func>
void SafeExecute(Func f) {
static_assert(noexcept(f()), "该函数可能抛出异常");
f();
}
最后说说实际项目中的经验。在分布式系统里,异常处理要结合日志系统和监控告警。我们团队定了个规范:所有捕获的异常必须带上上下文信息,比如:
catch (const std::exception& e) {
logger->error("[模块名][函数名] 异常信息: {}, 线程ID: {}, 时间戳: {}",
e.what(), std::this_thread::get_id(), GetTimestamp());
metrics->Increment("exception_count"); // 监控计数
}
记住,好的异常处理不是为了防止出错,而是为了让出错时的行为可控。在系统设计中,要考虑异常情况下的资源清理、状态回滚和错误上报,这样才能打造出真正健壮的系统。