在c++++中,自定义异常错误信息的推荐做法是继承std::exception并重载what()方法。1. 创建一个继承自std::exception的类,并添加用于存储错误信息的std::string成员变量;2. 在构造函数中接收错误信息字符串并初始化该成员变量;3. 重写what()方法,返回该成员变量的c_str(),确保指针生命周期有效;4. 可选地,为异常类添加额外上下文信息(如文件名、行号、错误码)并在构造函数中初始化;5. 可选择在what()中整合上下文信息或提供独立getter方法以支持结构化访问。这种方式不仅保持与标准异常处理机制兼容,还提升了错误信息的可读性和程序对异常的精细控制能力。

在C++中,要自定义异常的错误信息,最直接且推荐的做法是继承自
std::exception
what()

自定义C++异常并重载
what()
std::exception
what()
以下是一个基本的实现思路:
立即学习“C++免费学习笔记(深入)”;

#include <exception>
#include <string>
#include <iostream>
#include <stdexcept> // For std::runtime_error example
// 自定义异常类
class MyCustomException : public std::exception {
private:
std::string message_; // 存储具体的错误信息
public:
// 构造函数,接收错误信息
explicit MyCustomException(const std::string& msg) : message_(msg) {}
// 重载what()方法,返回错误信息
// 注意:noexcept是C++11引入的,表示该函数不会抛出异常
// 返回的const char* 必须在异常对象的生命周期内有效
const char* what() const noexcept override {
return message_.c_str();
}
// 也可以添加其他方法来获取更详细的上下文信息
// 例如:int getErrorCode() const;
};
// 示例函数,可能抛出自定义异常
void process_data(int value) {
if (value < 0) {
// 抛出带有特定错误信息的自定义异常
throw MyCustomException("输入值不能为负数: " + std::to_string(value));
}
// 模拟其他处理...
std::cout << "数据处理成功: " << value << std::endl;
}
int main() {
try {
process_data(10);
process_data(-5); // 这里会抛出异常
} catch (const MyCustomException& e) {
// 捕获自定义异常
std::cerr << "捕获到自定义异常: " << e.what() << std::endl;
} catch (const std::exception& e) {
// 捕获其他标准异常
std::cerr << "捕获到标准异常: " << e.what() << std::endl;
} catch (...) {
// 捕获所有其他未知异常
std::cerr << "捕获到未知异常" << std::endl;
}
std::cout << "程序继续执行..." << std::endl;
return 0;
}在这个例子中,
MyCustomException
std::string
what()
e.what()
std::string
在我看来,直接抛出
std::string

首先,它丧失了类型信息。当你
throw std::string("Error!")catch (std::string& e)
catch (const std::exception& e)
其次,
std::exception
what()
std::exception
std::exception
e.what()
std::string
char*
最后,内存管理也是一个考量。抛出
std::string
std::string
char*
std::exception
what()
const char*
what()
const char* what() const noexcept
返回类型**:这意味着
const
what()
const
noexcept
noexcept
what()
std::terminate
what()
基于这些约束,最佳实践通常是:将错误信息存储在自定义异常类的一个
std::string
what()
std::string
message_.c_str()
class MyCustomException : public std::exception {
private:
std::string message_; // 存储错误信息
public:
explicit MyCustomException(const std::string& msg) : message_(msg) {}
const char* what() const noexcept override {
// 关键点:返回内部std::string的c_str()
// std::string保证了其内部缓冲区的生命周期与std::string对象一致
return message_.c_str();
}
};这种方式确保了
what()
const char*
message_
std::string
message_
MyCustomException
MyCustomException
message_
常见陷阱:
what()
std::string
c_str()
std::string
what()
// 错误示例
const char* what() const noexcept override {
std::string temp_msg = "Error: " + message_;
return temp_msg.c_str(); // temp_msg在函数返回后销毁,指针悬空
}// 这种简单返回字面量是安全的,但无法自定义内容
const char* what() const noexcept override {
return "Generic error.";
}仅仅一个简单的错误信息字符串,在很多复杂的场景下可能远远不够。当异常发生时,我们往往需要知道更多上下文信息来定位问题,比如:哪个文件出了问题?哪一行代码?具体的错误码是什么?操作的用户是谁?时间戳是多少?
为了在自定义异常中包含这些更丰富的上下文信息,我们可以为异常类添加额外的成员变量,并在构造函数中接收这些信息。然后,我们可以选择几种方式来暴露这些信息:
在what()
what()
#include <exception>
#include <string>
#include <iostream>
#include <sstream> // 用于字符串拼接
class FileOperationException : public std::exception {
private:
std::string message_;
std::string filename_;
int line_number_;
int error_code_; // 比如系统错误码
public:
FileOperationException(const std::string& msg, const std::string& filename, int line, int err_code)
: message_(msg), filename_(filename), line_number_(line), error_code_(err_code) {}
const char* what() const noexcept override {
std::ostringstream oss;
oss << "文件操作错误: " << message_
<< " (文件: " << filename_
<< ", 行: " << line_number_
<< ", 错误码: " << error_code_ << ")";
// 注意:这里需要将拼接后的字符串存储起来,不能直接返回临时对象的c_str()
// 最佳实践是,让message_存储完整的拼接字符串
// 为了演示,这里假设message_已经包含了所有信息
return message_.c_str(); // 假设message_在构造时就已拼接好
}
// 为了避免what()内部拼接导致的问题,通常会在构造函数或一个内部辅助函数中完成拼接
// 或者,更好的方法是提供getter,让外部按需获取详细信息
// 这里只是为了演示在what()中包含更多信息的概念,实际代码中message_应该在构造函数中完成拼接
};
// 改进后的FileOperationException,在构造函数中拼接what()信息
class ImprovedFileOperationException : public std::exception {
private:
std::string full_message_; // 存储what()的完整信息
std::string filename_;
int line_number_;
int error_code_;
// 辅助函数,用于构建完整的错误信息
std::string build_full_message(const std::string& msg, const std::string& filename, int line, int err_code) {
std::ostringstream oss;
oss << "文件操作错误: " << msg
<< " (文件: " << filename
<< ", 行: " << line
<< ", 错误码: " << err_code << ")";
return oss.str();
}
public:
ImprovedFileOperationException(const std::string& msg, const std::string& filename, int line, int err_code)
: full_message_(build_full_message(msg, filename, line, err_code)),
filename_(filename), line_number_(line), error_code_(err_code) {}
const char* what() const noexcept override {
return full_message_.c_str();
}
// 提供独立的getter方法,让捕获者可以结构化地访问这些信息
const std::string& getFilename() const { return filename_; }
int getLineNumber() const { return line_number_; }
int getErrorCode() const { return error_code_; }
};
void read_config(const std::string& path) {
// 模拟文件读取失败
if (path == "invalid.conf") {
throw ImprovedFileOperationException("无法打开配置文件", path, __LINE__, 1001);
}
std::cout << "成功读取配置文件: " << path << std::endl;
}
int main_context_info() {
try {
read_config("valid.conf");
read_config("invalid.conf");
} catch (const ImprovedFileOperationException& e) {
std::cerr << "捕获到文件操作异常: " << e.what() << std::endl;
std::cerr << "详细信息 - 文件: " << e.getFilename()
<< ", 行: " << e.getLineNumber()
<< ", 错误码: " << e.getErrorCode() << std::endl;
} catch (const std::exception& e) {
std::cerr << "捕获到标准异常: " << e.what() << std::endl;
}
return 0;
}提供独立的Getter方法: 这是我个人更倾向的方式。虽然
what()
what()
在上面的
ImprovedFileOperationException
what()
getFilename()
getLineNumber()
getErrorCode()
选择哪种方式取决于你的需求。如果只是为了日志记录或给用户看,
what()
以上就是如何自定义C++异常的错误信息 重载what()方法最佳实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号