第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):上是这么描述的
-
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.
-
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.
逻辑错误
- 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()括号中的参数是某个类继承链中不同层次的类的对象,此时括号中的参数可否不用引用类型?为什么?
答:
-
可以不用引用类型。
-
如果在try块里面抛出的是exception指针,那么catch的异常参数也可以是指针。但是由于内存管理等等问题,c++ primer推荐用reference。
-
如果一个派生类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;
}
}