#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;
class Person
{
public:
Person()
{
a = 1;
cout << "Parent()..." << endl;
}
~Person()
{
cout << "~Parent()..." << endl;
}
private:
int a;
};
//异常基本语法
int divide(int x, int y)
{
//unwinding(栈解旋)
Person p1, p2;//当函数抛出异常时,这个函数里定义的所有变量都会被析构,这种行为成为unwinding(栈解旋)
if (y == 0)
{
throw y;//抛异常,抛出异常后,后面的代码就不再执行了
}
cout << "correct" << endl;
return x / y;
}
//最底层的函数抛出异常,会一级级向上抛出,如果上一级没有进行处理会抛到更上一层,直到处理,但如果抛到最顶层还没有被处理,则这个程序就会被终止
//因此c++异常机制是跨函数的,是必须处理的
void test1(int x, int y)
{
divide(x, y);
}
//异常接口声明
//表示这个函数只能抛出int, float, char三种类型异常,抛出其他异常报错
void func1() throw(int, char, float)
{
throw 3;
}
//这种写法为不能抛出任何异常
void func2() throw()
{
}
//这种写法为可以抛出任何异常
void func3()
{
}
class Teacher
{
public:
//异常可以抛出对象,因此可以在类中定义异常的打印函数
Teacher(const char * p)
{
int len;
len = strlen(p) + 1;
this->p = new char[len];
strcpy(this->p, p);//拷贝\0
cout << "调用了构造函数" << endl;
}
Teacher(const Teacher &t)
{
int len;
len = strlen(t.p) + 1;
this->p = new char[len];
strcpy(this->p, t.p);//拷贝\0
cout << "调用了拷贝构造函数" << endl;
}
Teacher &operator=(Teacher &t)
{
int len;
len = strlen(t.p) + 1;
this->p = new char[len];
strcpy(this->p, t.p);//拷贝\0
cout << "调用了赋值函数" << endl;
}
~Teacher()
{
delete[] p;
cout << "调用了析构函数" << endl;
}
void what()
{
cout << "发生异常" << endl;
}
private:
//string p;//使用string类型,是不需要考虑赋值函数和拷贝构造函数的,只有私有成员里有指针时,才需要考虑
char *p;
};
void temp1()
{
//这儿的流程是先调用构造函数,生成一个匿名对象(注意这个匿名对象其实与有名对象意义一样),然后调用拷贝构造函数再建一个匿名对象,把这个用拷贝构造函数声明的匿名对象传给catch函数,然后直接命名,不调用赋值函数
throw Teacher("111");//这儿抛出的是匿名对象,且这儿调用了拷贝构造函数
//throw new Teacher("111");传指针的话用这种方法传递,先在堆中创建一个对象,并返回指针,然后把指针扔出,这时catch用指针去接,执行完逻辑需要delete[],因为这个对象这堆空间,不会主动触发析构函数,需要delete去主动触发析构函数
}
//编写自己的异常类:
//1.最好要继承标准异常类
//下面两条,当你使用exception类的成员变量时,就不需要重写拷贝构造函数和析构函数,但如果子类自己定义一个成员变量时,就需要重写父类的析构函数和what()函数
//仍是下面两条,我认为看情况写与不写,不是一定要写的,有时也不一样要继承,直接定义一个类扔出就可以了
//2.当继承标准异常类时,应重写父类的what函数和虚析构函数
//3.考虑是否显示写出拷贝构造函数和赋值函数,防止发生错误
class MyOutOfRange :public exception
{
public:
MyOutOfRange(const char *error) :exception(error)
{
}
/*
MyOutOfRange(const MyOutOfRange &m)
{
int len = strlen(m.error) + 1;
this->error = new char[len];
strcpy(this->error, m.error);
}
*/
/*
~MyOutOfRange()
{
if (error != NULL)
{
delete[] error;
}
}
*/
/*
virtual char const* what() const//const是修饰在本函数的生命周期内,传入的对象不可更改
{
return error;
}
*/
private:
};
class Animal
{
public:
Animal()
{
age = 0;
}
void setAge(int age)
{
if (age < 0 || age>100)
{
throw out_of_range("年龄不是在0-100之间");
}
else
{
throw MyOutOfRange("年龄是在0-100之间");
}
}
private:
int age;
};
//继承在异常中的应用
class Base
{
public:
virtual void what() = 0;
virtual ~Base() {};
private:
};
class SourceNull :public Base
{
public:
virtual void what()
{
cout << "资源为空" << endl;
}
private:
};
void myCopy(char *q, const char *p)
{
if (p == NULL)
{
throw SourceNull();
}
while (*p != '\0')//一定需要这样写,不能写成*p != 0这种形式
{
*q = *p;
p++;
q++;
}
*q = '\0';
}
int main()
{
//试着去捕获异常,如果没有发生异常,则正常执行,如果发生异常,根据抛出的对象类型进行匹配,然后执行catch里的内容
try {
divide(10, 0);
//divide(10, 1);//不能这样写,一个事件对应一个try...catch,当上面事件的异常被捕捉时,后面的代码就不再执行了,直接执行catch匹配到的逻辑
}
catch (int y) {//根据抛出的异常进行匹配,我们throw抛出的是int类型,因此catch (int)捕获的应是int类型,也可以把抛出的变量接住,写法:catch (int y)
cout << "error" << endl;
}
try {
test1(10, 0);
}
catch (int y) {
cout << "error" << endl;
}
try {
func1();
//devide();//可以不用明确规定抛出的异常为什么类型,这种写法也是可行的
}
catch (char a) {
cout << "char" << endl;
}
catch (int a) {
cout << "int" << endl;
}
catch (float a) {
cout << "float" << endl;
}
//这种写法为捕获所有异常
catch (...) {
cout << "unknown" << endl;
}
try {
temp1();
}
catch (Teacher t) {
t.what();
//delete t;//当接收的是指针时
}
Animal a;
try {
a.setAge(50);
}
//我们扔出的就是out_of_range类型的对象
//因为标准库异常都是继承exception类的,因此可以直接用父类对象去接子类对象
//这儿其实有个问题,因为是父类对象去接子类对象的,因此不会发生多态,但下面调用e.what()看起来却是发生了多态,我想这个what()函数输出的字符串为父类里的字符串,并不是子类新定义的字符串,因此因为是直接对父类中变量直接赋值,所以当然赋值什么输出什么,通过追原码,这儿就是为父类中定义的对象赋值,且也是输出父类中的变量
catch (exception e) {
cout << e.what() << endl;
}
const char *p1 = "111";
char *p2 = NULL;
char q[64] = { 0 };
try {
myCopy(q, p1);
}
catch (SourceNull s)
{
s.what();
}
cout << q << endl;
return 0;
}