博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++ Primer Plus 学习笔记 第十五章 异常 abort() 返回错误提示,try-throw-catch exception和其派生类 RTTI 类型转换运算符
阅读量:4127 次
发布时间:2019-05-25

本文共 15961 字,大约阅读时间需要 53 分钟。

abort() 来源于cstdlib 或 stdlib.h中 手动调用该函数来终止程序。 程序会跳出如下信息然后程序终止

untenable arguments to hmean()This application has requested the Runtime to terminate it in an unusual way.Please contact the application's support team for more information.

或者使用手动判断异常的方式 免去程序强制退出

error2.cpp

#include 
#include
bool hmean(double a, double b, double * ans);int main(){ double x, y, z; std::cout << "Enter two numbers: "; while (std::cin >> x >> y) { if (hmean(x, y, &z)) std::cout << "Hearmonic mean of " << x << " and " << y << " is " << z << std::endl; else std::cout << "One value should not be the negative " << "of the other - try again.\n"; std::cout << "Enter next set of numbers
: "; } std::cout << "Bye!\n"; return 0;}bool hmean(double a, double b, double * ans){ if (a == -b) {// 程序示例是这样 但是这句表达式有毛用 至今看不出来 *ans = DBL_MAX; return false; } else { *ans = 2.0 * a * b / ( a + b); return true; }}

异常机制三部曲

引发异常,使用处理程序捕获异常,使用try块

这个在大部分的编程语言中都通用

throw() 抛异常

try() 可能会引发异常的代码块

catch() 异常捕获处理。

这个在JAVA Python中是一样的 只是关键字有区别

程序示例:

#include 
double hmean(double a, double b);int main(){ double x, y, z; std::cout << "Enter two numbers: "; while(std::cin >> x >> y) { try{ z = hmean(x, y); } catch(const char * s) { std::cout << s << std::endl; std::cout << "Enter a new pair of numbers: "; continue; } std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl; std::cout << "Enter next set of numbers
: "; } std::cout << "Bye!\n";}double hmean(double a, double b){ if (a == -b) throw "bad hmean() arguments: a = -b not allowed"; return 2.0 * a * b / (a + b);}

如果没有捕获 C++ 默认调用abort()

将对象用作异常类型:

exc_mean.h

#include 
// bad_hmean异常是判断是否为相反数// bad_gmean异常是判断参数是否小于0class bad_hmean{ private: double v1; double v2; public: bad_hmean(double a = 0, double b = 0) : v1(a), v2(b) {} void mesg();};inline void bad_hmean::mesg(){ std::cout << "hmean(" << v1 << ", " << v2 << "): " << "invalid arguments: a = -b\n";}class bad_gmean{ public: double v1; double v2; bad_gmean(double a = 0, double b = 0) : v1(a), v2(b) {} const char* mesg();};inline const char* bad_gmean::mesg(){ return "gmean() arguments should be >= 0\n";}

error4.cpp

#include 
#include
#include "exc_mean.h"// 在各自函数中引发异常double hmean(double a, double b);double gmean(double a, double b);int main(){ using std::cout; using std::cin; using std::endl; double x, y, z; cout << "Enter two numbers: "; while (cin >> x >> y) { try{ z = hmean(x, y); cout << "Harmonic mean of " << x << " and " << y << " is " << z << endl; cout << "Geometric mean of " << x << " and " << y << " is " << gmean(x, y) << endl; cout << "Enter next set of numbers
: "; }// 通过捕获异常对象的引用类型来确定是用哪个catch catch(bad_hmean & bg) { bg.mesg(); cout << "Try again.\n"; continue; } catch(bad_gmean & hg) { cout << hg.mesg(); cout << "Values used: " << hg.v1 << ", " << hg.v2 << endl; cout << "Sorry, you don't get to play any more.\n"; break; } } cout << "Bye!\n"; return 0;}double hmean(double a, double b){ if (a == -b) throw bad_hmean(a, b); return 2.0 * a * b / (a + b);}double gmean(double a, double b){ if (a < 0 || b < 0) throw bad_gmean(a, b); return std::sqrt(a * b);}

异常规范:

noexcept 指出所在行的代码不会出现异常 例如:

double marm() noexcept; 

但是 谁能保证呢  发誓都没用的  所以 C++11也建议不要用这玩意儿

同时还有一个函数 noexcept() 用来判断操作数是否会引发异常。

 

栈解退:

正常的函数调用是这样的

如果调用时有异常是这样的:

示例图

程序示例

exc_mean.h

#include 
class bad_hmean{ private: double v1; double v2; public: bad_hmean(double a = 0, double b = 0) : v1(a), v2(b) {} void mesg();};inline void bad_hmean::mesg(){ std::cout << "hmean(" << v1 << ", " << v2 << "): " << "invalid arguments: a = -b\n";}class bad_gmean{ public: double v1; double v2; bad_gmean(double a = 0, double b = 0) : v1(a), v2(b) {} const char* mesg();};inline const char* bad_gmean::mesg(){ return "gmean() arguments should be >= 0\n";}

error5.cpp

#include 
#include
#include
#include "exc_mean.h"// 此DEMO是用来展示程序执行到哪一步class demo{ private: std::string word; public: demo (const std::string & str) { word = str; std::cout << "demo " << word << " create\n"; } ~demo() { std::cout << "demo " << word << " destroyed\n"; } void show() const { std::cout << "demo " << word << " lives!\n"; }};double hmean(double a, double b);double gmean(double a, double b);double means(double a, double b);int main(){ using std::cout; using std::cin; using std::endl; double x, y, z; { demo d1("found in block in main()"); cout << "Enter two numbers: "; while(cin >> x >> y) { try{ z = means(x, y); cout << "The mean mean of " << x << " and " << y << " is " << z << endl; cout << "Enter next pair: "; } catch(bad_hmean & bg) { bg.mesg(); cout << "Try again.\n"; continue; } catch(bad_gmean & hg) { cout << hg.mesg(); cout << "Values used: " << hg.v1 << ", " << hg.v2 << endl; cout << "Soryy, you don't get to play any more.\n"; break; } } d1.show(); } cout << "Bye!\n"; cin.get(); cin.get(); return 0;}double hmean(double a, double b){ if (a == -b) throw bad_hmean(a, b); return 2.0 * a * b / (a + b);}double gmean(double a, double b){ if (a < 0 || b < 0) throw bad_gmean(a, b); return std::sqrt(a * b);}double means(double a, double b){ double am, hm, gm; demo d2("found in means()"); am = (a + b) / 2.0; try {// 这里会捕获两次异常 一次是means自身捕获的 还有一次是它抛出去的然后由main()捕获的 hm = hmean(a, b);// 这里如果出现异常直接抛给main() 因为当前函数中没有对此异常进行捕获 gm = gmean(a, b); } catch (bad_hmean & bg) { bg.mesg(); std::cout << "Caught in means()\n";// 这里抛给main() 一旦出现异常 函数后面的内容就不会执行。 如果函数有调用对象则直接调用解析函数 throw; } d2.show(); return (am + hm + gm) / 3.0;}

执行结果

程序说明

异常的

异常的其他特性;

异常抛出来的总是对象的拷贝 以免对象是在函数中创建的临时对象而被释放掉造成错误

异常捕获使用引用的原因是 可以引用异常的派生类 这样可以捕获一系列的同一基类的异常 

但是 catch的排列顺序要与派生顺序相反。

exception类 

是一个抽象异常类

通过重新定义该类的what()函数 返回相应的字符串

通过此类派生了stdexcept类

该类又派生出两个系列的基类

logic_error和runtime_error

logic_error:

runtime_error

bad_alloc异常和new

new导致的分配问题会引发bad_alloc异常 <new>中已经包含了该异常的声明

程序示例:

#include 
#include
#include
using namespace std;struct Big{ double stuff[20000];};int main(){ Big* pd; try{ cout << "Trying to get a big block of memory:\n"; pd = new Big[10000]; cout << "Got past the new request:\n"; } catch (bad_alloc & ba) { cout << "Caught the exception!\n"; cout << ba.what() << endl; exit(EXIT_FAILURE); } cout << "Memory successfully allocated\n"; pd[0].stuff[0] = 4; cout << pd[0].stuff[0] << endl; delete [] pd; return 0;}

 

该程序我没有正常调出bad_alloc异常 因为在编译的时候 编译器就告诉我太大了 [手动无奈]

如果希望new失败的时候返回的是空指针 就这么写

int* pi = new (std::nothrow) int;

异常,继承和类

嗯。说的就是普通的类中带有异常内部类。

上代码吧:

sales.h

#include 
#include
class Sales{ public: enum {MONTHS = 12};// 声明内部异常类 class bad_index: public std::logic_error { private: int bi; public: explicit bad_index(int ix, const std::string & s = "Index error in Sales object:\n"); int bi_val() const {return bi;} virtual ~bad_index() throw() {} }; explicit Sales(int yy = 0); Sales(int yy, const double * gr, int n); virtual ~Sales(){} int Year() const { return year;} virtual double operator[] (int i) const; virtual double & operator[] (int i); private: double gross[MONTHS]; int year;};class LabeledSales : public Sales{ public:// 声明内部异常类 class nbad_index : public Sales::bad_index { private: std::string lbl; public: nbad_index(const std::string & lb, int ix, const std::string & s = "Index error in LabeledSales object\n"); const std::string & label_val() const {return lbl;} virtual ~nbad_index() throw() {} }; explicit LabeledSales(const std::string & lb = "none", int yy = 0); LabeledSales(const std::string & lb, int yy, const double * gr, int n); virtual ~LabeledSales() {} const std::string & Label() const {return label;} virtual double operator[](int i) const; virtual double & operator[](int i); private: std::string label;};

sales.cpp

#include "sales.h"#include 
using std::string;Sales::bad_index::bad_index(int ix, const string & s) : std::logic_error(s), bi(ix){}Sales::Sales(int yy){ year = yy; for (int i = 0; i < MONTHS; ++i) gross[i] = 0;}Sales::Sales(int yy, const double * gr, int n){ year = yy; int lim = ( n < MONTHS) ? n : MONTHS; int i; for (i = 0; i < lim; ++i) gross[i] = gr[i]; for (; i < MONTHS; ++i) gross[i] = 0;}double Sales::operator[](int i) const{ if (i < 0 || i >= MONTHS) throw bad_index(i); return gross[i];}double & Sales::operator[](int i){ if (i < 0 || i >= MONTHS) throw bad_index(i); return gross[i];}LabeledSales::nbad_index::nbad_index(const string & lb, int ix, const string & s) : Sales::bad_index(ix, s){ lbl = lb;}LabeledSales::LabeledSales(const string & lb, int yy) : Sales(yy){ label = lb;}LabeledSales::LabeledSales(const string & lb, int yy, const double * gr, int n) : Sales(yy, gr, n){ label = lb;}double LabeledSales::operator[](int i) const{ if (i < 0 || i >= MONTHS) throw nbad_index(Label(), i); return Sales::operator[](i);}double & LabeledSales::operator[](int i){ if (i < 0 || i >= MONTHS) throw nbad_index(Label(), i); return Sales::operator[](i);}

user_sales.cpp

#include 
#include "sales.h"int main(){ using std::cout; using std::cin; using std::endl; double vals1[12] = { 1220, 1100, 1122, 2212, 1232, 2334, 1884, 2393, 3302, 2922, 3002, 3544 }; double vals2[12] = { 12, 11, 22, 21, 32, 34, 28, 29, 33, 29, 32, 35 }; Sales sales1(2011, vals1, 12); LabeledSales sales2("Blogstar", 2012, vals2, 12); cout << "First try block:\n"; try { int i; cout << "Year = " << sales1.Year() << endl; cout << "Label = " << sales2.Label() << endl;// 这里迭代的时候到12下标就溢出 这里会捕获到异常 for ( i = 0; i <= 12; i++) { cout << sales2[i] << " "; if (i % 6 == 5) cout << endl; } cout << "End of try block 1.\n"; } catch(LabeledSales::nbad_index & bad) { cout << bad.what(); cout << "Company: " << bad.label_val() << endl; cout << "bad index: " << bad.bi_val() << endl; } catch(Sales::bad_index & bad) { cout << bad.what(); cout << "bad index: " << bad.bi_val() << endl; } cout << "done\n"; return 0;}

未捕获的异常和意外终止的异常

未捕获的异常系统会调用terminate()函数,该函数默认会调用abort()终止程序。

可以设置该函数使其执行自定义的函数

// terminate函数来源于exception#include 
using namespace std;// 设置terminate函数调用的是指定的函数set_terminate(myQuit);void myQuit(){cout << "Terminating due to uncaught excetion\n";exit(5);}

意外发生的异常

可以指定异常贵方来指定捕获哪些异常:

// 声明抛出的异常类, 这是异常规范double Argh(double, double) throw(out_of_bounds);try{    x = Argh(a, b);}// 在使用Argh函数的地方捕获指定异常catch(out_of_bounds & ex){    ...}

这样很麻烦 如果是在二开祖传代码 鬼知道会爆出什么过异常  如果都按照异常规范来操作的话 一来可能异常会有很多,这样写估计要写两大行。二来 咱这么详细 总会有漏 。所以 C++11开始要抛弃这种规范。

这种意外导致的异常C++是调用unexpected()函数(名字很奇怪) 然后该函数调用terminate() 然后该函数默认调用abort()

同样 也有set_unexpected()函数 用来自定义意外异常函数

但是此方法与set_terminate()不一样,有限制:

意思就是 set_unexpected()中如果是引发异常 那就看该异常与throw()中的异常是不是一样 如果是的话就就用该throw()中的异常处理,也就是说将意外情况引导到指定的catch中。

如果异常不在异常规范中,且规范中没有std::bad_exception类型的异常 则调用terminate()

如果异常不在异常规范中,但是规范中有std::bad_exception类型的异常。 那将会抛std::bad_exception异常

通用方法:

#include 
using namespace std;set_unexpected(myUnexpected);void myUnexpected(){ // 或者 throw(); throw std::bad_exception();}double Argh(double, double) throw(out_of_bounds, bad_exception);...try{ x = Argh(a, b);}catch(out_of_bounds & ex){ ...}catch(bad_exception & ex){ ...}

异常注意事项

不要在模板中使用异常规范  因为涉及内存的动态分配问题

解决方案就是讲delete [] ar;反倒抛异常的语句前面

这说的是WINDOWS是么 - -

RTTI

运行阶段类型识别。 用来确定对象类型的

有三个元素

dynamic_cast:用来判断某个对象指针是否可以安全的转换为另一个对象的指针。如果可以,正常转换,如果不行,则返回空指针。格式:

Superb * pm = dynamic_cast<Superb *>(pg);

代码示例:

#include 
#include
#include
using std::cout;class Grand{ private: int bold; public: Grand(int h = 0) : bold(h) {} virtual void Speak() const {cout << "I am a grand class!\n";} virtual int Value() const {return bold;}};class Superb : public Grand{ public: Superb(int h = 0) : Grand(h) {} void Speak() const {cout << "I am superb class!!\n";} virtual void Say() const { cout << "I hold the superb value of " << Value() << "!\n";}};class Magnificent : public Superb{ private: char ch; public: Magnificent(int h = 0, char c = 'A') :Superb(h), ch(c) {} void Speak() const {cout << "I am a magnificent class!!!\n";} void Say() const {cout << "I hold the character " << ch << " and the integer " << Value() << "!\n";}};Grand * GetOne();int main(){ std::srand(std::time(0)); Grand * pg; Superb * ps; for ( int i = 0; i < 5; i++) { pg = GetOne(); pg->Speak(); if (ps = dynamic_cast
(pg)) ps->Say(); } return 0;}Grand * GetOne(){ Grand* p; switch( std::rand() % 3) { case 0: p = new Grand(std::rand() % 100); break; case 1: p = new Superb(std::rand() % 100); break; case 2: p = new Magnificent(std::rand() % 100, 'A' + std::rand() % 26); break; } return p;}

typeid运算符

判断两个对象是否同种类型

例子:

typeid(Magnificent) == typeid(*pg)(接收的参数可以是类名和结果为对象的表达式)

结果为true, 如果不同则返回false

如果pg是空指针 会引发bad_typeid异常。

type_info类:

程序示例

#include 
#include
#include
#include
using namespace std;class Grand{ private: int bold; public: Grand(int h = 0) : bold(h) {} virtual void Speak() const {cout << "I am a grand class!\n";} virtual int Value() const {return bold;}};class Superb : public Grand{ public: Superb(int h = 0) : Grand(h) {} void Speak() const {cout << "I am superb class!!\n";} virtual void Say() const { cout << "I hold the superb value of " << Value() << "!\n";}};class Magnificent : public Superb{ private: char ch; public: Magnificent(int h = 0, char c = 'A') :Superb(h), ch(c) {} void Speak() const {cout << "I am a magnificent class!!!\n";} void Say() const {cout << "I hold the character " << ch << " and the integer " << Value() << "!\n";}};Grand * GetOne();int main(){ std::srand(std::time(0)); Grand * pg; Superb * ps; for ( int i = 0; i < 5; i++) { pg = GetOne(); cout << "Now processing type " << typeid(*pg).name() << ".\n"; pg->Speak(); if(ps = dynamic_cast
(pg)) ps->Say(); if (typeid(Magnificent) == typeid(*pg)) cout << "Yes, you're really magnificent.\n"; } return 0;}Grand * GetOne(){ Grand* p; switch( std::rand() % 3) { case 0: p = new Grand(std::rand() % 100); break; case 1: p = new Superb(std::rand() % 100); break; case 2: p = new Magnificent(std::rand() % 100, 'A' + std::rand() % 26); break; } return p;}

类型转换运算符

为了更严格的定义类型转换

C++添加了以下4中运算符

dynamic_cast: 这个前面说过了 就是转换前判定是否能够安全转换对象类型

const_cast: 这个只用于一次性。除了将const转换成非const(或转成volatile类型)使用之外 转换成其他类型的都将抛出异常

                        这个只针对非const 传入到要求const的函数形参中 ,如果本身是const变量的话那是返回空指针

程序示例:

#include 
using std::cout;using std::endl;void change(const int * pt, int n);int main(){ int pop1 = 38383; const int pop2 = 2000; cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl; change(&pop1, -103); change(&pop2, -103); cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl; return 0;}void change(const int * pt, int n){ int* pc; pc = const_cast
(pt); *pc += n;}

运行结果

static_cast: 这个是检测用于被转换的类与将要转换成的类之间有没有关联  是不是基-派生 或者间接基类,间接派生 如果是两个完全不相干的类 则会抛异常

reinterpret_cast:这种是用于转换一些莫名其妙但是有时候又用得到的类型转换:看例子吧

正常不这么干吧 这样转换 好奇怪。

C++中转换的限制:

总结:

转载地址:http://ciepi.baihongyu.com/

你可能感兴趣的文章
tp5令牌数据无效 解决方法
查看>>
自己的网站与UCenter整合(大致流程)
查看>>
laravel 制作通用的curd 后台操作
查看>>
【小红书2017年笔试】求一个数组中平均数最大的子数组
查看>>
Linux基础系列-定时器与时间管理
查看>>
Linux基础系列-可执行程序的产生过程
查看>>
Linux基础系列-Kernel 初始化宏
查看>>
Linux子系统系列-I2C
查看>>
<iOS>关于自定义description的一点用法
查看>>
Unix 命令,常用到的
查看>>
DLL中建立进程共享数据段需要注意的语法问题
查看>>
服务器端技术----Http请求的处理过程
查看>>
C语言-预处理指令2-条件编译
查看>>
C语言-预处理指令3-文件包含
查看>>
C语言-变量类型
查看>>
C语言-static和extern关键字1-对函数的作用
查看>>
C 语言-static和extern关键字2-对变量的作用
查看>>
【JavaScript 教程】浏览器—History 对象
查看>>
还不会正则表达式?看这篇!
查看>>
100道+ JavaScript 面试题,助你查漏补缺
查看>>