PoEdu - C++阶段班 【Po学校】 C++中的异常系统 课堂笔记

程序代码中的2种错误:

  1. runtime_error  编译错误,语法错误,编译器会提示。
  2. logic_error  运行时错误,语义错误,会发生各种各样的情况。

异常体系:

    • throw type // 抛出
    • try{}     //尝试
    • catch{}    //捕捉  

 throw抛出的,不仅仅可以是数字,还可以是一个类型,或者是一个类;有抛出就要有相应的捕捉(catch),捕捉时也要注意隐匿的转换。如果不想写过多的捕捉代码,又不想放过其中任何一个异常,可以写成catch{...}

throw可以在除了析构函数之外的任何函数里面出现,甚至出现在自己写的Exception的构造函数里。

异常体系具备栈安全的机制:

抛出之前,会对栈进行清理。出一个函数的栈,就看try{} catch{}有没有处理,没有处理就清理栈,继续上抛,如此向上再次看try...catch...有没有做处理,没有就清理栈,向上抛;如此循环向上,如果到最上面还是没有try...catch....处理,就抛给编译器处理。如此所有局部变量就被销毁了;如果我们是堆上的函数异常了,就没有这些功能了。catch{}是根据栈来展开的。堆上就要程序员自己delete了。

#include <stdio.h>
#include <exception>
#include <iostream>
#include <string>

class  MyExpict
{
public:
	MyExpict(const std::string &str);
		void What()const;
private:
	std::string str_;
};

MyExpict::MyExpict(const std::string& str):str_(str)
{
	if (str.empty())
	{
		throw MyExpict("str为空");
	}
}

void MyExpict::What() const
{
	if (str_.empty())	
		throw MyExpict("str_为空");	
	std::cout << str_ << std::endl;	
}

class Test
{
public:
	Test(int id):id_(id)
	{
		std::cout << "Test()" << id_<<std::endl;
	}
	Test(const Test& test)
	{
		std::cout << "Test(const Test &test)" << id_<<"copy"<<std::endl;
	}
	~Test()
	{
		std::cout << "~Test()" << std::endl;
	}
private:
	int id_;
};

Test& Foo4()
{
	Test *test = new Test(2);
	return *test;
}


void Foo3()
{
	Test test(1);
	Foo4();
	throw MyExpict("Foo3");
}

void Foo2(Test test)
{
	Foo3();
}

void Foo1(Test& test)
{
	Foo2(test);
}


int main()
{	
	try
	{
		Foo1(Test(0));
	}	
	catch(MyExpict e)
	{
		e.What();
	}
	
	return 0;
}

  

运行:

Test()0
Test(const Test &test)0copy
Test()1
Test()2
~Test()
~Test()
~Test()
Foo3

 

最后只有new出来的Test没有释放,因为它开辟在堆空间。

 

自定义的异常类,在继承时一定要注意,要将派生类生成的对象写在基类对象的调用之前;一定要先catch()派生类,然后再catch()基类。

#include <stdio.h>
#include <exception>
#include <iostream>
#include <string>

class  MyExpict
{
public:
	MyExpict(const std::string &str);
		void What()const;
private:
	std::string str_;
};

MyExpict::MyExpict(const std::string& str):str_(str)
{
	if (str.empty())
	{
		throw MyExpict("str为空");
	}
}

void MyExpict::What() const
{
	if (str_.empty())	
		throw MyExpict("str_为空");	
	std::cout << str_ << std::endl;	
}

class MyOtherExpict:public MyExpict
{
public:
	MyOtherExpict(const std::string &str);
	void What()const;
private:
	int id_;
};

class Test
{
public:
	Test(int id):id_(id)
	{
		std::cout << "Test()" << id_<<std::endl;
	}
	Test(const Test& test)
	{
		std::cout << "Test(const Test &test)" << id_<<"copy"<<std::endl;
	}
	~Test()
	{
		std::cout << "~Test()" << std::endl;
	}
private:
	int id_;
};

Test& Foo4()
{
	Test *test = new Test(2);
	return *test;
}


void Foo3()
{
	Test test(1);
	Foo4();
	throw MyOtherExpict("Foo3");
}

void Foo2(Test test)
{
	Foo3();
}

void Foo1(Test& test)
{
	Foo2(test);
}


int main()
{	
	try
	{
		Foo1(Test(0));
	}	
	catch(MyExpict e)
	{
		e.What();
	}
	catch(MyOtherExpict e)  //派生类的catch()要写在 基类之前
	{
		e.What();
	}
	
	return 0;
}

  运行:

1>------ 已启动生成: 项目: ErrorTest, 配置: Debug Win32 ------
1> Error.cpp
1>e:\c_code\errortest\errortest\error.cpp(93): warning C4286: “MyOtherExpict”: 由基类(“MyExpict”)在行 89 上捕获
1>Error.obj : error LNK2019: 无法解析的外部符号 "public: __thiscall MyOtherExpict::MyOtherExpict(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (??0MyOtherExpict@@QAE@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z),该符号在函数 "void __cdecl Foo3(void)" (?Foo3@@YAXXZ) 中被引用
1>Error.obj : error LNK2019: 无法解析的外部符号 "public: void __thiscall MyOtherExpict::What(void)const " (?What@MyOtherExpict@@QBEXXZ),该符号在函数 __catch$_main$1 中被引用
1>E:\C_Code\ErrorTest\Debug\ErrorTest.exe : fatal error LNK1120: 2 个无法解析的外部命令
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

 

  • 异常继承时需要派生类在基类之前 。
  • 抛出指针:非迫不得已,不可抛出指针;任意一个指针都可以被void*捕获(catch)。

exception 是所有异常类的基类

View ColorizedCopy to Clipboardclass exception {
public:
    exception();
    exception(const char * const &message);
    exception(const char * const &message, int);
    exception(const exception &right);
    exception& operator=(const exception &right);
    virtual ~exception();
    virtual const char *what() const;
};
  • runtime_error 类

      • overflow_error 溢出
      • range_error   范围
      • underflow_error  下溢
class runtime_error : public exception {
public:
    explicit runtime_error(const string& message);
    explicit runtime_error(const char *message);
};
  • logic_error类

      • ivalid_argument  不存在的参数
      • length_error   长度 
      • out_of_range   超出范围
      • domain_error 领域   
class logic_error : public exception {
public:
    explicit logic_error(const string& message);
    explicit logic_error(const char *message);
};

以一两类异常,只是把异常做了一个整体的划分。同级别的派生类还有:

  • bad_alloc 类   重点,用的多

class bad_alloc : public exception {
   bad_alloc();
   virtual ~bad_alloc();
};

在具体new的场合下,在指针分配出错了,异常时用bad_alloc类

  • bad_cast类  重点,用的多

catch (bad_cast)
   statement
class bad_cast : public exception {
public:
   bad_cast(const char * _Message = "bad cast");
   bad_cast(const bad_cast &);
   virtual ~bad_cast();
};

转换时出现错误用bad_cast

 

异常的正确使用方法:

  1. 继承std::exception
  2. 首先catch(Myexception e),再来catch(std::exception e)
  3. 写在main函数中,其它的地方只写throw

 抛出还可以如下图;但不建议如此写。

函数后面会抛出一个什么样的异常,这个问题很有争议,然后就延申出了另外一个“noexcept”

noexcept  表示此函数没有异常

  • 冷知识:noexcept后面可以传参数,但不要这么写。
    • noexcept(true)
    • noexcept(false)     表示此函数会抛出异常

其实只有一个void无参的空函数,才是真正意义上的noexcept

noexcept只要了解就行

 切记:永远不要在析构函数里面throw! 

加一个throw()到函数中?

这是异常规范,只会出现在声明函数中,表示这个函数可能抛出任何类型的异常

  • void GetTag() throw(int);表示只抛出int类型异常
      • 并不表示一定会抛出异常,但是一旦抛出异常只会抛出int类型,如果抛出非int类型异常,调用unexsetpion()函数,退出程序。
  • void GetTag() throw(int,char);表示抛出in,char类型异常
  • void GetTag() throw();表示不会抛出任何类型异常
  • void GetTag() throw(...);表示抛出任何类型异常

posted on 2017-02-25 14:49  zzdoit  阅读(596)  评论(0编辑  收藏  举报

导航