异常
异常
知道c++异常是怎么抛出的,我们怎么接受处理,及其过程
一、异常
1、什么是异常
1.异常是程序在执行期间产生的问题。c++的异常指的是程序在运行的时候所发生的一些特殊的情况。比如除法运算,除以0的操作
注意:我们平时看到的错误是语法错误
二、c++异常
1、c++异常的三个关键字
1.throw:用throw抛出异常
2.catch:用catch接受异常
3.try:在try中执行写有抛出异常的代码
2、示例
try
{
throw "我是一个异常";
cout<<"我是异常的下一句话"<<endl;
}
catch(const char* p)
{
cout<<"出了异常"<<endl;
cout<<p<<endl;
}
#include<iostream>
#include<windows.h>
using namespace std;
int fun(int a, int b)
{
if (b == 0)
throw "除数不能为0";
return a / b;
}
//抛出的异常,和接收异常的地方类型要严格匹配,对于基本数据类型之间不会自动转换了
int main()
{
try
{
//有抛出异常的代码
fun(2, 0);
}
catch (const char* p)//将除数不能为0赋值给了p这个指针
{
//接收异常之后要做的事
MessageBox(0, p, 0, 0);
}
system("pause");
return 0;
}
注意:对于我们抛出的一个异常,我们必须接收,如果不接收就会报错,异常也可以嵌套,那么就会出现里层抛出一个异常,但是并没有在里层的catch块接收,那么外层如果有可以接收的,就会在外层接收
//有点像套娃
#include<iostream>
#include<windows.h>
using namespace std;
int fun(int a, int b)
{
if (b == 0)
throw "where";
return a / b;
}
//抛出的异常,和接收异常的地方,类型要严格匹配
void test()
{
try
{
//有抛出异常的代码
fun(2, 0);
}
catch (const char* p)//将除数不能为0赋值给了p这个指针
{
cout << p << endl;
throw 666.666;
//接收异常之后要做的事
//MessageBox(0, p, 0, 0);
}
}
void test1()
{
try
{
test();
}
catch (double a)
{
cout << a << endl;
//cout << "我接住了异常1" << endl;
throw "我不接这个锅";
}
}
int main()
{
try
{
test1();
}
catch (const char* p)
{
cout << p << endl;
}
catch(...)//可以接收任何异常(基本数据类型,但是像系统的异常接收不了)
{
cout<<"我接住了异常"<<endl;
}
system("pause");
return 0;
}
3、异常的生命周期
1.如果说抛出的异常是一个变量,那么这个变量的存活周期会从throw抛出异常开始直到接收这个异常,在这个过程中,这个变量都会存活
4、栈解旋(了解)---一种现象
1.从try开始,到throw抛出异常之前,所有栈上的对象会被释放,这个过程被称之为:栈解旋
#include<iostream>
#include<windows.h>
using namespace std;
class person
{
public:
person()
{
cout << "构造" << endl;
}
~person()
{
cout << "析构" << endl;
}
};
int fun(int a, int b)
{
if (b == 0)
{
person p1;
person p2;
person p3;
throw p3;
}
return a / b;
}
int main()
{
try
{
fun(1, 0);
}
catch(person& p)
{
cout << "接收到了异常" << endl;
}
/*
构造
构造
构造
析构
析构
析构
接收到了异常
析构
*/
system("pause");
return 0;
}
5、自定义异常
下面是c++的一系列标准的异常,定义中我们是可以直接使用这些类的
异常描述
系统异常类的使用
#include<iostream>
#include<exception>
#include<string>
using namespace std;
//自己写的异常类
class myexception :public exception
{
string str;
public:
myexception(string str)
{
this->str = str;
}
const char* what()const
{
return str.c_str();
}
};
//异常多态的使用,还是父类指针指向子类对象,通过父类指针调用虚函数成员
int main()
{
//系统的异常类都是带参构造
//out_of_range oor("我是一个字符串");
myexception me("我也是一个字符串");
try
{
//throw oor;
throw me;
}
catch (exception&e)
{
cout << e.what() << endl;
}
system("pause");
return 0;
}
三、c++11
1、初始化列表的方式
//旧写法
int a;
int b(2.1);//将2赋值给b,但是小数点后的被省掉了
//新写法
int m{4};
int n={(int)3.14};//新的写法不允许有精度的损失
2、空指针
int* p=NULL;//旧写法
int* p1=nullptr;//新写法
3、自动类型
int a=1;//旧写法
auto b=2;//新写法,根据值去自动匹配b的类型,必须初始化
auto i={1,2,3};
//typeid(变量名).name() 返回变量的类型
cout<<typeid(b),name()<<endl;//int
//用来遍历数组,遍历有效内容,只是遍历
int arr[]={1,2,3,5,6,8,6,5};
for(auto i:arr)
{
cout<<i<<" ";
}
4、decltype()
int a=1;
decltype(a)b;//理解为根据a的类型定义了一个和a类型一致的b变量
decltype((a))m=a;//再加一个圆括号,给a定义了一个别名m
5、新的for规则
string str="awdwdwf";
//遍历这个str
//旧方法
for(int i=0;i<str.size();i++)
{
cout<<str[i]<<endl;
}
//新方法
for(auto i:str)//这里其实使用的是迭代器输出
{
cout<<i<<endl;
}
//这个方法,只能用来遍历数组,容器,i所代表的是里面存的元素,如果是个指针,那么是不能这样遍历的
6、给类型取别名
typedef int hp;//旧写法
using hp=int;//新写法
typedef void (*pfun)();//旧写法
using pfun=void(*)();//新写法
7、default在类中的写法
class A
{
public:
A();
}
A::A()=default;//默认调用默认构造
8、final禁止虚函数发生重写
class A
{
public:
virtual void fun()final{}
//意味着这个虚函数,不能被子类重写,也就是说子类必须用父类的虚函数,所以父类的这个虚函数必须实现其功能
}