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异常是判断参数是否小于0
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";
}
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异常
程序示例:
#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
代码示例:
#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++中转换的限制:
总结: