崔毅东 C++程序设计入门(下) 第9单元:白公曾咏牡丹芳,一种鲜妍独“异常” 笔记

本文深入探讨C++的异常处理机制,包括异常类的使用、自定义异常、多重异常捕获、异常传播和重抛异常。通过实例代码解析了runtime_error与logic_error的区别,并提供了形象的异常传播例子。
摘要由CSDN通过智能技术生成

第01节:概览异常

在这里插入图片描述在这里插入图片描述
QuotientWithException.cpp

#include <iostream>
using namespace std;

int main() {
  // Read two intergers
  cout << "Enter two integers: ";
  int number1, number2;
  cin >> number1 >> number2;

  try {
    if (number2 == 0)
      throw number1;
      
    cout << number1 << " / " << number2 <<
            " is "  << (number1 / number2) << endl;
  }
  catch (int e) {
    cout << "Exception: an integer " << e <<
            " cannot be divided by zero" << endl;
  }

  cout << "Execution continues ..." << endl;
  
  return 0;
}

在这里插入图片描述
在这里插入图片描述

第02节:异常处理机制的优点

在这里插入图片描述在这里插入图片描述
QuotientWithFunction.cpp

#include <iostream>
using namespace std;

int quotient(int number1, int number2) {
  if (number2 == 0)
    throw number1;

  return number1 / number2;
}

int main() {
  // Read two intergers
  cout << "Enter two integers: ";
  int number1, number2;
  cin >> number1 >> number2;

  try {
    int result = quotient(number1, number2);
    cout << number1 << " / " << number2 << " is "
      << result << endl;
  }
  catch (int e) {
    cout << "Exception from function: an integer " << e <<
            " cannot be divided by zero" << endl;
  }

  cout << "Execution continues ..." << endl;
}

第03节:异常类

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

#include <iostream>
using namespace std;

int main() {
  try   {
    for (int i = 1; i <= 100; i++)  {
      new int[70000000];
      cout << i << " arrays have been created" << endl;
    }
  }
  catch (bad_alloc &e) {
    cout << "Exception: " << e.what() << endl;
  }
  return 0;
}

在这里插入图片描述

#include <typeinfo>
#include "DerivedCircle.h"
#include "Rectangle.h"
#include <iostream>
using namespace std;

int main() {
  try   {
    Rectangle r(3.0, 4.0);
    Circle & c = dynamic_cast<Circle&>(r);
  }
  catch (bad_cast &e)   {
    cout << "Exception: " << e.what() << endl;
  }
}

在这里插入图片描述

#include <typeinfo>
#include <iostream>
#include <stdexcept>
using namespace std;

int quotient(int number1, int number2)
{
  if (number2 == 0)
    throw runtime_error("Runtime error in quotient");

  return number1 / number2;
}

int main()
{
  // Read two intergers
  cout << "Enter two integers: ";
  int number1, number2;
  cin >> number1 >> number2;

  try
  {
    int result = quotient(number1, number2);
    cout << number1 << " / " << number2 << " is "
      << result << endl;
  }
  catch (exception &e)
  {
    cout << "Exception: " << e.what() << endl;
    cout << "Exception type: " << typeid(e).name() << endl;
  }

  cout << "Execution continues ..." << endl;
  return 0;
}

在这里插入图片描述
u09s03 - runtime_error与logic_error的使用区别是什么?
你能说说 runtime_error 与 logic_error 这两个异常的主要区别是什么?

何时应该使用 runtime_error 而何时又该使用 logic_error?
答:C++0x Standard says (clause 19.2):上是这么描述的

  1. In the error model reflected in these classes (i.e. the exception types), errors are divided into two broad categories: logic errors and runtime errors.

  2. The distinguishing characteristic of logic errors is that they are due to errors in the internal logic of the program. In theory, they are preventable.

逻辑错误

  1. By contrast, runtime errors are due to events beyond the scope of the program. They cannot be easily predicted in advance.

程序的作用范围造成的( the “scope” is defined as the library code’s expectations, not your program’s design)

在Cpp标准上也有这样的描述:

logic_error
“The class logic_error defines the type of objects thrown as exceptions to report errors presumably detectable before the program executes, such as violations of logical preconditions or class invariants.”

在程序执行前,去对一个逻辑上的先决条件进行判断

runtime_error
“The class runtime_error defines the type of objects thrown as exceptions to report errors presumably detectable only when the program executes.”

在程序执行的过程中,报出错误。

比如,runtime_error的overflow_error是在程序进行计算(运行)的时候发现的。

而,logic_error的out_of_range,以vector为例,虽然也是程序执行过程中发现的,但是并没有去取vector中真实数据,而是先检查了这个“取操作”是否可行。

(by wuqijssn)

第04节:自定义异常类

在这里插入图片描述

在这里插入图片描述在这里插入图片描述
Triangle.h

#include "AbstractGeometricObject.h"
#include "TriangleException.h"
#include <cmath>

class Triangle: public GeometricObject
{
public:
  Triangle(double side1, double side2, double side3)
  {
    if (!isValid(side1, side2, side3))
      throw TriangleException(side1, side2, side3);

    this->side1 = side1;
    this->side2 = side2;
    this->side3 = side3;
  }

  double getSide1()
  {
    return side1;
  }

  double getSide2()
  {
    return side2;
  }

  double getSide3()
  {
    return side3;
  }

  double setSide1(double side1)
  {
    if (!isValid(side1, side2, side3))
      throw TriangleException(side1, side2, side3);

    this->side1 = side1;
  }

  double setSide2(double side2)
  {
    if (!isValid(side1, side2, side3))
      throw TriangleException(side1, side2, side3);

    this->side2 = side2;
  }

  double setSide3(double side3)
  {
    if (!isValid(side1, side2, side3))
      throw TriangleException(side1, side2, side3);

    this->side3 = side3;
  }

  double getPerimeter()
  {
    return side1 + side2 + side3;
  }

  double getArea()
  {
    double s = getPerimeter() / 2;
    return sqrt(s * (s - side1) * (s - side2) * (s - side3));
  }

private:
  double side1, side2, side3;

  bool isValid(double side1, double side2, double side3)
  {
    return (side1 < side2 + side3) && (side2 < side1 + side3) &&
      (side3 < side1 + side2);
  }
};

TriangleException.h

#include <stdexcept>
using namespace std;

class TriangleException: public logic_error
{
public:
  TriangleException(double side1, double side2, double side3)
    : logic_error("Invalid triangle")
  {
    this->side1 = side1;
    this->side2 = side2;
    this->side3 = side3;
  }

  double getSide1()
  {
    return side1;
  }

  double getSide2()
  {
    return side2;
  }

  double getSide3()
  {
    return side3;
  }

private:
  double side1, side2, side3;
};

TestTriangle.cpp

#include <iostream>
#include "Triangle.h"
using namespace std;

int main() {
  try
  {
    Triangle triangle(2, 1, 1);
    cout << triangle.getPerimeter() << endl;
    cout << triangle.getArea() << endl;
  }
  catch (TriangleException &ex)
  {
    cout << ex.getSide3();
  }
}

在这里插入图片描述在这里插入图片描述

第05节:多重异常捕获

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
NonPositiveSideException.h

#include <stdexcept>
using namespace std;

class NonPositiveSideException
  : public logic_error {
private:
  double side;

public:
  NonPositiveSideException(double side)
    : logic_error("Non-positive side") {
    this->side = side;
  }

  double getSide() {
    return side;
  }
};

NewTriangle.h

#include "AbstractGeometricObject.h"
#include "TriangleException.h"
#include "NonPositiveSideException.h"
#include <cmath>

class Triangle: public GeometricObject
{
public:
  Triangle()
  {
    side1 = side2 = side3 = 1;
  }

  Triangle(double side1, double side2, double side3)
    throw (NonPositiveSideException, TriangleException)
  {
    check(side1);
    check(side2);
    check(side3);

    if (!isValid(side1, side2, side3))
      throw TriangleException(side1, side2, side3);

    this->side1 = side1;
    this->side2 = side2;
    this->side3 = side3;
  }

  double getSide1()
  {
    return side1;
  }

  double getSide2()
  {
    return side2;
  }

  double getSide3()
  {
    return side3;
  }

  void setSide1(double side1)
  {
    check(side1);
    if (!isValid(side1, side2, side3))
      throw TriangleException(side1, side2, side3);

    this->side1 = side1;
  }

  void setSide2(double side2)
  {
    check(side2);
    if (!isValid(side1, side2, side3))
      throw TriangleException(side1, side2, side3);

    this->side2 = side2;
  }

  void setSide3(double side3)
  {
    check(side3);
    if (!isValid(side1, side2, side3))
      throw TriangleException(side1, side2, side3);

    this->side3 = side3;
  }

  double getPerimeter()
  {
    return side1 + side2 + side3;
  }

  double getArea()
  {
    double s = getPerimeter() / 2;
    return sqrt(s * (s - side1) * (s - side2) * (s - side3));
  }

private:
  double side1, side2, side3;

  bool isValid(double side1, double side2, double side3)
  {
    return (side1 < side2 + side3) && (side2 < side1 + side3) &&
      (side3 < side1 + side2);
  }

  void check(double side) throw (NonPositiveSideException)
  {
    if (side <= 0)
      throw NonPositiveSideException(side);
  }
};

MultipleCatchDemo.cpp

#include <iostream>
#include "NewTriangle.h"
using namespace std;

int main()
{
  try
  {
    cout << "Enter three sides: ";
    double side1, side2, side3;
    cin >> side1 >> side2 >> side3;
    Triangle triangle(side1, side2, side3);
    cout << "Perimeter is " << triangle.getPerimeter() << endl;
    cout << "Area is " << triangle.getArea() << endl;
  }
  catch (NonPositiveSideException &ex)
  {
    cout << ex.what();
    cout << " the side is " << ex.getSide() << endl;
  }
  catch (TriangleException &ex)
  {
    cout << ex.what();
    cout << " three sides are " << ex.getSide1() << " "
      << ex.getSide2() << " " << ex.getSide3() << endl;
  }
}

在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述
u09s05 - catch块的参数类型可以不用引用类型吗?
我们给的很多例子中, catch () 括号中的异常对象参数都是引用类型。

catch () 括号中的异常对象参数可否不用引用类型?

catch () 括号中的异常对象参数可否使用指针类型,比如: catch (Exception* e)

在多重异常捕获的代码中,若几个catch()括号中的参数是某个类继承链中不同层次的类的对象,此时括号中的参数可否不用引用类型?为什么?
答:

  1. 可以不用引用类型。

  2. 如果在try块里面抛出的是exception指针,那么catch的异常参数也可以是指针。但是由于内存管理等等问题,c++ primer推荐用reference。

  3. 如果一个派生类Exception不被以引用传出到一个catch基类Exception块,那么有关派生类Exception的内容将会丢失,再将其抛出的时候就外部调用函数就只能以基类catch了。(by kevw22)

第06节:异常传播

在这里插入图片描述在这里插入图片描述在这里插入图片描述
u09s06 - 异常传播的形象例子?
你能给出一个比较形象的描述异常传播的例子吗?比如《果壳中的宇宙》?比如虫子钻洋葱?
答:比如DELL客户维修站,客户的灵越系列5547的屏幕出现了花屏,县城基站的维修不了,往市里,市里的小哥一看,卧槽,这什么问题,去找省里的老大哥,哎哟妈呀,这啥玩意了,不行,被操哭了,再去找总部的老头儿终于搞定了。exception发现了,然后解决方案一级一级往下传,因为县里,市里,省里的哥们都没辙,所以他们只能返回结果。(by 喵星折腾党党…)

第07节:重抛异常 以及 异常规约

在这里插入图片描述
RethrowExceptionDemo.cpp

#include <iostream>
#include <stdexcept>
using namespace std;

int f1() throw() {
  try {
    throw runtime_error("Exception in f1");
  }
  catch (exception& ex) {
    cout << "Exception caught in function f1" << endl;
    cout << ex.what() << endl;
    throw; // rethrow the exception
  }
}

int main() {
  try {
    f1();
  }
  catch (exception& ex) {
    cout << "Exception caught in function main" << endl;
    cout << ex.what() << endl;
  }
}

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值