我们学习过程中编写小代码,遇到错误的办法一般是return,exit,但是如果是一个大型的程序,比如百度的服务器,不能因为一个客户端断开,造成自己的套接字无效而退出程序。这样就需要一套容错机制,有一个错误处理系统,其中就包含异常这个技术,一个错误处理系统一般的包含:异常,标准错误码,错误日志记录以及监测系统,今天我们先说异常。
一.什么是异常?
c++之父在《the c++ programming language》中讲到:一个库的作者可以检测出发生了运行时错误,但一般不知道怎么样区处理它们(因为和用户具体的应用有关);另一方面,库的用户知道怎么样处理这些错误,但却无法检测它们何时发生(如果能检测,就可以在用户的代码里处理了,不用留给库去发现)。提供异常的基本目的就是为了处理上面的问题。基本思想是:让一个函数在发现了自己无法处理的错误时候抛出(throw)一个异常,然后它的(直接或间接)调用者能够处理这个问题。
tips:关键就是错误预处理。
首先我们还是先来看一段代码
复习一下atoi的知识点,功能是将数字字符串转换成整型。
#include
#include
using namespace std;
int main()
{
int data=atoi("1234");
cout<
此时正常运行,结果是:
1234
可是现在,我们把1234做一下改变
#include
#include
using namespace std;
int main()
{
int data=atoi("abcd");
cout<
显然不能成功,运行出来结果是0.
当我们作为用户看到这个0,我们不禁会想,到底是我们传的字符串就是‘0’,还是我们传递了错误的字符串。
这个时候我们可能想到这样处理一下
#include
#include
using namespace std;
int myatoi(const char *str)
{
if(*str<'0'||*str>'9')
cout<<"wrong arg!!"<
高级了点,有了一条错误打印,但是对于我们程序没有任何意义。
然后想到我们可以用goto语句,但是goto语句是不可以在不同的程序段跳转的。
所以其实这个问题在c语言中一直是没有解决的,但是我们的c++却可以解决。
#include
#include
using namespace std;
int myatoi(const char *str)
{
if(*str<'0'||*str>'9')
throw "xxxxxxx";
else
return atoi(str);
}
int main()
{
try{
int data=myatoi("asd");
cout<
现在就可以略过程序运行,转去执行错误的语句,把异常和正常运行结果区分开。
二.使用标准异常
上面的例子我们丢的是一个字符串,但是有些时候我们丢的是字符串有些时候却丢的是整型,为了统一规范,我们的c++制定了标准,让我们在使用throw——try——catch的时候直接去调用类,
#include
#include
using namespace std;
float func(int x,int y);
float func(int x,int y)
{
if(y==0) //检查错误
{
/*//return -1;
//定义无效参数类型的对象,用想传递的错误信息初始化
invalid_argument tmp("error,y=0,needs y!=0");
//抛出类对象,就是在抛出异常,立即结束该函数向下指行
throw tmp;*/
throw invalid_argument("error,y=0,needs y!=0");
}
return x/y;
}
int main()
{
AA: int a,b;
cin>>a>>b;
try{
cout<
三.自定义异常
当标准库中的类实现不了我们想要的功能,这时候需要我们去写我们想要的功能,有两种办法,第一种是从标准异常派生,第二种是完全自定义异常。
#include
using namespace std;
class MyException{
public:
MyException(const char *err) noexcept : errmsg(err){ }
const char * priError() const noexcept {
return errmsg;
}
private:
const char *errmsg;
};
float func(int x, int y)
{
if(y == 0)
//抛出完全自定义异常类
throw MyException("error, needs y!=0");
return x/y;
}
int main(int argc, char *argv[])
{
int a, b;
cin >> a >> b;
try{
cout << func(a, b) << endl;
} catch ( MyException &err){
cout << err.priError() << endl;
}
return 0;
}
四.异常规范
异常规范是在c++11中弃用的c++语言功能,这些规范原本用来提供有关可从函数引发哪些异常的摘要信息,但在实际应用中发现这些规范存在问题,证明确实有一个一定用处的异常规范是throw()规范。
例如:
void MyFunction(int i)throw();
告诉编译器不引发任何异常,它相当于nothrow。这种用法是可选的,(c+11)在ISO C++11标准中,引入了noexcept,尽可能使用noexcept指定函数是否可能会引发异常。
转换函数
一.什么是转换函数?
就像c语言一样,c++也有类型转换的需求,不管是显示转换还是隐式转换,如下内置的基础类型转换规范
例:
int val=0;
char ch='a';
int main()
{
val=ch;
cout<
类作为c++中的自定义类型,需要类型转换时,c++提供类型转换函数(type conversion function)将一个类的对象转换成另一类型的数据。
转换函数的实质其实就是运算符重载,只是重载的运算符不是内置的运算符而是类名这个特殊的自定义类型。
语法
operator 类型名()
{
实现转换的语句
}
转换函数的基本格则:
转换函数只能是成员函数,无返回值,空参数。
不能定义到void的转换,也不允许转换成数组或者函数类型。
转换常定义为const形式,原因是它并不改变数据成员的值。
二.标准的转换函数
1.
reinterpre_cast
#include
using namespace std;
int main(int argc, char *argv[])
{
char *p = "hello world";
int *q = NULL;
// q = p;
// 将 一个指针转换类一种类型的指针
q = reinterpret_cast(p);
cout << *q << endl;
return 0;
}
2.const_cast
3.static_cast
4.dynamic_cast
自定义 类型转化
#include
using namespace std;
//转换函数,将当前类型转换成 int类型(将类类型,转换成其他的任何类型(void除外))
//语法:
// operator 类型(){
//
// }
// 注意:
// 1、转换函数没有数据类型,但是有 return返回值
// 2、转换函数必须是类的成员函数,而且空参数
// 3、不能定义到 void 、数组、函数类型的转换
// 4、转换函数常为 const修饰
// 实质:
// 运算符重载,只是重载的运算符只能是 数据类型
class Subclass;
class Base{
public:
Base(int x) : x(x){}
int getValue(){ return x; }
operator int(){ return x; }
operator Subclass();
private:
int x;
};
class Subclass : public Base{
public:
Subclass(int x, int y) : Base(y), x(x){}
int getValue(){ return x; }
private:
int x;
};
Base::operator Subclass(){
return Subclass(x, 0);
}
int main(int argc, char *argv[])
{
int a = 3;
Base obj(5);
obj = a; //移动构造(重载了 =运算符)
cout << obj.getValue() << endl;
obj = 97;
a = obj; // 调用类 Base 中的转换函数, 转换为 int类型
cout << a << endl;
char ch = obj;
cout << ch << endl;
Base obj1(3);
Subclass obj2(5, 1);
obj1 = obj2;
cout << obj1.getValue() << endl;
obj1 = 23;
obj2 = obj1; //调用Base类中的转换函数,转换为 Subclass类型
cout << obj2.getValue() << endl;
return 0;
}
三.慎用转换函数
智能指针
一.什么是智能指针?
先看一个实例:
对象指针式函数nofreemem内的局部变量,每次new出对象,但是nofreemem返回时并没有delete,造成的后果就是对象指针指向的堆区在运行后并没有释放,并且对象也没有销毁
智能指针就能解决这个问题,它是一个特殊的类模板,重载了“->”" * "运算符,实现了c++的自动内存回收机制。
智能指针通用实现技术是使用引用计数(reference count)。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少对象的指针指向同一对象。
2.
3.
4.
5.
:
#include
using namespace std;
/************************************************************************
* 文件说明
************************************************************************/
#include
class Base{
public:
Base(int x): x(x){ cout << "line: " << __func__ << endl; }
~Base(){ cout << "line: " << __func__ << endl; }
int getValue(){ return x; }
private:
int x;
};
void test()
{
//定义资源共享的智能指针 p(实质是一个类模板的对象),
//当该函数结束时,该对象的生命周期将会结束,系统会自动调用析构函数回收堆区资源
#if 0
shared_ptr p(new Base(12));
#else
// make_shared<>():函数模板,目的将堆区开辟的空间,进行共享处理,安全性比 new更高
shared_ptr p = make_shared ( Base(13) );
#endif
//智能指针 p,重载 ->和*号运算符,因此访问 成员必须用 -> 运算符
cout << p->getValue() << endl;
//定义另一个shared_ptr指针,指向同一个对区
shared_ptr q = p;
}
int main(int argc, char *argv[])
{
test();
return 0;
}