当前位置: 首页 > news >正文

C++异常、嵌套类

一、异常

1.概念

C++中的异常机制是一种处理程序运行时错误的重要手段,它允许程序在遇到错误时,立即停止当前的执行流程,并跳转到预先设定好的处理错误代码的地方。

使用异常机制可以避免程序因为错误而崩溃,提高程序的稳定性和可靠性。

 

2.语法

异常的抛出和捕获是通过throw和try-catch语句来实现的。

①throw语句:

异常通常在程序执行过程中遇到无法处理的错误时抛出,例如除零错误、内存访问错误、数组下标越界等。

当这些错误发生时,可以使用throw语句来抛出一个异常。这个异常通常是一个对象,它包含了关于异常的详细信息。异常对象可以是任何类型,但通常是从exception类或其子类派生的对象。

throw语句的实际作用是跳转,即命令程序跳到另一条语句。

②try-catch语句:

try语句:try块中包含可能抛出异常的代码。如果在这段代码中抛出了异常,那么控制权将被转移到与之异常类型匹配的catch块中。

catch语句:catch块用于捕获和处理异常。catch块可以有多个,用于捕获不同类型的异常。

catch(...)语句可以捕获所有类型的异常,通常放在异常处理程序的最后,以确保至少有一个catch块能够处理未被其它catch块捕获的异常。

示例:

double divide(double a, double b) {if (b == 0)throw "除数不能为0";return a / b;
}
try
{double x = divide(5, 0);
}
catch (const char* s)
{cout << s << endl;  //除数不能为0
}
catch (...)
{cout << "error" << endl;
}

 

3.匹配规则

①关于catch块:

  • 异常被捕获后,执行对应的catch块,然后继续执行try-catch之后的代码。如果抛出的异常对象未被捕获或者没有匹配正确的类型,那么程序会终止并报错。
  • 被选中的catch块是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。

抛出异常对象后,会生成该异常对象的拷贝,因为抛出的可能是一个临时对象。在catch块被调用后,这个拷贝的对象将被销毁。

②栈解退:

栈解退是处理程序异常时的一种机制,它允许程序在遇到异常时,从异常点开始,自动释放调用序列中所创建的对象和临时变量。这一过程不仅涉及栈的清理,还包括为这些对象调用析构函数,确保资源的正确释放。

当一个函数抛出异常时,程序会沿着调用栈向上查找,直到找到一个能够处理该异常的catch块或者到达程序的最顶层(main函数)。如果在main函数中仍然找不到合适的catch块,程序会调用terminate函数,导致程序终止。

栈解退机制确保了即使程序在执行过程中遇到异常,也能够安全退出并释放所占用的资源,防止内存泄漏和其它资源管理问题。

示例:

class Resource {
public:Resource() {cout << "Resource()" << endl;}~Resource() {cout << "~Resource()" << endl;}
};
void func2() {throw runtime_error("runtime_error");
}
void func1() {Resource r;func2();
}
try
{func1();
}
catch (const runtime_error& e)
{cout << e.what() << endl;
}
catch (...)
{cout << "error" << endl;
}
//输出
Resource()
~Resource()
runtime_error

 

4.抛出父类对象的引用

①为何抛出引用:

既然throw语句会生成副本,为什么catch语句中要使用引用呢?毕竟,将引用作为返回值通常是为了避免创建副本以提高效率。

答案是,引用还有另一个重要特征:父类引用可以指向子类对象。

②继承和多态:

抛出父类异常的引用意味着在捕获异常时,子类异常对象可以被父类异常对象的catch块捕获。

如果一个继承结构要分别处理不同的异常类型,则使用父类引用可以捕获任何异常对象,而使用子类引用只能捕获它自己的异常对象。

③排列catch块:

如果有一个异常类继承层次结构,应这样排列catch块:

将捕获子类的catch语句放在最前面,将捕获父类异常的catch语句放在最后面。

示例:

class Base {};class Derived :public Base {};void func() {Derived d;throw d;
}
try {func();
}
/*
catch (const Derived& d) {cout << "Derived" << endl;
}
*/
catch (const Base& b) {cout << "Base" << endl;
}
catch (...) {cout << "error" << endl;
}
//输出Base

 

5.标准库

C++中的标准库提供了一组异常类,用于在程序中处理异常。这些异常类构成了一个层次结构,可以针对性地处理不同的错误类型。

①常用的异常类:

  • exception:所有标准异常类的父类,提供了一个虚函数what(),用于返回异常的描述信息。
  • runtime_error:用于表示程序运行时遇到的意外情况,如找不到文件或内存不足。
  • logic_error:用于表示程序内部逻辑错误,例如违反了程序的预设条件。
  • bad_alloc:表示内存分配失败,例如使用new导致的内存分配问题。

②局限性:

  • 标准库中的异常类通常只提供笼统的错误信息,而不是具体的错误类型。
  • 在调试时难以确定错误发生的具体位置和原因。
  • 标准库中的异常类有时不足以描述特殊的错误和异常情况。

示例:

try {throw bad_alloc();
}
catch (const bad_alloc& b) {cout << "bad_alloc error" << endl;cout << b.what() << endl;
}
catch (...) {cout << "error" << endl;
}
//输出
bad_alloc error
bad allocation

 

6.自定义异常

C++允许自定义特定的异常类型来处理程序中特定的错误或异常情况。

①步骤:

创建一个继承自exception的自定义异常类,通过重写what()函数来提供异常的描述信息。

②示例:

class MyException :public exception {
public:virtual const char* what() const noexcept override {return "除数不能为0";}
};double divide(double a, double b) {if (b == 0)throw MyException();return a / b;
}
try {divide(3, 0);
}
catch (const MyException& e) {cout << e.what() << endl;
}
catch (...) {cout << "error" << endl;
}
//输出
除数不能为0

 

7.异常安全

  • 在构造函数中抛出异常可能会导致对象不完整或不完全初始化。
  • 在析构函数中抛出异常可能会导致内存泄露。
  • 可以使用RAII(资源获取即初始化)机制,通过智能指针自动管理资源,确保资源在正确的地方释放,避免内存泄漏。

noexcept关键字用于声明函数不会抛出异常。

 

 

二、嵌套类

1.概念

一个类可以定义在另一个类的内部,该类被称为嵌套类。

嵌套类是独立的类,它不属于外部类,只是它的定义被放在了外部类的内部。

嵌套类的作用如下:

  • 嵌套类可以将那些与外部类相关但不希望暴露给外部的类进行隐藏。
  • 当一些类在逻辑上和其它类紧密相关时,可以使用嵌套类进行逻辑分组。

 

2.作用域

嵌套类的作用域与其在外部类中声明的位置有关。

嵌套类的实例化对象可以作为外部类的数据成员,也可以作为外部类的成员函数的参数或者在成员函数内部实例化。

①声明为private:

如果嵌套类被定义为私有成员,那么它只能在外部类的内部使用。

②声明在为protected:

如果嵌套类被定义为保护成员,那么它可以在外部类及其子类中使用。

③声明为public:

如果嵌套类被定义为公有成员,那么它可以在外部类的外部使用,但需要通过外部类的名字来限定。

示例:

class Outer {
public:class Inner {};void func(Inner& i) {Inner inner;}Inner inner;
};class DeOuter :public Outer {
private:Inner i;
};void test01() {Outer::Inner inner;
}

 

3.访问权限

嵌套类实际上是外部类的友元类。嵌套类可以访问外部类的所有成员,包括私有成员。

然而,外部类不能直接访问嵌套类的私有成员。如果想要访问,通常需要提供公共接口或使用友元关系。

示例:

class Outer {
private:int data;
public:class Inner {private:int data;public:void func(Outer& out) {cout << out.data << endl;  //可直接访问}friend class Outer;};Inner inner;void print() {cout << inner.data << endl;  //若不是友元,则无法访问}
};

4.静态嵌套类

静态嵌套类是一种特殊的嵌套类,它不依赖于外部类的实例对象。

静态嵌套类特点:

  • 不能访问外部类的非静态成员。
  • 不需要通过外部类的实例对象就能直接访问其外部类的静态成员。
  • 不需要创建外部类的实例,可以直接通过类名访问。
class Outer {
private:static int data;int value;
public:static class Inner {public:void print() {cout << data << endl;//cout << value << endl;  //无法访问}static int value;};};
int Outer::data = 10;
int Outer::Inner::value = 20;
Outer::Inner inner;
inner.print();  //10
cout << Outer::Inner::value << endl;  //20

 

 

 

 

 


http://www.mrgr.cn/news/60265.html

相关文章:

  • axure中继器
  • 解决:git SSL certificate problem: unable to get local issuer certificate
  • 用kali入侵 DarkHole_2测试
  • 如何在vscode中使用鼠标滑轮滚动来改变字体大小
  • Java - 人工智能;SpringAI
  • dy a_bogus 1.0.1.17 最新版本补环境 分析
  • 基于SSM少儿编程管理系统的设计
  • 帝佛卡干邑荣耀登陆泰国王权King Power
  • MPP音视频总结
  • 如何通过自签名证书让本地环境变为 https
  • 高通学习1-TLMM(TODO)
  • 深度学习并行训练算法一锅炖: DDP, TP, PP, ZeRO
  • 鸿蒙开发融云demo录制语音消息
  • 转换手机录音文件为文本
  • 鸿蒙生态开发以及技术栈介绍
  • 第三十二篇:TCP协议粘包和滑动窗口,TCP系列七
  • 贷款有门道:白名单和黑名单,线上线下申请,你都知道吗?
  • [mysql]多行子查询(只包含不相关子查询案例)
  • Kotlin学习第三课
  • Linux 重启命令全解析:深入理解与应用指南
  • 【代码优化Tip】关于结构
  • 设计模式——备忘录模式
  • gitlab如何重置密码
  • APP闪退原因
  • 【Spring MVC】请求参数的传递
  • 算力中心四大类型