一、C++ 异常处理😊
1.1 定义
C++ 中的异常处理用于应对程序运行中的异常情况(如除零、数组越界等),通过 try-catch 机制捕获和处理错误,防止程序崩溃。
异常是程序运行时意外发生的事件,可以通过抛出(throw)和捕获(catch)机制处理。
1.2 通俗解释
- 抛异常(throw): 就像在程序运行时发现问题了,扔出一个问题给系统处理。
- 捕获异常(catch): 系统接收到这个问题后,根据你的代码去解决问题。
- try 块: 这是一个保护块,把可能出错的代码放在这里,避免程序直接崩溃。
1.3 固定格式
try {// 可能抛出异常的代码throw exception_object; // 抛出异常
} catch (exception_type variable_name) {// 捕获异常并处理
} catch (...) {// 捕获所有类型的异常(可选)
}
throw
:用来抛出异常,可以是内置类型(如整数、字符串)或自定义类型(如对象)。catch
:用来捕获异常,必须匹配throw
的类型,或者用...
捕获所有类型。try
:将可能出错的代码块包裹起来。
1.4 注意点
-
匹配顺序:
catch
子句是按顺序匹配的,第一个匹配的会被执行。catch (...)
必须放在最后,用于捕获所有未明确处理的异常。
-
异常传递:
- 如果函数中抛出的异常没有被处理,会向调用者函数传播,直到找到合适的
catch
块。
- 如果函数中抛出的异常没有被处理,会向调用者函数传播,直到找到合适的
-
自定义异常类:
- 可以通过定义自己的类,抛出更具体的异常信息。
1.5C++中的主要异常类型
1.5.1. 内置类型的异常
1. 整数异常
可以直接抛出整数类型的异常,用于简单错误标识。
#include <iostream>
using namespace std;int main() {try {throw 42; // 抛出整数异常} catch (int e) { // 捕获整数类型的异常cout << "Caught integer exception: " << e << endl;}return 0;
}
/*
输出:
Caught integer exception: 42
*/
2. 字符串异常
可以抛出字符串(如 const char*
或 std::string
)类型的异常,通常用来描述错误信息。
#include <iostream>
using namespace std;int main() {try {throw "An error occurred"; // 抛出字符串异常} catch (const char* e) { // 捕获字符串类型异常cout << "Caught exception: " << e << endl;}return 0;
}
/*
输出:
Caught exception: An error occurred
*/
3. 浮点数异常
可以直接抛出浮点类型,用于表示数值相关的错误。
#include <iostream>
using namespace std;int main() {try {throw 3.14; // 抛出浮点数异常} catch (double e) { // 捕获浮点类型异常cout << "Caught floating-point exception: " << e << endl;}return 0;
}
/*
输出:
Caught floating-point exception: 3.14
*/
4. 布尔类型异常
布尔类型也可以被抛出,通常用作简单的状态标识。
#include <iostream>
using namespace std;int main() {try {throw true; // 抛出布尔类型异常} catch (bool e) {cout << "Caught boolean exception: " << (e ? "true" : "false") << endl;}return 0;
}
/*
输出:
Caught boolean exception: true
*/
1.5.2. 标准库类型的异常
C++ 提供了一些预定义的异常类,它们位于标准库中,包含在 <stdexcept>
、<exception>
等头文件中。以下是常用的标准异常类型:
1. std::exception
- 是所有标准异常类的基类。
- 通常作为通用异常捕获的类型。
#include <iostream> #include <exception> using namespace std;int main() {try {throw exception(); // 抛出标准异常} catch (exception& e) { // 捕获标准异常cout << "Caught standard exception: " << e.what() << endl;}return 0; } /* 输出: Caught standard exception: std::exception */
2. std::logic_error
- 表示程序逻辑错误,通常由于程序员的疏忽引起。
- 例如:访问空指针、非法参数等。
#include <iostream>
#include <stdexcept>
using namespace std;int main() {try {throw logic_error("Logic error occurred"); // 抛出逻辑错误} catch (logic_error& e) {cout << "Caught logic_error: " << e.what() << endl;}return 0;
}
/*
输出:
Caught logic_error: Logic error occurred
*/
3
. std::runtime_error
- 表示程序运行时的错误。
- 例如:文件未找到、网络中断等。
#include <iostream>
#include <stdexcept>
using namespace std;int main() {try {throw runtime_error("Runtime error occurred"); // 抛出运行时错误} catch (runtime_error& e) {cout << "Caught runtime_error: " << e.what() << endl;}return 0;
}
/*
输出:
Caught runtime_error: Runtime error occurred
*/
4. std::bad_alloc
- 表示内存分配失败。
- 当
new
操作符无法分配内存时,抛出此异常。
#include <iostream>
#include <new>
using namespace std;int main() {try {int* arr = new int[1000000000000000]; // 请求过多内存} catch (bad_alloc& e) {cout << "Caught bad_alloc: " << e.what() << endl;}return 0;
}
/*输出:
Caught bad_alloc: std::bad_alloc
*/
5. std::out_of_range
- 表示访问容器时超出有效范围。
- 例如:访问数组中不存在的元素。
#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;int main() {try {vector<int> vec = {1, 2, 3};cout << vec.at(10); // 超出范围} catch (out_of_range& e) {cout << "Caught out_of_range: " << e.what() << endl;}return 0;
}
/*输出:
Caught out_of_range: vector::_M_range_check: __n (which is 10) >= this->size() (which is 3)
*/
1.5.3自定义类型的异常
用户可以自定义类作为异常类型,用于处理程序特定的错误场景。
1.自定义异常类
#include <iostream>
#include <string>
using namespace std;class MyException {string message;
public:MyException(string msg) : message(msg) {}string getMessage() const { return message; }
};int main() {try {throw MyException("Custom exception occurred!"); // 抛出自定义异常} catch (MyException& e) {cout << "Caught exception: " << e.getMessage() << endl;}return 0;
}
/*输出:
Caught exception: Custom exception occurred!
*/
1.5.4 捕获所有类型的异常
1.使用 catch(...)
捕获任意类型的异常。
#include <iostream>
using namespace std;int main() {try {throw 3.14; // 抛出浮点数异常} catch (...) { // 捕获所有异常cout << "Caught an unknown exception." << endl;}return 0;
}
/*输出:
Caught an unknown exception.
*/
1.6总结
常见的异常类型
1.6.1内置类型:
- 整数、浮点数、字符串、布尔值。
1.6.2标准库类型:
std::exception
(基类)。std::logic_error
(逻辑错误)。std::runtime_error
(运行时错误)。std::bad_alloc
(内存分配失败)。std::out_of_range
(超出范围错误)。
1.6.3用户自定义类型:
- 用户可以定义自己的类来表示异常类型。
1.7捕获规则
- 使用匹配的
catch
处理具体异常。 - 使用
catch(...)
捕获所有未处理的异常。
1.7.1综合代码示例与解析:
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
using namespace std;// 自定义异常类
class MyException {string message;
public:MyException(string msg) : message(msg) {}string getMessage() const { return message; }
};int main() {try {// Uncomment (取消注释) 以下一行代码逐个测试不同类型的异常// throw 42; // 整数类型异常// throw 3.14; // 浮点数类型异常// throw "An error occurred!"; // 字符串异常// throw string("String object error"); // std::string 异常// throw logic_error("Logic error"); // 标准库逻辑错误// throw runtime_error("Runtime error"); // 标准库运行时错误// throw out_of_range("Out of range error"); // 超出范围异常// throw bad_alloc(); // 内存分配失败异常throw MyException("Custom exception occurred!"); // 自定义异常} catch (int e) {cout << "Caught an integer exception: " << e << endl;} catch (double e) {cout << "Caught a floating-point exception: " << e << endl;} catch (const char* e) {cout << "Caught a C-string exception: " << e << endl;} catch (string& e) {cout << "Caught a string object exception: " << e << endl;} catch (logic_error& e) {cout << "Caught logic_error: " << e.what() << endl;} catch (runtime_error& e) {cout << "Caught runtime_error: " << e.what() << endl;} catch (out_of_range& e) {cout << "Caught out_of_range: " << e.what() << endl;} catch (bad_alloc& e) {cout << "Caught bad_alloc: " << e.what() << endl;} catch (MyException& e) {cout << "Caught custom exception: " << e.getMessage() << endl;} catch (...) { // 捕获所有其他类型的异常cout << "Caught an unknown exception." << endl;}cout << "Program continues after exception handling." << endl;return 0;
}
1.7.2运行结果示例:
1. 抛出整数类型异常:throw 42;
输出:
Caught an integer exception: 42
Program continues after exception handling.
2. 抛出浮点数类型异常:throw 3.14;
输出:
Caught a floating-point exception: 3.14
Program continues after exception handling.
3. 抛出 C 字符串类型异常:throw "An error occurred!";
输出:
Caught a C-string exception: An error occurred!
Program continues after exception handling.
4. 抛出 C++ 字符串类型异常:throw string("String object error");
输出:
Caught a string object exception: String object error
Program continues after exception handling.
5. 抛出标准库逻辑错误异常:throw logic_error("Logic error");
输出:
Caught logic_error: Logic error
Program continues after exception handling.
6. 抛出标准库运行时错误异常:throw runtime_error("Runtime error");
输出:
Caught runtime_error: Runtime error
Program continues after exception handling.
7. 抛出超出范围异常:throw out_of_range("Out of range error");
输出:
Caught out_of_range: Out of range error
Program continues after exception handling.
8. 抛出内存分配失败异常:throw bad_alloc();
输出:
Caught bad_alloc: std::bad_alloc
Program continues after exception handling.
9. 抛出自定义异常:throw MyException("Custom exception occurred!");
输出:
Caught custom exception: Custom exception occurred!
Program continues after exception handling.
10. 抛出未匹配的异常(如结构体或未列出的类型);
struct UnknownException {};
throw UnknownException();
输出:
Caught an unknown exception.
Program continues after exception handling.
1.7.3通用总结
- 对每种异常类型都有专门的
catch
块处理,并输出对应的内容。 - 如果异常类型不匹配,会进入通用的
catch(...)
块,确保程序不会因未捕获的异常而崩溃。
解析:
-
逐个抛出不同类型的异常:
- 使用
throw
语句抛出整数、浮点数、字符串、标准库异常、自定义异常等。
- 使用
-
多个
catch
块:- 每个
catch
块捕获一种特定类型的异常。 - 使用
catch(...)
捕获所有未明确处理的异常。
- 每个
-
自定义异常:
- 定义了一个
MyException
类,用于抛出和捕获自定义的错误。
- 定义了一个
二、用命名空间避免同名冲突
1. 什么是同名冲突?
1.1 同名冲突现象
在大型项目中,不同的模块或库可能会定义相同名字的变量、函数或类。这会导致程序不知道该调用哪一个。例如:
#include <iostream>
using namespace std;void print() {cout << "Global print function" << endl;
}namespace ModuleA {void print() {cout << "ModuleA's print function" << endl;}
}int main() {print(); // 问题:调用的是全局的 print 还是 ModuleA 的 print?return 0;
}
/*
输出:
Global print function
*/
这种情况下,同名的 print
函数可能引发歧义,导致意外的错误行为。
2. 什么是命名空间(namespace)?
2.1 定义
命名空间是 C++ 提供的一种机制,用来组织代码,解决名字冲突问题。
通过命名空间,程序员可以为变量、函数或类添加“所属空间”的限定,避免与其他模块中的名字冲突。
2.2 语法格式
namespace [命名空间的名字]{// 定义变量、函数、类
}
namespace_name
是命名空间的名字。- 命名空间的成员通过
namespace_name::member
访问。
3. 使用命名空间解决名字冲突
3.1 基本使用
将同名的函数或变量放入不同的命名空间,可以解决名字冲突问题:
#include <iostream>
using namespace std;namespace ModuleA {void print() {cout << "ModuleA's print function" << endl;}
}namespace ModuleB {void print() {cout << "ModuleB's print function" << endl;}
}int main() {ModuleA::print(); // 调用 ModuleA 的 print 函数ModuleB::print(); // 调用 ModuleB 的 print 函数return 0;
}
/*
输出:
ModuleA's print function
ModuleB's print function
*/
解释:
- 将
print()
函数分别放在ModuleA
和ModuleB
命名空间中。 - 通过
ModuleA::print()
或ModuleB::print()
调用,避免了名字冲突。
3.2 嵌套命名空间
命名空间可以嵌套使用,用于组织更复杂的代码:
#include <iostream>
using namespace std;namespace Outer {namespace Inner {void print() {cout << "Inner namespace print function" << endl;}}
}int main() {Outer::Inner::print(); // 调用嵌套命名空间中的函数return 0;
}
/*
输出:
Inner namespace print function
*/
注意:
- 嵌套命名空间使代码结构更清晰,但调用成员时需要完整的命名空间路径。
4. 使用命名空间中的成员
4.1 使用 using
声明
通过 using namespace
声明,可以简化命名空间成员的调用:
#include <iostream>
using namespace std;namespace ModuleA {void print() {cout << "ModuleA's print function" << endl;}
}int main() {using namespace ModuleA; // 引入 ModuleA 命名空间print(); // 直接调用 ModuleA 的 print 函数return 0;
}
/*
输出:
ModuleA's print function
*/
4.2 注意点
-
如果多个命名空间中有同名成员,不能直接使用
using namespace
,需要明确调用:using namespace ModuleA; using namespace ModuleB; print(); // 错误:不明确调用哪个命名空间的 print
-
可以使用
using
声明单个成员:using ModuleA::print; // 只引入 ModuleA 的 print 函数 print();
5. 无命名空间的情况
5.1.问题
在没有命名空间的情况下,同名冲突会导致程序错误。例如:
#include <iostream>
using namespace std;void print() {cout << "Global print function" << endl;
}void print() { // 重复定义cout << "Duplicate print function" << endl;
}int main() {print(); // 错误:重复定义导致编译错误return 0;
}
5.2.解决
将函数放入不同的命名空间即可避免冲突。
6. 标准命名空间 std
6.1 定义
C++ 标准库中的所有内容都定义在命名空间 std
中,例如 cout
、cin
、string
等。
6.2使用方法
-
通过
std::
使用标准库成员:std::cout << "Hello, World!" << std::endl;
-
或者引入
std
命名空间:using namespace std; cout << "Hello, World!" << endl;
6.3注意:
- 在大型项目中,避免使用
using namespace std;
,因为可能会与用户定义的名字冲突。
7. 总结
-
同名冲突:
- 不同模块可能定义相同名字的变量、函数或类,导致冲突。
-
命名空间:
- 通过
namespace
将代码分组,解决名字冲突。 - 成员访问语法:
namespace_name::member
。
- 通过
-
using
的使用:- 使用
using namespace
简化命名空间成员的调用,但要注意避免歧义。
- 使用
-
标准命名空间:
std
是 C++ 标准库的命名空间,包含cout
、cin
等标准成员。
三、总结
1.异常处理
- 异常处理通过 try-catch 机制捕获和处理异常,防止程序崩溃。
- 使用
throw
抛出异常,catch
捕获异常。 - 可自定义异常类,用于处理复杂错误。
2.命名空间
- 命名空间用于组织代码,避免命名冲突。
- 使用
namespace_name::identifier
访问命名空间中的成员。 - 可以使用
using namespace
简化调用。