STL学习笔记(四)——温习一下C++的一些重要概念(上)

温习一下C++的一些重要概念(上)

关于命名空间与头文件的概念,相信你对此已经理解得很深刻了,在此不再赘述(可翻阅《C++ Primer Plus》的相关章节)。我们来重点回顾一下以下知识点:
  • 差错和异常处理
  • 可被调用的对象
  • 并发与多线程
  • 初识分配器
本节我们主要学习差错和异常处理,想了解try{}catch{}详细用法的朋友请仔细阅读本部分内容。

1.差错和异常处理

a) 标准异常类

所有被语言本身或标准库抛出的异常,都派生自基类exception,定义于。它是若干标准异常类的基类。这些标准异常类可分为三组:
  1. 语言本身支持的异常
  2. C++标准库发出的异常
  3. 程序作用域之外发出的异常(例如,资源不足)
STL学习笔记(四)——温习一下C++的一些重要概念(上)_第1张图片

针对语言支持而设计的异常类

此类异常用以支撑某些语言特性,所以,从某种角度说它们不是标准库的一部分,而是核心语言的一部分。以下操作如果失败会抛出该类异常:
  • 运行期间“动态类型转换”失败时,dynamic_cast会抛出bad_cast异常,此异常定义于
  • 运行期间类型辨识(RTT1)过程中,如果交给typeid操作符的实参为0或空指针,typeid操作符会抛出bad_typeid异常,此异常定义于
  • 定义于内的bad_exception异常被用来处理非预期的异常。它可以由函数unexpected()抛出,该函数会在“某个函数抛出的异常不在异常明细内”时被调用。[注:]c++11不再鼓励使用异常明细。

针对逻辑差错而设计的异常类

针对逻辑差错而设计的异常类派生自logic_error。逻辑差错是指可在程序中避免的错误,包括“违背逻辑先决条件”或“违反class不变性”等。c++标准库提供了5种针对逻辑差错的异常类:
  • invalid_argument表示无效实参,例如,将bitset以char初始化。
  • length_error指出某个行为可能超过最大允许的大小,例如,对某个字符串附加太多字符。
  • out_of_range指出实参值不在预期范围内,例如,array或string索引越界。
  • domain_error指出领域范围内的错误。
  • future_error指出当使用非同步系统调用时发生的逻辑差错。
针对逻辑差错而设计的异常类通常被定义于内,但是class future_error被定义于

针对运行期差错而设计的异常类

派生自runtime_error的异常都被用来指出“不在程序作用域内且不容易回避”的事件。
  • range_error指出内部计算时发生区间错误。例如,在“wide string”和“byte string”之间转换时可能发生该异常。
  • overflow_error指出算术运算时发生上溢。例如,bitset被传入一个整数值会抛出该异常。
  • underflow_error指出算术运算发生下溢。
  • system_error指出因底层操作系统而发生的差错。例如,c++标准库可在并发环境中抛出该异常。
  • bad_alloc是最重要的一个异常。只要new失败,定义于中的bad_alloc就会被抛出。如果传给new的大小小于0或超过编译器定义的极限,派生自bad_alloc的bad_array_new_length就会被抛出。
  • bad_weak_ptr会在根据shared pointer创建weak pointer失败时被抛出,它被定义于头文件中。
  • bad_function_call会在function外覆物被调用但其实没有目标时被抛出,它被定义于头文件中。
  • 针对标准库的I/O部分,有一个特别的异常类ios_base::failure,它被定义于头文件中。此类异常可能会在stream发生错误或遇上end-of-file而改变其状态时被抛出。c++11起该类派生自system_error,c++11之前派生自exception。
针对运行期差错而设计的异常类通常被定义于内,但是class system_error被定义于

由标准库抛出的异常类

如前所述,几乎所有异常类都有可能被C++标准库抛出。比如,bad_alloc异常。
此外,由于标准库可能用到应用程序开发人员所写的代码,所以也可能间接抛出任何异常。
标准库的任何具体实现都有可能提供更多异常类。然而,使用这些非标准类将导致程序难以移植。所以,最好只使用标准异常类。

异常类的头文件

为了能够处理标准库可能抛出的所有异常,必须包含:
#include         // for classes exception and bad_exception
#include         // for most logic and runtime error classes
#include      // for system errors(since c++11)
#include               // for out-of-memory exceptions
#include               // for I/O exceptions
#include            // for errors with async() and futures(since c++11)
#include          // for bad_cast and bad_typeid

b) 异常类成员

为了在catch子句中处理异常,你必须采用该异常类的接口。所有标准异常都提供了what(),某些异常类还提供了code()。

成员函数what()

对所有标准异常而言,可用来获取类型以外的附加信息的唯一成员函数就是what(),它返回一个以'\0'结尾的字符串。
namespace stdexcept
{
	class exception
	{
	public:
		virtual const char* what() const noexcept;
		// ...
	}
}
what()返回是string内容由编译器实现定义。注:该string有可能是以'\0'结尾的宽字节字符串,可被转换为wstring。what()返回的字符串在其所属异常对象析构后,或被赋予新值后,就不再有效了。

差错码和差错状态

异常类system_error和future_error还拥有一个额外的成员可以取得详细信息。然而在进入详细信息之前,我们必须先了解“差错码”和“差错状态”的差别:
  • 差错码 是一种用来封装差错码值的轻型对象,差错码值可由编译器实现指定,不过某些差错码是标准化的。
  • 差错状态 是一种提供差错描述的对象。
if (ec == std::errc::invalid_argument) {...} //检查差错状态
if (ec == std::future_errc::no_state) {...}  //检查差错码
为了能够处理差错码和差错状态,可提供额外的非虚成员函数code():
namespace std
{
	class system_error : public runtime_error
	{
	public:
		virtual const char* what() const noexcept;
		const error_code& code() const noexcept;
		// ...
	}
	
	class future_errc : public logic_error
	{
	public:
		virtual const char* what() const noexcept;
		const error_code& code() const noexcept;
		// ...
	}
	
	class error_code
	{
	public:
		const error_category& category() const noexcept;
		int value() const noexcept;
		string message() const;
		explicit operator bool() const noexcept;
		error_condition default_error_condition() const noexcept;
		// ...
	}
}
我们可以这样处理异常:
STL学习笔记(四)——温习一下C++的一些重要概念(上)_第2张图片

c) 以exception_ptr传递异常

C++11起,C++标准库提供了一个能力:将异常存储于类型为exception_ptr的对象中,稍后才在其他情境中处理它们:
#include 
std::exception_ptr eptr;  // 默认为nullptr.

void foo()
{
	try
	{
		throw ...;
	}
	catch(...)
	{
		eptr = std::current_exception(); // 保存当前异常到eptr中
	}
}

void bar()
{
	if(eptr != nullptr)
	{
		std::rethrow_exception(eptr); // 重新抛出被保存的异常
	}
}
以上特性在线程之间传递异常时特别有用。

d) 抛出异常

所有提供what()接口的逻辑差错和运行期差错标准异常,都只有一个构造函数接受std::string和一个构造函数接受const char*参数(C++11新增)。例如:
namespace std
{
	class logic_error : public exception
	{
	public:
		explicit logic_error(const string& whatString);
		explicit logic_error(const char* whatString);    // since c++11
		// ...
	}
}

namespace std
{
	class system_error : public runtime_error
	{
	public:
		system_error(error_code ec, const string& whatString);
		system_error(error_code ec, const char* whatString);    // since c++11
		system_error(error_code ec);
		system_error(int ev, const error_category& ecat, const string& whatString);
		system_error(int ev, const error_category& ecat, const char* whatString);
		// ...
	}
}
抛出异常是很简单的一件事:
throw std::out_of_range("out_of_range(somewhere, somehow)");
throw std::system_error(std::make_error_code(std::errc::invalid_argument), "This is system_error!");

e) 自定义异常类

我们还可以建立一个自己的异常类,使其直接或间接派生自exception基类。注:必须确保what()机制和code()机制能正常运作(提示:基类的what()是虚函数)。

<未完,转下一节>

你可能感兴趣的:(STL/Boost,STL,exception,异常处理)