C++primer阅读笔记------------用于大型程序的工具

该博客用于记录自己在阅读过程中不懂的知识点,很少用到但比较重要的知识点以及模棱两可的知识点


异常处理通过一步步寻找匹配的catch字句,于该try块关联的catch语句如果没有匹配的,则寻找外层的try块,如果最外层也找不到,则退出当前函数继续寻找,直到退出主函数,程序调用标准库函数terminate终止程序的执行,这个过程称为栈展开


使用类来控制资源的分配可以确保资源在发生异常后也能正常释放掉,异常抛出的代码块中,对象会被析构,称之为堆栈反解,有个例外时,当构造函数出现异常时,成员只有部分被构造成功,所以析构函数不能正常执行,所以应该避免在构造函数中出现
异常,如果不能避免,则一定要在析构函数中捕获

异常的匹配类似于参数的匹配,但它不会有类型转换,除了
派生类到基类的转换可以有,但此时形参必须是引用或者指针,否则会出现切割派生类问题
非const转const
数组转换为数组指针,函数转换为函数指针

为什么栈存在返回栈顶元素的成员函数和弹栈的成员函数,却没有既弹栈又返回栈顶元素的成员函数呢,因为这样的话这个函数同时做了两件事

template<typename T> T stack<T>::pop()
{
    if(count == 0)
        throw logic_error("stack underflow");
    else
        return data[count--];
}


假设类似上面这部分代码,如果执行完data[count--]后返回时出现了异常,那么它不会返回,但是count已经--了,遵守内聚设计的原则,我们应该将这两件事分到两个函数中

还有个例子
Widget& Widget::operator=(const Widget& rhs)
{
    If(this == rhs) return *this; //证同性测试
    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}


假如在delete后new新内存时出现了异常,则会导致pb指向一块删除的内存,为了让以上代码具备异常安全性,做适当修改,如下

Widget& Widget::operator=(const Widget& rhs)
{
    If(this == rhs) return *this; //证同性测试
    Bitmap *pOrig = pb;
    pb = new Bitmap(*rhs.pb); //现在这里即使发生了异常,也不会影响this指向的对象
    delete pOrig;
    return *this;
}


对一个已存在的命名空间的定义可以看作是该命名空间的扩展,由此可以看出命名空间可以是不连续的


外层命名空间通过添加命名空间限定符来访问嵌套命名空间,而访问内联命名空间中的名字可以被外层命名空间直接使用而不用添加限定符

未命名命名空间定义的变量拥有静态生命周期,且可以不连续,但只能在一个文件里面不连续

使用空间别名来简化使用

using指示引入的命名空间的生命周期从当前行到当前作用域结束,不能出现在类的作用域中,using指示由于引入的是一块作用域,所以难免遇到名称相同的问题,此时当前作用域的名字会隐藏引入的作用域的名字
using声明一次只引入一个成员,生命周期同上

命名空间有个特殊点:
std::string s;
std::cin >> s;
等价于
operator>>(std::cin, s);
因为cin和s是类类型的所以,编译器在调用该函数的时候会查找istream和string所属命名空间std,就找到了string的输入运算符;就不需要提供operator的命名空间了

之前提到过move和forward建议使用带限定语的版本(std::),如果我们定义了一个接受单一参数的版本,不管形参是什么类型,都会于标准库冲突

当一个另外的为声明的类或函数第一次出现在友元声明中,则我们认为它是最近的外层命名空间的成员,有个很特殊的操作
namespace A{
  class C{
    friend void f2();
    friend void f(const C&);   //两个友元函数在友元声明之外没有其他声明,所以两函数隐式地成为命名空间A的成员
  }
}
int main(){
  A::C obj;
  f(obj);      //正确,根据实参查找规则通过A::找到了A::f
}


using声明声明的是一个名字,所以它所有的重载版本都会被引入当前作用域

多重继承时,基类的构造顺序和派生列表中基类的出现顺序保持一致

多继承中如果从一个或多个基类中继承构造函数需要显示的声明,且从基类继承来的构造函数参数不能完全相同,如果完全相同,派生类必须定义一个相同的自己的构造函数

派生类如果定义了自己的拷贝/赋值构造函数,则必须在完整的对象上执行拷贝/赋值,意味着它的基类也应该拥有这些操作

多重继承和普通继承一样,可访问某个基类的指针都可以指向派生类

继承体系中查找过程是自底向上的,如果要查找的名字在同一级的基类中发现了多个,则会产生二异性

虚继承共享基类,但是这样依然可能会产生二异性,如果某成员只在一个或一条继承链上被定义了,不会产生二异性,产生二异性的最好解决方法是在派生类中定义自己的版本

虚继承体系的构造方式与普通继承体系稍有不同,虚基类总是先于非虚基类构造,例如,B和C虚继承A,D继承B和C,当生成一个D对象时
先使用D构造函数的初始值列表提供的初始值构造虚基类A,如果没有显示的初始化A,则会隐式的调用A的默认构造函数
接下来构造B和C(顺序按继承列表)
然后构造D部分

如果有多个虚基类,不论直接虚基类还是间接虚基类,先按照继承列表顺序检查,如果有虚基类,则先构造虚基类,虚基类都构造完成后再按顺序构造其他类
合成的拷贝和移动构造函数 以及 合成的成员运算符中的成员 都按该顺序
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值