12.19学习
摸鱼了好几天了,今天想要狂雪,把primer整本搞完
话不多嗦,直接开始。
异常处理:
1.异常处理当遇到throw语句时,沿调用链的函数可能会提早退出,并沿着调用链的对象将被销毁。
重点:int* p = new int;之类的只能做到销毁int*,无法执行动态内存的销毁。
*可以通过类的析构函数执行销毁(智能指针好用啊)
无需访问抛出的异常时,可省略参数名 *catch(int*)
2.静态类型决定了catch接受的异常类型!!!
3.catch的匹配不一定是最佳的,而是至上而下的。
4.catch的匹配机制十分严格,仅支持const转化,派生类向基类转化,数组指针,函数指针。
5.空throw出现于处理语句内,代表重新抛出异常。这里注意一个点,若我们对异常对象做出某些处理,并且要让这个处理保留
在重新抛出的异常中,在catch异常时候就要使用其的引用。
6.try语句块与构造函数:
A(int x)try: data(x){ } catch(...){ }
try既能处理初始化列表发生的异常,也可以处理构造函数函数体执行时发生的异常。
但需要注意的是,函数形参的初始化不归try管。
7.noexcept函数中也可以抛出函数(违背承诺),当违背承诺发生时,程序会调用terminate.
8.void foo() noexcept(true值)承诺不会抛出异常。
9.noexcept除了当作说明符外,还是一个一元运算符。noexcept接受一个表达式,并判断它是否会抛出异常。
这个在与上一条联用有很好的效果。
10.函数指针也有是否有可能抛出异常被分为两类,两类指针不能互相转化。
11.派生类继承虚函数时候,派生类虚函数的声明必须显式地做出相应的保证。
命名空间:
1. namespace在它的作用域内要保持唯一,且namespace不能定义在函数或类的内部。
2.namespace可以是不连续的,
namespace nsp{ }
这既可以是定义一个新的命名空间,也可以是为已有的命名空间添加新的成员。
所以我们可以把包含声明的命名空间定义在头文件中,在其他的源程序再添加命名空间中的成员的定义。
一些问题与思考:
inline func定义于namespace会发生什么?
出了定义它的源文件,其他源文件并不能使用。
class的定义为什么不能再源文件中?
我琢磨了很久,应该是想出了一套比较好理解的解释方法。
首先我们要先意识到一个点,如果我们在头文件”定义“一个类,不同于定义函数,这个头文件可以被多个源文件声明。
我认为类的”定义“区别于一般对象的定义,类的”定义“没有产生任何实例产生,所以类的”定义“更类似于一种”声明“。
在namespace里也是类似的,我们尽管对包含该namespace的头文件进行了多次的#include,但里面只是一堆声明,并不会产生任何不好的效果。
我们可以注意到一个点,我们调用某个函数时,其实只是先需要他的声明,支持之后再给出定义。
支持在其他源文件中定义这样的接口就是出于这样的原因。
而类就不支持这样,我们在使用类来定义对象时,该类必须时完全的,所以不能将类的定义置于其他源文件中。
namespace floor1{ inline namespace floor2{ int x = 1; } } cout<<floor1::x;
inline namespace与普通namespace的唯一区别就是,inline无需通过namespace名来进行访问成员。
一个特例,inline namespace如果在全局命名空间中,就无法与全局变量区分
一个源文件的未命名命名空间只归该源文件所有,其名字可以直接使用,但同上也可能会跟包含的作用域的变量产生歧义。
namespace t = nps;
上面是namespace别名的定义方式
using声明一次之引入一个对象,且会隐藏外部作用域的同名对象。
using std::cout;
using指示,将整个namespace变为可见
using namespace std;
但using指示不同于using声明,它将会将命名空间成员提升到一个最近的,并且同时包含了using指示与命名空间的作用域
例:
namespace t{ int x = 10; } x = 110; int main(){ using namespace t; cout<<x; }
此时x就会产生二义
这是using t::x;所不会产生的效果。
写得有点多了,先放松一下,晚上继续搞。