Exceptional C++笔记
笔记,两本书的记录,不值得看。以前记在印象笔记的,搬迁出来做个记录
GENETIC
item 1 iterator 有效性,不能解引用无效的迭代器。未定义行为。
item 2~3 实现case-insensitive 字符串 理解base_string
item 4~5 具有最大可复用性的通用容器
item 6 临时对象
item 7 标准库
EXCEPTION-SAFETY
item 8~17 异常安全 stack pop 为什么设计成void
item 18 代码复杂性 如果允许异常,代码隐藏多少路径
item 19 代码复杂性2 异常安全,基本保证,不泄露,强力保证,commit-or-rollback
- 要strong-guarantee 可能就需要牺牲点性能(校验代码保证异常安全,包括拷贝构造异常,RAII,rollback等)如果函数具有多重side-effect那就得封装分层来解决
类设计与继承
item 20~25 class mechanics class技术
- 传引用,explicit关键字等小建议
- 几个小建议,虚基类 using override默认值不要乱改
- 几个OO概念,确定is-a/has-a关系,不要乱用public继承,
- 设计模式,pimpl惯用法,函数方法和继承-复写方法比较
- 重申上面的观点,不要滥用继承
PIMPL
item 26~28 编程依赖
item 29 编程级防火墙 (compilation firewalls)
item 30 fast pimpl
名称搜索,命名空间,接口原则
item 31 名称搜索 ADL
item 32~34 接口原则
内存管理
item 35——37 内存管理
- 内存基本概念
- RAII
traps. pitfalls. and anti-idioms
item 38 对象等同?资源可能泄露
item 39 自动转换
item 40 对象的生命周期
item 41 对象的生命周期2
- 以上两条主要讲成员函数placement-new在this上不可取,不安全。比如继承引入的切割问题,也导致开销大,非异常安全,改变了对象的生命周期
杂项 miscellaneous
item 42 变量初始化 T t();会被解析成函数声明
item 43 const
item 44 casts
item 45 bool,bool语义
item 46 forwarding functions 函数调用转发效率问题 传引用,除非profiler避免内联
item 47 control flow中的异常安全
泛型程序设计与c++标准库
item1 流 理解cin/cout 实现一个echo 理解工程设计原则-即尽可能的提高扩充-尽量提高封装
item2 理解std::remove 原理(
item3 状态谓词带来的问题 stateful-predicate 没有好的解决方案,用上智能指针,共享谓词状态。还是考虑需求为啥要这样实现吧。
item4 扩充模板 用 traits 还是继承
- 继承,约束接口,约束类(imperfect c++也讲过,就是函数指针赋值,让编译器帮你检查)
- SFINAE 来匹配接口指针
- 工厂类?将约束构造放在类内部,客户端类作为模板参数,来(内部特化)匹配出相应的构造
- traits 高扩展,(如果嫌逐个类写traits麻烦也可以参考AA 的书中的解决办法)
- 为了处理模板中的分类而是用继承不足以成为使用继承 的理由。
item5 typename 以及如何使用
- 名字查找,模板实例化才知道,所以不能找到的符号会报错,所以需要typename暗示出来。
- typedef hell
item6 容器,指针,和不是容器的容器
- 原生指针和解引用迭代器有时候很有用,但是好考虑是否有效
- fuck vector
//bitvector,用vector vector deque
item7 使用vector和deque
- 顺序容器优于数组,默认用vector ,经常在头尾插入删除就用deque
- deque分页组织,在体积增长上优于vector,vector push_back要用reverse惯用法,否则低效(反复扩大->挪动)另外,reserve不会缩小空间(shrink_to_fit in c++11)可以用swap trick(shrink_to_fit应该也是这样实现的)
- swap惯用法 包括上面的场景与clear操作(swap 一个空对象更trick一些,这点在effective stl 中也有提到,不过c++11应该优化了clear)
item8 set 与map
- 关联容器,作为key相对位置不能变,key不能被强行改,否则内部的数据结构遍历就会失去控制(map就是const key)
- set可以看成 map<Key,void>
- 你必须知道你在做什么(如果你用const_cast强行改的话)
item9 f(a++) f(a);a++ 代码中的微小差别会带来影响吗
- 多种语义 ,a 是类,那么a的operator++(int)实现是怎样的,f可能是宏,也可能是函数传引用参数或指针
- 接着上面,第二种写法,考虑a 是迭代器,f是erase,迭代器失效就不能调用operator++(int)了,但第一种写法却是对的
- 总而言之,两种写法都要小心,你必须知道你在做什么
item10 模板特殊化与重载
- 显式特化 偏特化(只有类模板可以)
- 函数模板特化,函数重载引入的歧义性
item11 熟悉泛型设计的例子
优化与性能
item12 inline
- 你到底想优化什么,内联如果不是热点,反而适得其反使得提及增大cache 加大反而降低速度
- profiler 说了算,优化原则,别用
item13~item16 缓冲优化 string实现 以及COW写时复制
- 最佳缓冲区增长策略 logn
- COW 一个应用,Linuxfork,引用计数,所谓的浅拷贝和深拷贝不过是返回this 还是new(this)
- COW带来的问题,operator[](不能共享就new一个)(禁止拷贝也行),迭代器,线程不安全(这个和shared_ptr不是线程安全一个道理)(加锁性能损失也很大)
- 新版std::sring 转而采用SSO,需要理解原理
异常安全
item17-19 构造函数失败 ->处理异常 ->未捕获异常
- 对象生命期,如果构造函数异常了会怎样(初始化列表也可以加上try-block(就是有点难看)(就是function-try-block),且必须抛异常,所以尽量放在函数内部局部的处理(普通try-block))
- 接上,只要任何一个基类或成员子对象构造失败,整体就构造失败,改变设计转向pimpl,而不是继承->还是耦合与内聚的问题
- try-block 也不建议用在析构中,析构抛异常是傻逼行为(c++11 编译器会警告,noexcept)
- 注意构造函数的异常规范,能用RAII就用RAII吧。
- 接上,假设傻逼了,析构抛异常不处理,用if-std::uncaught_exception分支处理,会使语义复杂,也很傻逼。还不如用个try-clean_func-catch来处理。
item20-21 管理指针 ->auto_ptr?
- 参数求值完成在函数调用前,考虑函数调用中的异常安全问题,比如new多个参数?参数可能构造失败
- 试图使用auto_ptr解决问题,直接把参数改成auto_ptr 也是不行的,但有个临时方案,再封装一层函数模板返回auto_ptr临时对象,临时对象的生命周期只有一行,如果失败前面的参数资源也不会泄露
- 或者就不要这么写,资源由智能指针托管就行了,别再入参里new
item22 异常安全与类的设计->如何写个operator=()
- 假设类中有类成员(多个),考虑 class& operator=(class&)如何才能更安全 (万金油 pimpl)
- 保证swap 异常安全,用swap( 上智能指针)
item23 异常安全与类的设计->继承带来的影响
- 尽量不用继承来描述is-implemented-in-terms-of,考虑继承下的对象怎么实现operator=() (过于耦合)
继承与多态
item24-28 为什么要使用多继承 ->模拟多继承 ->纯虚函数 ->受控的多态
- 为什么要使用多继承,不是接口抽象类就不要瞎继承了
- 模拟多继承,->组合,像多继承那样安排就好了。
- 切割问题,引入中间类避免
- 纯虚函数,纯虚函数也可以提供默认行为,由派生类显式调用更明显些。
- 接上,默认行为 + 装饰器模式,提供部分行为
- 控制多态 ->友元函数。
内存及资源管理
item29-31 auto_ptr带来的问题 c++11 开始auto_ptr被废弃
- auto_ptr 不能托管数组 数组new 比较坑爹 改进方案 抄一个auto_array, 写个adapter封装数组,不要用数组
- auto_ptr拷贝构造的问题,delete两次(c++11 用unique_ptr取代,禁止拷贝)
- 实现一个value_ptr(有点像加强版unique_ptr)
自由函数与宏
item32 递归声明
- 函数指针如何返回自身? ->代理类封一层函数指针,绕出来,而不是void *硬刚,无开销,就是不能模板通用
item33 模拟嵌套函数
- 嵌套类和局部类。实现operator()()(c++11 有lambda)
item34-35 宏带来的问题
- 安全性问题 const替换
- head-file guard 这个无法取代
- 括号
- 本质是名字替换。所以可能造成冲突,宏机制各个编译器实现有差别
杂项 MISC
item36 初始化 了解构造初始化拷贝初始化语义 (《深入了解c++对象模型》)
item37 前置声明消除编译期依赖
item38 typedef
- 偷懒 (所以说在c中,不值得用typedef来简写struct)
- traits
item39-40 namespace
- 不要在头文件中使用using 污染名字空间
- using与声明顺序,不要放在#include前
书后是一个多线程 cow string实现(加锁),以及性能测试