

在程序运行过程中,错误是不可避免的:除法运算中除数为零、数组访问越界、内存分配失败等。传统的错误处理方式主要有两种:
错误码返回方式:函数通过特定返回值表示错误,调用者需手动检查
// 传统错误码示例
int divide(int a, int b, int& result) {
if (b == 0) return -1; // 返回错误码
result = a / b;
return 0; // 成功
}缺点:错误码易被忽略,代码中错误处理与业务逻辑混杂。
全局错误标志:通过全局变量(如errno)标记错误
缺点:多层函数调用时,错误需逐层传递,代码冗余且易出错。
异常处理将错误检测与错误处理分离:
throw)catch)异常处理的优势:
C++ 异常处理通过三个关键字实现:try、throw、catch,基本结构如下:
try {
// 可能抛出异常的代码
if (错误条件) {
throw 异常值; // 抛出异常
}
} catch (异常类型1 参数) {
// 处理类型1的异常
} catch (异常类型2 参数) {
// 处理类型2的异常
} catch (...) {
// 处理所有未捕获的异常(兜底)
}#include <iostream>
#include <string>
using namespace std;
// 除法函数:除数为零时抛出异常
double divide(double a, double b) {
if (b == 0) {
// 抛出异常(可以是任意类型)
throw string("除数不能为零!");
}
return a / b;
}
int main() {
double num1, num2, result;
cout << "请输入两个数(用空格分隔):";
cin >> num1 >> num2;
try {
// 尝试执行可能抛出异常的操作
result = divide(num1, num2);
// 若没有异常,继续执行
cout << num1 << " / " << num2 << " = " << result << endl;
}
// 捕获string类型的异常
catch (const string& errorMsg) {
cout << "错误:" << errorMsg << endl;
}
// 捕获所有未处理的异常
catch (...) {
cout << "发生未知错误!" << endl;
}
// 异常处理后,程序继续执行
cout << "程序继续运行..." << endl;
return 0;
}运行结果 1(正常情况):
请输入两个数(用空格分隔):10 2
10 / 2 = 5
程序继续运行...运行结果 2(异常情况):
请输入两个数(用空格分隔):8 0
错误:除数不能为零!
程序继续运行...异常处理流程

函数可以声明其可能抛出的异常类型,称为异常接口声明,语法:
返回类型 函数名(参数列表) throw(异常类型列表);throw():表示函数不抛出任何异常throw(Type1, Type2):表示函数可能抛出Type1或Type2类型异常⚠️ 注意:C++11 起推荐使用
noexcept替代传统异常接口声明,传统方式在 C++17 中已被弃用。
#include <iostream>
#include <stdexcept>
using namespace std;
// 声明可能抛出invalid_argument异常
void setAge(int age) throw(invalid_argument) {
if (age < 0 || age > 150) {
throw invalid_argument("年龄必须在0-150之间");
}
cout << "年龄设置为:" << age << endl;
}
// 声明不抛出任何异常(C++11前写法)
void safeFunc() throw() {
cout << "这个函数不会抛出异常" << endl;
}
int main() {
try {
setAge(200); // 会抛出异常
safeFunc();
} catch (const invalid_argument& e) {
cout << "错误:" << e.what() << endl;
}
return 0;
} 当异常被抛出时,程序会退出当前try块,并自动销毁在try块中创建的所有局部对象,这个过程称为栈展开(Stack Unwinding)。这保证了资源不会因为异常而泄露。
#include <iostream>
using namespace std;
// 测试类:跟踪对象创建和销毁
class TestObject {
private:
string name;
public:
// 构造函数
TestObject(const string& objName) : name(objName) {
cout << "对象 [" << name << "] 被创建" << endl;
}
// 析构函数
~TestObject() {
cout << "对象 [" << name << "] 被销毁(析构函数调用)" << endl;
}
};
// 三级函数:抛出异常
void thirdLevel() {
TestObject obj3("三级对象");
cout << "在thirdLevel中抛出异常..." << endl;
throw "异常来自thirdLevel"; // 抛出异常
}
// 二级函数:调用三级函数
void secondLevel() {
TestObject obj2("二级对象");
thirdLevel(); // 调用可能抛出异常的函数
cout << "thirdLevel执行完成(这句不会执行)" << endl;
}
// 一级函数:调用二级函数
void firstLevel() {
TestObject obj1("一级对象");
secondLevel();
cout << "secondLevel执行完成(这句不会执行)" << endl;
}
int main() {
TestObject mainObj("主函数对象");
try {
firstLevel();
} catch (const char* errorMsg) {
cout << "捕获异常:" << errorMsg << endl;
}
return 0;
}运行结果:
对象 [主函数对象] 被创建
对象 [一级对象] 被创建
对象 [二级对象] 被创建
对象 [三级对象] 被创建
在thirdLevel中抛出异常...
对象 [三级对象] 被销毁(析构函数调用)
对象 [二级对象] 被销毁(析构函数调用)
对象 [一级对象] 被销毁(析构函数调用)
捕获异常:异常来自thirdLevel结论:
mainObj)不会在栈展开时销毁,会在程序结束时正常销毁 C++ 标准库定义了一套异常类体系,所有标准异常都继承自std::exception基类,该类提供了what()方法返回异常描述信息。
@startuml
title C++标准异常类层次结构
exception exception {
+ what(): const char*
}
exception bad_alloc
exception bad_cast
exception bad_typeid
exception logic_error {
+ logic_error(const string& msg)
}
exception domain_error
exception invalid_argument
exception length_error
exception out_of_range
exception runtime_error {
+ runtime_error(const string& msg)
}
exception range_error
exception overflow_error
exception underflow_error
exception <|-- bad_alloc
exception <|-- bad_cast
exception <|-- bad_typeid
exception <|-- logic_error
exception <|-- runtime_error
logic_error <|-- domain_error
logic_error <|-- invalid_argument
logic_error <|-- length_error
logic_error <|-- out_of_range
runtime_error <|-- range_error
runtime_error <|-- overflow_error
runtime_error <|-- underflow_error
@enduml
#include <iostream>
#include <stdexcept> // 标准异常头文件
#include <vector>
using namespace std;
int main() {
try {
// 1. 动态内存分配失败(bad_alloc)
// int* largeArray = new int[1000000000000ULL];
// 2. 无效参数(invalid_argument)
// throw invalid_argument("参数必须为正数");
// 3. 数组越界访问(out_of_range)
vector<int> numbers(5); // 大小为5的向量
cout << "尝试访问numbers[10]:";
numbers.at(10); // at()方法会抛出out_of_range异常
// 4. 运行时错误(runtime_error)
// throw runtime_error("文件读取失败");
}
// 捕获内存分配异常
catch (const bad_alloc& e) {
cout << "内存分配失败:" << e.what() << endl;
}
// 捕获越界异常
catch (const out_of_range& e) {
cout << "越界错误:" << e.what() << endl;
}
// 捕获参数错误
catch (const invalid_argument& e) {
cout << "参数错误:" << e.what() << endl;
}
// 捕获所有标准异常(基类)
catch (const exception& e) {
cout << "标准异常:" << e.what() << endl;
}
return 0;
}运行结果:
尝试访问numbers[10]:越界错误:vector::_M_range_check: __n (which is 10) >= this->size() (which is 5)我们基于银行账户管理程序,加入异常处理机制,处理存款负数、取款超额等异常情况。
#include <iostream>
#include <string>
#include <stdexcept> // 标准异常基类
using namespace std;
// 银行账户异常基类(继承自标准异常)
class AccountException : public exception {
protected:
string errorMsg; // 异常信息
public:
// 构造函数
AccountException(const string& msg) : errorMsg(msg) {}
// 重写what()方法,返回异常描述
const char* what() const noexcept override {
return errorMsg.c_str();
}
};
// 无效存款金额异常
class InvalidDepositException : public AccountException {
public:
InvalidDepositException(double amount)
: AccountException("无效存款金额:" + to_string(amount) + "(必须大于0)") {}
};
// 无效取款金额异常
class InvalidWithdrawException : public AccountException {
public:
InvalidWithdrawException(double amount)
: AccountException("无效取款金额:" + to_string(amount) + "(必须大于0)") {}
};
// 余额不足异常
class InsufficientFundsException : public AccountException {
public:
InsufficientFundsException(double balance, double amount)
: AccountException("余额不足:当前余额" + to_string(balance) +
",尝试取款" + to_string(amount)) {}
};
// 银行账户类
class BankAccount {
private:
string accountId; // 账号
string ownerName; // 户主姓名
double balance; // 账户余额
public:
// 构造函数
BankAccount(const string& id, const string& name, double initialBalance = 0.0)
: accountId(id), ownerName(name), balance(initialBalance) {
// 初始余额不能为负
if (initialBalance < 0) {
throw InvalidDepositException(initialBalance);
}
}
// 获取账号
string getAccountId() const { return accountId; }
// 获取户主姓名
string getOwnerName() const { return ownerName; }
// 获取当前余额
double getBalance() const { return balance; }
// 存款操作
void deposit(double amount) {
if (amount <= 0) {
throw InvalidDepositException(amount); // 抛出异常
}
balance += amount;
cout << "存款成功!存入:" << amount << ",当前余额:" << balance << endl;
}
// 取款操作
void withdraw(double amount) {
if (amount <= 0) {
throw InvalidWithdrawException(amount); // 抛出异常
}
if (amount > balance) {
throw InsufficientFundsException(balance, amount); // 抛出异常
}
balance -= amount;
cout << "取款成功!取出:" << amount << ",当前余额:" << balance << endl;
}
// 显示账户信息
void showAccountInfo() const {
cout << "\n===== 账户信息 =====" << endl;
cout << "账号:" << accountId << endl;
cout << "户主:" << ownerName << endl;
cout << "当前余额:" << balance << " 元" << endl;
cout << "====================\n" << endl;
}
};
// 账户管理函数
void manageAccount(BankAccount& account) {
int choice;
double amount;
do {
cout << "\n===== 账户管理菜单 =====" << endl;
cout << "1. 查看账户信息" << endl;
cout << "2. 存款" << endl;
cout << "3. 取款" << endl;
cout << "4. 退出" << endl;
cout << "请选择操作:";
cin >> choice;
try {
switch (choice) {
case 1:
account.showAccountInfo();
break;
case 2:
cout << "请输入存款金额:";
cin >> amount;
account.deposit(amount);
break;
case 3:
cout << "请输入取款金额:";
cin >> amount;
account.withdraw(amount);
break;
case 4:
cout << "感谢使用,再见!" << endl;
break;
default:
cout << "无效选择,请重新输入!" << endl;
}
}
// 捕获账户相关异常
catch (const AccountException& e) {
cout << "操作失败:" << e.what() << endl;
}
// 捕获其他标准异常
catch (const exception& e) {
cout << "错误:" << e.what() << endl;
}
} while (choice != 4);
}
int main() {
try {
// 创建银行账户(初始余额可能抛出异常)
BankAccount account("6222021001234567890", "张三", 1000.0);
cout << "账户创建成功!户主:" << account.getOwnerName() << endl;
// 管理账户
manageAccount(account);
}
// 捕获账户初始化异常
catch (const exception& e) {
cout << "程序启动失败:" << e.what() << endl;
return 1; // 异常退出
}
return 0;
}代码说明:
std::exception,便于统一处理运行示例(异常情况):
账户创建成功!户主:张三
===== 账户管理菜单 =====
1. 查看账户信息
2. 存款
3. 取款
4. 退出
请选择操作:3
请输入取款金额:1500
操作失败:余额不足:当前余额1000.000000,尝试取款1500.000000异常安全性(Exception Safety)指函数在抛出异常时,程序仍能保持一致状态的能力,分为三个级别:
#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;
// 基本保证实现:异常可能导致部分修改
void basicGuarantee(vector<int>& v, int index, int value) {
if (index < 0 || index >= v.size()) {
throw out_of_range("索引越界");
}
v[index] = value; // 可能已修改
if (value < 0) {
throw invalid_argument("值不能为负"); // 抛出异常
}
}
// 强保证实现:使用复制-交换技术
void strongGuarantee(vector<int>& v, int index, int value) {
if (index < 0 || index >= v.size()) {
throw out_of_range("索引越界");
}
if (value < 0) {
throw invalid_argument("值不能为负");
}
// 1. 复制原对象
vector<int> temp = v;
// 2. 修改副本
temp[index] = value;
// 3. 交换副本与原对象(无异常操作)
swap(v, temp);
}
int main() {
vector<int> v = {1, 2, 3, 4, 5};
// 测试基本保证
try {
basicGuarantee(v, 2, -10); // 会抛出异常
} catch (const exception& e) {
cout << "基本保证测试:" << e.what() << endl;
cout << "修改后的值:" << v[2] << endl; // 已被修改为-10
}
// 恢复原始值
v = {1, 2, 3, 4, 5};
// 测试强保证
try {
strongGuarantee(v, 2, -10); // 会抛出异常
} catch (const exception& e) {
cout << "强保证测试:" << e.what() << endl;
cout << "值未被修改:" << v[2] << endl; // 仍为3
}
return 0;
}异常抛出时若未正确释放资源(内存、文件句柄等),会导致资源泄露。RAII(资源获取即初始化) 是解决该问题的最佳实践:资源在对象构造时获取,在析构时释放。
#include <iostream>
#include <fstream>
#include <memory> // 智能指针
using namespace std;
// 1. 手动管理资源:可能泄露
void riskyResourceManagement() {
// 分配资源
int* data = new int[100]; // 动态内存
ofstream file("data.txt"); // 文件资源
// 模拟异常
throw runtime_error("发生错误!");
// 以下代码不会执行,导致内存泄露
delete[] data; // 内存未释放
// 文件会在ofstream析构时关闭(因为file是局部对象)
}
// 2. RAII管理资源:安全可靠
void safeResourceManagement() {
// 智能指针自动管理内存(RAII)
unique_ptr<int[]> data(new int[100]); // 异常时自动释放
ofstream file("data.txt"); // RAII管理的文件资源
// 模拟异常
throw runtime_error("发生错误!");
// 无需手动释放资源,析构函数会自动调用
}
int main() {
try {
riskyResourceManagement();
} catch (const exception& e) {
cout << "捕获异常:" << e.what() << endl;
cout << "注意:riskyResourceManagement可能导致内存泄露!" << endl;
}
try {
safeResourceManagement();
} catch (const exception& e) {
cout << "捕获异常:" << e.what() << endl;
cout << "safeResourceManagement资源已正确释放!" << endl;
}
return 0;
}RAII 核心思想:将资源封装在对象中,利用对象的生命周期管理资源,确保异常发生时资源被自动释放。
C++11 引入noexcept说明符,用于声明函数是否可能抛出异常:
void func() noexcept;:函数不会抛出任何异常void func() noexcept(condition);:当condition为真时不抛出异常noexcept相比传统throw()的优势:
#include <iostream>
#include <vector>
using namespace std;
// 声明不会抛出异常
void safeOperation() noexcept {
cout << "这是一个安全的操作" << endl;
}
// 条件noexcept:当T的移动构造函数不抛异常时,本函数也不抛
template <typename T>
void swapObjects(T& a, T& b) noexcept(noexcept(T(std::move(a)))) {
T temp(std::move(a)); // 若T的移动构造函数noexcept,则本操作安全
a = std::move(b);
b = std::move(temp);
}
// 可能抛出异常的函数
void riskyOperation(bool shouldThrow) {
if (shouldThrow) {
throw runtime_error("发生异常");
}
cout << "操作成功" << endl;
}
int main() {
// 测试noexcept函数
safeOperation();
// 测试条件noexcept
vector<int> v1 = {1, 2, 3};
vector<int> v2 = {4, 5, 6};
swapObjects(v1, v2);
// 测试noexcept运算符
cout << "safeOperation是否不抛异常:" << noexcept(safeOperation()) << endl;
cout << "riskyOperation是否不抛异常:" << noexcept(riskyOperation(false)) << endl;
// 调用可能抛异常的函数
try {
riskyOperation(true);
} catch (const exception& e) {
cout << "捕获异常:" << e.what() << endl;
}
return 0;
}最佳实践:
noexcept,不应抛出异常noexceptnoexcept本章我们系统学习了 C++ 异常处理机制,核心要点总结:
try块包含可能出错的代码,throw抛出异常,catch捕获并处理std::exception,便于统一处理和信息传递noexcept声明,替代传统异常接口;合理使用标准库异常类异常处理是编写健壮 C++ 程序的关键技术,合理使用能显著提高代码的可读性和可靠性,但也需避免过度使用(简单错误可用返回值处理)。
希望本文能帮助大家掌握 C++ 异常处理的核心知识!所有代码均可直接编译运行,建议动手实践加深理解。如有疑问,欢迎在评论区交流~