第十周 C++11新特性和C++高级主题
1.C++11新特性(一)
2.C++11新特性(二)
3.强制类型转换
4.异常处理
程序运行发生异常
程序运行中总难免发生错误
(1)数组元素的下标超界、访问NULL指针
(2)除数为0
(3)动态内存分配new需要的存储空间太大
……
引起这些异常情况的原因可能是:
(1)代码质量不高,存在BUG
(2)输入数据不符合要求
(3)程序的算法设计时考虑不周到
…….
发生异常怎么办
(1)不只是简单地终止程序运行
(2)能够反馈异常情况的信息:哪一段代码发生的、什么异常
(3)能够对程序运行中已发生的事情做些处理:取消对输入文件的改动、释放已经申请的系统资源
……
通常的做法是:在预计会发生异常的地方,加入相应的代码,但这种做法并不总是适用的
……//对文件A进行了相关的操作。
fun(arg, ……);//可能发生异常
……
调用者该如何知道fun(arg, …)是否发生异常?没有发生异常,可以继续执行;发生异常,应该在结束程序运行前还原对文件A的操作。但是,
fun(arg, …)是别人已经开发好的代码
fun(arg, …)的编写者不知道其他人会如何使用这个函数
fun(arg, …)会出现在表达式中,通过返回值的方式区分是否发生异常,不符合编写程序的习惯,而且可能发生多种异常,通过返回值判断也很麻烦。
那么,需要一种手段:(1)把异常与函数的接口分开,并且能够区分不同的异常;(2)在函数体外捕获所发生的异常,并提供更多的异常信息.
异常处理
一个函数运行期间可能产生异常。在函数内部对异常进行处理未必合适。因为函数设计者无法知道函数调用者希望如何处理异常。
告知函数调用者发生了异常,让函数调用者处理比较好
用函数返回值告知异常不方便
用try、catch进行异常处理
try块包含有可能产生异常的语句,如果遇到异常就用throw抛出异常,用类型表示异常类型。一旦遇到异常,throw抛出后就跳出try块,进入catch块。一个try块后可以跟很多个catch块,但是catch块只有一个会被真的执行,因为遇到异常就结束try块了,异常只有一个。如果try块没有抛出异常,就不会进入任何catch块。catch块捕获任何类型的异常,参数匹配try块中throw出的数据类型,catch(…)捕获任何类型的异常,可以作为最后一个catch块,防止有异常类型没有被捕获。
#include
using namespace std;
int main()
{
double m ,n;
cin >> m >> n;
try {
cout << "before dividing." << endl;
if( n == 0)
throw -1; //抛出int类型异常
else
cout << m / n << endl;
cout << "after dividing." << endl;
}
catch(double d) {
cout << "catch(double) " << d << endl;
}
catch(int e) {
cout << "catch(int) " << e << endl;
}
cout << "finished" << endl;
return 0;
}
程序运行结果如下:
9 6↙
before dividing.
1.5
after dividing.
finished
#include
using namespace std;
int main()
{
double m ,n;
cin >> m >> n;
try {
cout << "before dividing." << endl;
if( n == 0)
throw -1; //抛出整型异常
else if( m == 0 )
throw -1.0; //抛出double型异常
else
cout << m / n << endl;
cout << "after dividing." << endl;
}
catch(double d) {
cout << "catch(double) " << d << endl;
}
catch(...) {
cout << "catch(...) " << endl;
}
cout << "finished" << endl;
return 0;
}
程序运行结果:
9 0↙
before dividing.
catch(...)
finished
0 6↙
before dividing.
catch(double) -1
finished
异常的再抛出
如果一个函数在执行的过程中,抛出的异常在本函数内就被catch块捕获并处理了,那么该异常就不会抛给这个函数的调用者(也称“上一层的函数”);如果异常在本函数中没被处理,就会被抛给上一层的函数。
#include
#include
using namespace std;
class CException
{
public :
string msg;
CException(string s):msg(s) { }
};
double Devide(double x, double y)
{
if(y == 0)
throw CException("devided by zero");//这个异常就要被返回到上一层函数,让程序员看到
cout << "in Devide" << endl;
return x / y;
}
int CountTax(int salary)
{
try {
if( salary < 0 )
throw -1;
cout << "counting tax" << endl;
}
catch (int ) {//异常在函数内部就被处理掉了
cout << "salary < 0" << endl;
}
cout << "tax counted" << endl;
return salary * 0.15;
}
int main()
{
double f = 1.2;
try {
CountTax(-1);
f = Devide(3,0);//Devide函数内部遇到异常,直接跳出,赋值语句不会被执行
cout << "end of try block" << endl;
}
catch(CException e) {
cout << e.msg << endl;
}
cout << "f=" << f << endl;
cout << "finished" << endl;
return 0;
}
输出结果:
salary < 0
tax counted
devided by zero
f=1.2
finished
C++标准异常类
C++标准库中有一些类代表异常,这些类都是从exception类派生而来
bad_cast
在用 dynamic_cast进行从多态基类对象(或引用),到派生类的引用的强制类型转换时,如果转换是不安全的,则会抛出此异常。
#include
#include
#include
using namespace std;
class Base
{
virtual void func(){}
};
class Derived : public Base
{
public:
void Print() { }
};
void PrintObj( Base & b)
{
try {
Derived & rd = dynamic_cast<Derived&>(b);//此转换若不安全,会抛出bad_cast异常
rd.Print();
}
catch (bad_cast& e) {
cerr << e.what() << endl;
}
}
int main ()
{
Base b;
PrintObj(b);
return 0;
}
输出结果:
Bad dynamic_cast!
bad_alloc
在用new运算符进行动态内存分配时,如果没有足够的内存,则会引发此异常。
#include
#include
using namespace std;
int main ()
{
try {
char * p = new char[0x7fffffff];//无法分配这么多空间,会抛出异常
}
catch (bad_alloc & e) {
cerr << e.what() << endl;
}
return 0;
}
输出结果:
bad allocation
out_of_range
用vector或string的at成员函数根据下标访问元素时,如果下标越界,就会抛出此异常。[]也可以根据下标访问元素,速度比at成员函数快,但是遇到越界时不会抛出异常。例如:
#include
#include
#include
#include
using namespace std;
int main ()
{
vector<int> v(10);
try {
v.at(100)=100; //抛出out_of_range异常
}
catch (out_of_range& e) {
cerr << e.what() << endl;
}
string s = "hello";
try {
char c = s.at(100); //抛出out_of_range异常
}
catch (out_of_range& e) {
cerr << e.what() << endl;
}
return 0;
}
输出结果:
invalid vector<T> subscript
invalid string position
运行时类型检查
C++运算符typeid是单目运算符,可以在程序运行过程中获取一个表达式的值的类型。typeid运算的返回值是一个type_info类的对象,里面包含了类型的信息。
typeid和type_info用法示例
#include
#include //要使用typeinfo,需要此头文件
using namespace std;
struct Base { }; //非多态基类
struct Derived : Base { };
struct Poly_Base {virtual void Func(){ } }; //多态基类
struct Poly_Derived: Poly_Base { };
int main()
{
//基本类型
long i; int * p = NULL;
cout << "1) int is: " << typeid(int).name() << endl;
//输出 1) int is: int
cout << "2) i is: " << typeid(i).name() << endl;
//输出 2) i is: long
cout << "3) p is: " << typeid(p).name() << endl;
//输出 3) p is: int *
cout << "4) *p is: " << typeid(*p).name() << endl ;
//输出 4) *p is: int
//非多态类型
Derived derived;
Base* pbase = &derived;
cout << "5) derived is: " << typeid(derived).name() << endl;
//输出 5) derived is: struct Derived
cout << "6) *pbase is: " << typeid(*pbase).name() << endl;
//输出 6) *pbase is: struct Base
cout << "7) " << (typeid(derived)==typeid(*pbase) ) << endl;
//输出 7) 0
//多态类型
Poly_Derived polyderived;
Poly_Base* ppolybase = &polyderived;
cout << "8) polyderived is: " << typeid(polyderived).name() << endl;
//输出 8) polyderived is: struct Poly_Derived
cout << "9) *ppolybase is: " << typeid(*ppolybase).name() << endl;
//输出 9) *ppolybase is: struct Poly_Derived
cout << "10) " << (typeid(polyderived)!=typeid(*ppolybase) ) << endl;
//输出 10) 0
}