C/C++基础----用于大型程序的工具(异常处理,命名空间,多重继承)

独立开发的子系统间协同处理错误的能力
使用各种库(可能包含独立开发的库进行协同开发的能力)
对比复杂的应用概念建模的能力

异常处理

异常将问题的检测和解决过程分离开
当执行一个throw之后,程序控制权转移到了与之匹配的catch
控制权的转移有两个重要的含义
1沿着调用链的函数可能会提早退出
2一旦程序开始执行异常处理代码,则沿着调用链创建的对象将被销毁
  • 栈展开
栈展开,层层往外查找。如果找到了匹配的catch,则进入执行代码。执行完catch之后,找到与try块关联的最后一个catch子句之后的点,并从这里继续执行。
如果没找到,则调用terminate退出程序

栈展开过程中,对象被自动销毁,可能当前对象只构造了一部分,我们也要确保这部分被正确的销毁。

析构函数总是会被执行,可以使用类来控制资源的分配
析构函数需要执行某个可能抛出异常的操作,则该操作应该被放置在一个try语句块中,并且在析构函数中得到处理。
  • 异常抛出,catch
当抛出一条表达式时,该表达式的静态编译时类型决定了异常对象的类型
catch中声明的类型必须是完全类型,可以是左值引用,但不能是右值引用。
多个catch语句之间存在继承关系,继承链底端的类放在前面。
catch语句允许的类型转换
1非常量转常量
2派生类转基类
3数组或函数转指针

重新抛出
空throw只能出现在catch内或catch直接或间接调用的函数内。
catch(…)捕获所有异常

如想处理构造函数初始值抛出的异常
template<typename T>
Blob<T>::Blob(std::initializer_list<T> il) try : data(std::make_shared<std::vector<T>>(il) {}
既能处理构造函数体抛出的异常也能处理成员初始化列表抛出的异常。
  • noexcept说明符
经常用在1确认函数不会抛出异常2根本不知道该如何处理异常
noexcept运算符
noexcept(fun(i))  //表示给定的表达式是否会抛出异常
  • 异常类层次

exception仅仅定义了拷贝构造、拷贝赋值、虚析构函数、what虚函数
exception、bad_cast、bad_alloc定义了默认构造函数
logic_error和runtime_error没有默认构造,接收一个string或C风格字符串

(1)语言本身所支持的异常

此类异常用以支撑某些语言特性。主要包括:
bad_alloc:new操作失败会抛出。
bad_cast:执行期间加在一个引用上面的动态性型别转换操作失败时抛出。
bad_typeid:执行RTTI时,交给typeid的参数为零或空指针时抛出
bad_exception:非预期的异常

(2)C++标准程序库发出的异常

logic_error。逻辑错误是由于程序内部逻辑而导致的错误。逻辑错误是可以避免的,且在程序开始执行之前,能够被检测到。
domain_error:专业领域内的范畴 invalid_argument:无效参数,比如讲bitset以char而非0或1进行初始化 length_error:可能超越了最大极限,比如对着某个字符串附加太多字符。 out_of_range:参数不再预期范围内。例如在诸如array的容器或字符串string中采用一个错误索引。

runtime_error,用来指出“不在程序范围内,且不容易回避”的事件。此类错误只在程序执行时才是可检测的。
range_error:内部计算时发生区间错误
overflow_error:算数运算时发生上溢
underflow_error:算数运算时发生下溢

命名空间

为防止名字冲突提供了更加可控的机制
命名空间可以不连续
#include通常不在命名空间内,头文件中所有名字定义成该命名空间的成员
命名空间之外定义必须使用含有前缀的名字
模板特例化必须定义在原模板所属的命名空间内
内联命名空间中的名字可以直接被外层命名空间直接使用,关键字inline必须出现在命名空间第一次定义的地方。
  • 匿名的命名空间
未命名的命名空间中定义的变量拥有静态周期,可以不连续,但是不能跨越多个文件。
所定义名字的作用域与该命名空间所在作用域相同,不会横跨多个不同文件。
  • using声明和using指示
using声明
有效范围从声明的地方到using声明所在作用域结束。外层作用域的同名实体被隐藏。
可以出现在全局作用域、局部作用域、命名空间作用域以及类作用域中
简单地令名字在局部作用域内有效
using指示
可以出现在全局作用域、局部作用域、命名空间作用域。
将命名空间成员提升到包含命名空间本身和using指示的最近作用域的能力。
头文件通常只负责定义接口部分名字,而不定义实现部分名字。因此头文件做多只能在它的函数或者命名空间内使用using指示或声明。
命名空间中名字隐藏的规则有一个重要的例外:
给函数传递一个类类型的对象时,除了在常规的作用域查找外,还会查找实参类所属的命名空间,对应传递的引用或指针的调用同样有效。
1先在当前作用域寻找
2在外层作用域查找
3实参类所属的命名空间
影响候选函数的搜索
using声明如果跟已有函数冲突会引发错误,using指示则不会,只要调用时指明作用域。

一个未声明的类或函数如果第一次出现在友元声明中,则认为他是最近的外层命名空间的成员。

多重继承

构造顺序,首先构造虚基类,然后按派生列表中的顺序依次构造直接基类。
对象、指针和引用的静态类型决定了能够使用哪些成员。

猜你喜欢

转载自www.cnblogs.com/logchen/p/10188325.html