c++标准程序库----第三章 一般概念
3.1 命名空间(namespace) std
所谓namespace,是指标识符的某种可见范围。
事实上,c++标准程序库中的所有标识符都被定义于一个名为std的namespace中。
由于namespace的概念,使用c++标准程序库的任何标识符时,有三种选择:
1、直接指定标识符,例如std::ostream而不是ostream
std::cout<<std::hex<<3.4<<std:endl;
2、使用using declaration,使用声明的方式
using std::cout; using std::endl;
于是先前例子可写成
cout<<std::hex<<3.4<<endl;
3、using directive 我的理解是全局声明,这是最简便的方法。如果对namespace std采用using directive,便可让std内定义的所有标识符都有效曝光
using namespace std; cout<<hex<<3.4<<endl;
但这种方法可能导致意外的命名冲突,更糟的是甚至导致不一样的行为。如果场合不够明确(例如在头文件、模块或程序库中),就应避免使用using directive。
当然,我们也可以定义自己的命名空间,2.2.4中提过
2.2.4命名空间
//定义命名空间josuttis namespace josuttis{ class File; void myGlobalFunc(); } //使用命名空间 josuttis::File obj; ... josuttis::myGlobalFunc();
3.2 头文件(Header Files)
c++标准程序库中所有标识符都定义域namespace std里头
- c++头文件命名:
#include<iostream> #include<string>
- c标准头文件命名
#include<cstdlib>//was <stdlib.h> #include<cstring>//was <string.h>
为了向下兼容c,旧式的c标准头文件仍然有效,如果需要,你还是可以使用它们,如:
#include<stdlib.h>
3.3错误(Error)和异常(Exception)处理
异常即可以被硬件引发,又可以被软件引发。由硬件引发的异常通常由中断服务进程产生,例如,算术运算溢出和除数为0所产生的异常;由软件引发的异常由throw语句产生,操作系统和应用程序都可能引发异常。
【Tips:】
【1、程序至多执行其中一个异常处理过程。在相应的异常处理过程执行完毕后,紧随其后的异常处理过程都将被忽略,程序将继续执行这些异常处理过程之后的语句】
【2、异常处理过程在处理完异常之后,还可以执行没有操作数的throw语句,继续传播该类型的异常,tips:【没有操作数的throw只能在catch中执行,用于继续传播异常】【有操作数的throw只能在try中发出】。】
【3、异常发生后,首先产生一个描述异常现场和错误的对象【新版编译程序可以定义为模板类型】,然后定义顺序依次匹配异常处理过程的参数。先声明的异常处理过程将先得到执行机会。】
【4、基于3,派生类【子类:private(or protected) 父类】的异常处理过程应放在捕获基类异常的处理过程之前,catch(…)只能放在所有异常处理过程之后。否则,其它异常处理过程根本得不到捕获机会】
【5、对于未经处理的异常或传播后未经处理的异常,c++的监控系统将调用void terminate()处理。一般情况下,会通过指向abort()的指针调用abort()终止程序。。应用程序可以调用set_terminate()将指向abort()的指针指向自定义的处理函数】
【6、违反异常规格会导致发生不可意料的异常,不可意料的异常可由不可意料的异常处理过程unexpected处理,unexpected一般会终止应用程序的执行。unexpected可以引发一个已经声明了的异常,或者引发一个bad_exception类型的异常。】
【例11.5】
/*本例定义了一个类模板A的构造函数定义了异常接口,函数模板g定义了异常接口*/ #include<iostream> template<class S> class A{ S a; public: operator S(){ return a; } A()throw(){ a = 0; } A(S x){ a = x; } }; void f(void)throw(A<double>) { try{ throw *(new A<double>(2.5)); } catch (A<double>&m){ std::cout << "f:" << m << "\n"; //delete &m; } } template <class B> B g(B x)throw(A<B>) { try{ throw *(new A<B>); } catch (A<B> &m){ std::cout << "B:" << m << "\n"; //delete &m; } return x; } void main(void) { f(); g(1); }
在2.2.3节中提出了异常处理的概念
通过异常处理,c++标准程序库可以在不“污染”函数接口(亦即参数和返回值)的情况下处理异常。如果你遇到一个意外情况,可以通过“抛出一个异常”来停止一般的(正常的)处理过程:
class Error; void f() { ... if(exception-condition){ throw Error();//创建Error类的对象,并抛出它作为异常。 } }
上述throw开始了stack unwinding(栈回退)过程,也就是说,它将使得退离任何函数区段时的行为就像以return语句返回一样,然而程序却不会跳转到任何地点。对于所有被声明于某区段,而该区段却因程序异常而退离的局部对象而言,其析构函数(destructor)会被调用。栈回退的动作会持续直到退出main()或直到有某个catch子句捕捉并处理了该异常为止。
第一种情况的话,程序会结束:
int main() { try{ ... f(); ... } catch(const Error&){ ...//handle exception } }
这里try区段中任何“类型为Error的异常”都将在catch子句获得处理。发生异常时,产生一个Error类型的对象,由catch捕获到。【异常非回调函数,不会再返回到f()的位置】
异常对象(exception objects)其实就是一般类型或基本类型的对象,可以是int、string,也可以是类体系中某个template classes。通常你会设计一个特殊的额error类体系,你可以运用异常对象的状态(state),将任何信息从错误被侦测到的地方带往错误被处理的地方。
我们可以使用异常规格(exception specification)来指明某个函数可能抛出哪些异常。如:
void f() throw(bad_alloc);//f()只可能丢出bad_alloc异常
如果声明一个空白异常规格,那就表明该函数不会抛出任何异常
void f() throw();//f()不抛出任何异常
违反异常规格会导致发生不可意料的异常,不可意料的异常可由不可意料的异常处理过程unexpected处理,unexpected一般会终止应用程序的执行。unexpected可以引发一个已经声明了的异常,或者引发一个bad_exception类型的异常。
3.3.1标准异常类别(Standard Exception Classes)
语言本身或标准程序库所抛出的所有异常,都派生自基类 **exception**。 这些标准异常类型可分为三组: 1、语言本身支持的异常 2、c++标准程序库发出的异常 3、程序作用域(scope of a program)之外发生的异常
- 语言本身支持的异常
是核心语言的一部分,如果一下操作失败,就会抛出这一类异常。 - 全局操作符new操作失败,会抛出bad_alloc异常
- 执行期间,当一个加诸于reference身上的“动态型别转换操作”失败时,dynamic_cast会抛出bad_cast异常
- 执行期 型别辨识(RTTI)过程中,如果交给typeid的参数为零或空指针,typeid操作符会抛出bad_typeid异常
- 如果发生非预期的异常,bad_exception异常会接受处理:当函数抛出异常规格(exception specification)以外的异常,bad_exception就会调用unexpected()。例如:
class E1; class E2;//没有派生关系 void f()throw(E1)//异常规格:只抛出E1类型的异常 { ... throw E1();//抛出E1类型的异常 ... throw E2();//调用unexpected(),然后调用terminate() }
解释:f()之中抛出“类型为E2”的异常,这种动作违反了异常规格(exception specification)的设定,于是唤起unexpected,后者会唤起terminate()终止程序
然而如果在你的异常规格中列出bad_exception,那么unexpected()总是会重新抛出(rethrows)bad_exception异常。
class E1; class E2;//没有派生关系 void f()throw(E1,std::bad_exception)//异常规格:抛出E1类型的异常和其它非预期的异常 { ... throw E1();//抛出E1类型的异常 ... throw E2();//调用unexpected(),然后抛出bad_exception然后调用terminate() }
因此,如果异常规格罗列了bad_exception,那么任何未列于规格的异常,都将在函数unexpected()中被代之以bad_exception
-
c++标准程序库发出的异常
c++标准程序库异常总是派生自logic_error。所谓逻辑错误,包含违背逻辑前提或违反class的不变性。类别有: -
Invalid_argument表示无效参数,例如将bitset(array of bits)以char而非’0’、'1’进行初始化
-
length_error指出某个行为“可能超越了最大极限”,例如对着某个字符串附加太多字符
-
out_of_range指出参数值“不在预期范围内”,例如在诸如array的容器或字符串中采用一个错误索引
-
domain_error指出专业领域内的错误
-
此外 标准程序库I/O部分提供了一个名为ios_base::failure的特殊异常,当数据流(data stream)由于错误或由于到达文件尾端而发生状态改变时,就可能抛出这个异常
-
程序作用域(scope of a program)之外发生的异常
派生自runtime_error的异常,用来指出“不在程序范围内,且不容易回避”的时间。c++标准程序库针对执行期错误提供了以下三个classes: -
[ ]range_error指出内部计算时发生区间错误(range error)
-
[ ]overflow_error指出算术运算发生上溢出(overflow)
-
underflow_error指出算术运算发生下溢出(underflow)
c++标准程序库自身可能抛出range_error、out_of_range和invalid_argument异常。然而由于标准程序库会用到语言特性以及客户所写的程序代码,所以也可能间接抛出任何异常。尤其是,无论何时分配存储空间,都有可能抛出bad_alloc异常。
异常类别的头文件
基础exception和bad_exception定义于。bad_alloc定义于。bad_cast和bad_typeid定义于。ios_base::failure定义于。其它异常类别都定义于。
3.3.2异常类别(Exception Classes)的成员
标准异常的接口只含一个成员函数:what() 。用以获取“型别本身以外的附加信息”。
它返回一个以null结束的字符串:
定义
namespace std{ class **exception**{ public: virtual const char* **what()** const throw();//并没有将字符串的值进行初始化,因此在派生类中必须重新定 //义what ... }; }
要注意的是,除了what(),再没有任何异常提供任何其他成员函数,能够描述异常的种类,可以将what内容打印出来或者程序员自己推断引发异常的原因
try{ ... } catch(const std::exceptions &error){ std::cerr<<error.what()<<<std::enld;// print implementation-defined error message }
3.3.3抛出标准异常
允许抛出标准异常。生成时都只需要一个string参数,它将成为what()返回的描述字符串【这里定义,what()返回】。如logic_error定义如下:
namespace std{ class logic_error:public exception{ public: explicit logic_error(const string& whatString); }; }
提供这种功能的标准异常有:logic_error及其派生类别、runtime_error及其派生类别,ios_base::failure。你不能抛出exception,也不能抛出任何用以支持语言核心性质的异常。
想要抛出一个标准异常,只需生成一个描述该异常的字符串,并将它初始化,交给异常对象。
std::string s; ... throw std::out_of_range(s);
由于char*可被隐式转换为string,所以
throw std::out_of_range("out_of_range(somewhere,somehow)");
3.3.4从标准异常类别(Exception Classes)派生新类别
另一个在程序中采用标准异常类别的情况是,定义一个直接或间接派生自exception的特定异常类别。要这么做,首先必须确保what()机制正常运作。what()是个虚拟函数,所以提供what()的方法之一就是自己实现what();
namespace MyLib{ class MyProbelm:public std::exception{ public: MyProblem(...){ } virtual const char* what() const throw(){//what()函数 } } }; void f(){ .. //创建一个异常对象并抛出它 throw MyProblem(...); }
或派生自标准异常
namespace MyLib{ class MyRangeProbelm:public std::out_of_range{ public: MyRangeProblem(const string &whatString):out_of_range(WhatString){ }; ... } } }; void f(){ .. //创建一个异常对象并抛出它 throw MyRangeProblem("here is my special range problem"); ... }
补充c++程序设计实践的内容 异常对象的析构
引发异常后,首先在引发异常的函数内部寻找异常处理过程,如果没有找到异常处理过程,则在调用该函数的函数内继续寻找,就这样一直找到顶层调用者main。如果main也没有处理异常,则由c++的监控系统处理异常,监控系统通常会终止程序。
异常处理也可能产生内存泄露问题。程序在引发异常时,产生一个异常对象,在这个对象作为实参传递给异常处理过程中也存在浅拷贝构造问题。因此,如果异常对象含有指向动态内存的指针成员,则异常对象所属的类必须定义深拷贝构造函数。【使用指针或引用传递】
【例11.7】
/*局部对象的析构过程:局部对象的析构按函数的返回顺序进行,调用顺序是f g h ,返回顺序就是h g f */ /* #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<string>class EPISTLE{ char *msg; public: EPISTLE(const char *s){ msg = new char[strlen(s) + 1]; strcpy(msg, s); std::cout << msg; } ~EPISTLE(){ if (msg){ std::cout << msg; delete msg; msg = 0; }//析构时输出 } operator const char*(){ return msg; } }; void h(){ EPISTLE h("I am in h()\n");//局部变量或局部对象,在正常情况下,局部对象在函数返回前,自动调用析构函数析构。在出现异常时,C++为了支持局部对象自动析构,为每个函数建立了调用环境,在沿函数的调用链反向搜索的过程中,c++的监控系统会自动析构所有局部对象,但监控系统不析构new生成的局部对象 throw new EPISTLE("I have throw an exception\n");//c++的监控系统会自动析构所有局部对象,但监控系统不析构new生成的局部对象,因此需要delete该异常对象 } void g(){ EPISTLE g("I am in g()\n"); h(); } void f(){ EPISTLE f("I am in f()\n"); g(); } void main(void){ try{ f(); } catch (const EPISTLE *m){ delete m;//析构捕获到的异常对象m("I have throw an exception") } }*/
如果在执行构造函数的过程中引发了异常,则只有构造完毕的基类对象得到析构,没有完全构造好的派生类对象将不会析构。
- 接口的一般概念
- atitit 高并发之道 attilax著.docx 1. 概念 2 2. 并发一般涉及如下几个方面: 4 2.1. 多线程编程(已过时,不介绍) 4 2.2. 异步编程 4 2.3. 并行编程
- (WPF学习记录)第三章 Content的概念
- 进度控制的概念和一般原则
- 【电路第三章】电路中的一般分析方法
- Google C++测试框架系列入门篇:第三章 基本概念
- Netty In Action中文版 - 第三章:Netty核心概念
- 了解Linux的基础知识和一般概念
- 第三章 基本概念
- ARM链接器(一):链接的一般概念
- 【C】【笔记】《C和指针》 第一章 快速上手 第二章 基本概念 第三章 数据 第四章 语句 第五章 操作符和表达式
- 《Erlang 程序设计》练习答案 -- 第三章 基本概念
- 第三章-----操作系统基本概念
- Netty In Action中文版 - 第三章:Netty核心概念
- 第三章:Netty核心概念
- 第三章:基本概念(操作符)
- 第三章 基本概念(中) --《Javascript高级程序设计》
- 《机器学习实战》第三章:决策树(1)基本概念
- 概念学习和一般到特殊序
- SequoiaDB数据库的一般概念介绍