第1/2章: C++ 建议 / 两种不变性 const & co

2022-06-20  本文已影响0人  my_passion

第1章 C++ 设计原则 / 对 C & C++ 程序员的建议

1.1 C++ 设计 2 个原则

(1) 不给比 C++ 更底层的语言 留任何余地

(2) 零开销原则/ZOP
    与等价的 替代方法相比, 不浪费 1个字节(空间) 或 1个CPU时钟周期(时间)

1.2 建议

1 对 C++ 程序员的建议

    用 C++11 新特性, 使之 更现代化
    
    (1) 
        用    ctor       建立 不变式
    
        配合 ctor/dtor  简化 资源管理(RAII)
    
    (2) 优先用...      而非...
        ——————————————————————————————————————————————————————
        容器和算法       内置数组 和 专用代码
        ——————————————————————————————————————————————————————
        标准库         自己开发的代码
        ——————————————————————————————————————————————————————
        异常          错误代码 来报告 不能局部处理的错误
        ——————————————————————————————————————————————————————

    (3) smart ptr 
    
        ————————————————————————————————————————————————————————————————
        避免裸的 new/delete
        ————————————————————————————————————————————————————————————————
        用 unique_ptr 引用 多态对象
        ————————————————————————————————————————————————————————————————
        用 shared_ptr 引用 共享对象, 即 不止1个 owner 负责其析构 的对象
        ————————————————————————————————————————————————————————————————
    
    (4) 用 移动语义 来避免 copy 大对象
    
    (5) 用 模板 来保持 静态类型安全(消除 类型转换), 并 避免 类层次的 不必要使用

2 对 C 程序员的建议

    (1) 想发挥 C++ 相对于 C 的优势, 要采用 `(与 C) 不同的 设计&实现 风格`
        
        学 C++ 标准库 -> 与 C标准库 diff
        
            字符串 copy/cmp 用 =/== 而不是 strcpy()/strcmp() 等
        
    (2) 宏: C++ 几乎从来不用 -> 替代
    
        1) 定义 explicit 常
            const / constexpr / enum (class)
            
        2) 避免 函数调用开销
            inline
        
        3)  指明 函数族/类型族
            template
            
        4) 避免 名字冲突
            namespace
            
    (3) 优选...           而不是...                  
        new/vector      malloc()/realloc()
        std::vector     数组  
        std::string     C 风格字符串
        
    (4) 
        避免用 void* / union / 类型转换, 除非在 某些函数或类的 深层次实现中
        否则
            1) 会限制你从 类型系统 得到的 支持
            2) 通常, 1次 类型转换 就暗示着 1个 设计错误
        
        若 必须用 显式类型转换, 优选 命名的 显式类型转换(static_cast 等), 更准确地 表达意图
        
    (5) 真正需要 变量时, 再声明 并 立即初始化

    (6) 不要认为 用 C 风格辛苦写出的程序会 比 简短的C++替代程序(如使用 标准库) 更高效
        实际上, 通常正好相反

第2章 C++ 概览: 基础知识

2.1 基本概念

1 类型 / 初始化

    (2) 初始化方式
        1) 列表 {} 
            1) = 可选
            2) 确保不会发生 窄化类型转换
        
        2) = 
            = 配合 auto
                不存在 可能发生错误的 类型转换
            
            何时该用 auto?
                无明显理由 需 显式指定类型

2 2种 不变性 / 常量 / 常量表达式 / constexpr 函数

    (1) 3个难点
        1) 
            常量    : 初始化(编译时/运行时) 之后, 值不会再改变
            常量表达式: 编译时能求值
        
        2)
            const     常量: 可在 `运行时求值`
                  /|  |
              必为 |  | 未必为
                   |  |/
            cosntexpr 常量: `编译时能求值`
        
        3) cosntexpr 函数 
            实参 为 常量表达式  : 编译时求值 => 结果 (是常量表达式) 可用于 常量表达式 
            实参 为 非常量表达式: 运行时求值 => 结果不可用于常量表达式
        
    (2) C++ 支持 `2种 不变性`
    
        ——————————————————————————————————————————————————————————————————————————————
                |       const                | constexpr
        ——————————————————————————————————————————————————————————————————————————————  
                |                            | 1) 对象定义
        含义  | (编译器)承诺不改变这个值      |      编译时求值(若求不出 => 报错)
                |                            | 2) 函数定义
                |                            |   若 实参 为 常量表达式/else, 
                |                            |   则 函数 应该能/不能 用在 常量表达式
        ——————————————————————————————————————————————————————————————————————————————  
                |   说明`接口`               | 
        主要用于|                            | 1) 数据 放 只读内存 / 提升性能
                |                            | 2) ...
        ——————————————————————————————————————————————————————————————————————————————
        
                             constexpr double res1 = square(v1);    
                           /    
                          / 1) 若 square(10) 是 常量表达式, 则 正确
        // 常量                           
        cosnt int v1 = 10;  
                          \ 2) 与 用 constexpr 含义等价 
                           \
                             const double res2= square(v1); 
        
                             constexpr double res3 = square(v2);    
                          /  
                         /    
                        / 3) error!!! : var2 不是 常量表达式   
        // 非常量     /                
        int v2 = 10;        
                       \    
                        \ 4) ok: 运行时 求值
                         \
                            cosnt double res4 = square(v2); 
        
        5) double sum(const vector<double>&); // sum 不更改 其 参数的值
    
    (3) 常量表达式 应用
        1) 语言规则需要
            1> 数组 size
            2> case 标签
            3> constexpr 声明的 常量
            4> (某些) 模板实参
                    
        2) 不变性 (对象状态 不发生改变) 
        3) 编译时求值:性能高    

    new 运算符 分配内存的区域
        自由存储 / 动态内存 / 堆: 3 者含义相同            

2.3 模块化

模块
    函数 / 用户自定义类型 / 类层次 / 模板
    
构建C++ 程序的关键
    模块间: 清晰定义交互关系
        
    某模块:接口与实现 分离

模块化
    逻辑上
        通过 语言特性 描述模块
        
    物理上
        通过 划分文件 和 分离编译 分离/组合模块              

1 分离编译

    (1) 含义
        user 代码只能看见所用 类型和函数 的 声明
        它们的 定义 放 分离的 源文件, 并 `分别编译`
        
    (2) 优点
        1) 编译时间 降到最少
        2) 逻辑独立的部分 分离 => 错误几率 降到最低
        
    (3) 应用
        库: 分离编译的 代码片段 的 集合

    (4) 机制
    
                模块头文件: 
                    模块(Vector) 接口
                 /          \
                /            \
        用户源文件:      模块源文件: 
            使用 Vector       定义/实现 Vector

        // 1) Vector.h
        class Vector
        {
        public:
            Vector(int s);
            double& operator[](int);
            int size();
        private:
            double* elem; 
            int sz;
        };
        
        // 2) user.cpp
        #include "Vector.h"
        #include <cmath>
        using namespace std;
        double sqtr_sum(Vector& v)
        {
            // ...
        }
        

        // 3) Vector.cpp
        #include "Vector.h" // 获得接口
        
        Vector::Vector(int s)
            : elem {new double[s] }, sz(s) { }
        
        double& Vector::operator[](int i) { return elem[i]; }
        
        int Vector::size() { return sz; }

2 名字空间

    表达一组声明 属于 一个整体
    避免 `名字冲突`

3 错误处理

    (1) 异常
    
        1) 机制
            分离 运行时 错误捕获位置 与 错误处理位置
        
        2) 可 自定义 异常类
        
        3) 例子
            Vector 实现者 `检测` 异常, 通过 throw `通知` 使用者
            
            Vector 使用者 通过 catch 采取应对措施
            
        // 实现者
        double& Vector::operator[](int i) 
        {
            if(i<0 || size() <= i)
                throw std::out_of_range("Vector::operator[]");
            return elem[i]; 
        }
        
        // 使用者
        void f(Vector& v)
        {
            try
            {
                v[v.size()] = 10;
            }
            catch(std::out_of_range)
            {
                // 处理
            }
        }   
        
    (2) `类的不变式`
        
        1) 含义
            假定某事为真的 声明
            
        2) 建立 类的不变式 
            1> 是 Ctor 的责任, 从而 成员函数 可以依赖于 不变式
            
            2> 确保 成员函数退出时, 不变式 仍成立

        Vector::Vector(int s)
        {
            if(s <0)
                throw length_error{};
            elem = new double[s];
            sz(s);
        }
上一篇下一篇

猜你喜欢

热点阅读