美文网首页
Effective C++ 1: 让自己 习惯 C++

Effective C++ 1: 让自己 习惯 C++

作者: my_passion | 来源:发表于2021-07-20 21:17 被阅读0次

    part1 让自己 习惯 C++

    1 视 C++ 为 1个 语言联邦

    1 C++ 是 `5 重范式: Procedural/OO/GP/Functional/MP`
    
    2 C++ 可视为 `4个 子语言: (策略不同) C/OO C++/Template C++/STL`
        
        `[2/3] pass-by-reference-to-const 往往更好`
    

    2 尽量以 const / enum / inline 替换 #define, 即 优选 compiler 而不是 preprocessor

        ————————————————————————————————————————————————————————————
        3者 相比 #define 的 优势
        ————————————————————————————————————————————————————————————
        (1) const 
            
            [1] `编译器 检查` 
            
            [2] 可作 `class static 成员数据`
                
            Note: const 独特之处
            
                const object (默认 内部链接) 想具有 `global scope` 
                    
                    -> `定义/初始化 必须放 头文件`
        ————————————————————————————————————————————————————————————    
        (2) enum
            
            [1] `防止 通过 ptr/ref 访问 整型常量`
        ————————————————————————————————————————————————————————————    
        (3) inline 
        
            [1] 无副作用 / type safety
                
                宏 max(a, b) 以 `2个操作数递增` 形式 调用
        ————————————————————————————————————————————————————————————
    

    3 尽可能 用 const

    `1 const + returnType / paraType / mem_func`
    
            ——————————————————————————————————————————————————————————————————————————————————————————————————
            (1) const + returnType  : 预防 `没意思的赋值` 等 client 意外错误
    
                    (a * b) = c; // a*b 的 result 上 调 operator=  
            ——————————————————————————————————————————————————————————————————————————————————————————————————
            (2) const + paraType    : pass by reference to const
            ——————————————————————————————————————————————————————————————————————————————————————————————————
            (3) const mem func      : 用于 const object
    
                1) `2个 同名 mem func 的 const/non-const 版本 是 重载`
                
                2) mem func 为 const 的 2 层含义 & mutable
                
                    [1] bitwise const: const mem func 不可改变 object 内 任何 non-static 成员 data
                            |
                            |   问题: bit const 语义可能不合适 
                            |/
                        1] bit const 语法对 + 语义不对
                            
                            指针成员 + 不改指针, 但 通过 `返回 ref` 的 `下标运算符` 改变 `指针 所指位置上的值` 
                    
                        2] bit const 语法不对 + 语义不对 
                            
                            成员 `不算` 对象 `真正状态`
                            |
                            |   引出 
                            |/
                    [2] logical constness: 逻辑含义上 const -> mutable 成员变量 可 修改 
            ——————————————————————————————————————————————————————————————————————————————————————————————————
    
    `2 当 const 和 non-const mem func 实现等价` 时, 
            
        `用 const 版本 实现 non-const 版本 以 `避免代码重复`
    
                [1] static_cast 转型 non-const obj 为 const obj
                
                    *this 从 C& 转型为 const C&
                
                [2] const_cast 移除 return type (const T&) 中的 const
    
    3 const + ptr/iterator 注意 `const_iterator == 内容 const`
    

    4 object 使用前 要先 初始化

    `1 ctor 最好 用 initialization list 初始化`
    
            原因
    
        `[1] 高效性`
        
            `不 显式 写 init. list, compiler 会隐式 填/调 init. list 
                => 隐式 ctor 浪费了(=> 效率低), 隐式 built-in 初始化为 undefined value`
            
        `[2] 必要性`
        
            `built-in type 成员变量` 若为 `const 或 reference`
                `只能用 init. list 而不能用 assignment`
    
        2点注意 
            
            [1] init. list 中要 `列出 所有 mem variable, 遗漏 built-in variable 将导致 undefined behavior`
            
            [2] 多份 init. list 导致 code 重复
            
                assignment 表现 像 initialize 一样好 的 mem data 
                    从 initialization list 移到 assignemnt -> `公共 private func`
    
    `2 初始化次序`
    
        `(1) 不同 编译单元` 内定义的 `non-local static 对象` 初始化 次序 undefined
    
                `问题: 编译单元 1 中 non-local static object 初始化 use 编译单元 2 中 non-local static object 可能 尚未初始化 -> undefined behavior`
                    |
                    |   解决: `单例模式`
                    |/
                用 `函数调用` 替换 "直接访问 non-local static object"
                    
                    2个优势
                        [1] `所得 ref 指向 已初始化 对象`
    
                        [2] `未 调用函数, 就不会引发 构造和析构` 成本, 真正的 non-local static object 可没这等便宜
                
                    机制
                        [1] non-local static object ->替换为 -> local static object + return ref to it
                        
                        [2] `func 内 的 local static object 会在 "该函数被调用 期间" "首次遇上该对象之定义式" 时 被 初始化`
    
    === 细节
    

    0 Introduction

        (1) 2 类 忠告
    
            ————————————————————
            [1] 一般 设计策略
            
            [2] 特殊特性
            ————————————————————
            
        (2) 设计: 多种 chiose 该选哪个?
    
            ————————————————————————————————————————————
            [1] Inheritance / Template ?
    
            [2] public/private/protected Inheritance ?
    
            [3] Composition / private Inheritane ?
    
            [4] mem func / non-mem func ?
    
            [5] pass by value / pass by reference ?
            ————————————————————————————————————————————
            
        (3) 棘手 问题
            ———————————————————————————————————————————————————————————
            [1] assignment      适当 return type 是 什么?
    
            [2] dtor            何时该为 virtual?
    
            [3] operator new    无法找到 enough memory 时, 该怎么办 ?
            ———————————————————————————————————————————————————————————
            
        (4) 准则 天生就有 例外
            
            => 每个条款 都有说明
    
        (5) 本书目的
    
            tell 如何回避 compiler 难以显露 的问题
    
        (6) 本书用途
    
            彻底了解 C++ 
                ————————————————————————————
                [1] how 行为
                [2] why 那么行为
                [3] how 运用其行为 形成优势
                ————————————————————————————
    
        0.1 术语 terminology
    
            (1) declaration: tell compiler 某物 name 和 type
    
                ——————————————————————————————————————————————
                4 种 declarations
                ——————————————————————————————————————————————
                [1] object      extern int x; 
                [2] function    std::size_t func(int n); 
                [3] class       class A; 
                [4] template    templete <typename T> class B;
                ——————————————————————————————————————————————
    
            (2) definition: 为 compiler 提供 declaration 所遗漏的 细节
    
                ——————————————————————————————————————————————————————————————
                [1] object                      : compiler 为 object 分配内存
    
                [2] function /function template : 提供 code body
    
                [3] class / class template      : 列出 member
                ——————————————————————————————————————————————————————————————
    
            (3) Initialization: 给予 对象初值
    
                用户自定义对象, Initialization 由 ctor 执行
                
                ——————————————————————————————————————————————————————————————————————————————————
                1) default ctor
                    无参 / 所有参数都有 缺省值
    
                2) ctor + explicit 
                    阻止 ctor 被执行 implicitly type conversion
    
                    禁止 compiler 执行 非预期 的 type conversion
    
                    除非 有更好的理由 允许 ctor 用于 implicitly type conversion, 否则 用 explicit
    
                3) copy ctor: 
                    以 同型 object 初始化 自我新对象
                    
                4) copy assignment
                    以 同型 object 初始化 自我已有对象
                ——————————————————————————————————————————————————————————————————————————————————
                
                class A
                {
                public:
                    A();                        // default ctor
                    A(const A& rhs);            // copy ctor
                    A& operator=(const A& rhs); // copy assignment
                };
    
                // 调 default ctor
                A a1; 
    
                // 调 copy ctor
                A a2(a1);
                A a3 = a1; // note: = 也可 用来 调 copy ctor
    
                // 调 copy assignment
                a1 = a2;
    
            (4) STL 
    
                容器 
                    vector 等
    
                迭代器
                    vector<int>::iterator 等
    
                算法
                    for_each
                    find
    
                function objects
                    行为像函数
                    重载 函数调用运算符 operator() 的 class
                
            (5) 函数 signature: paraType + returnType
    
                std::size_t f(int x); -> std::size_t (int)
                
            (6) interface
    
                [1] Java 中 Interface 为 语言元素
    
                [2] C++  中 接口 指 一般设计概念
    
                    1] 函数 signature
                    
                    2] class 的 可访问 成员 
    
            (7) client 人/物 
                
                对 1个 模板/组件, 其 `使用者 -> client` 
    
                function / class / template 的 client: 其 caller
    
            (8) undefined behavior
                
                ——————————————————————————————————————
                2 种导致 undefined behavior 的 case 
                ——————————————————————————————————————
                [1] 解引用 nullptr
                
                [2] 指涉 `无效 数组 index`
                ——————————————————————————————————————
                    int* p = 0; 
                    std::cout << *p ;  // [1]
    
                    char name[] = "lilei"; 
                    char c = name[10]; // [2]
    
        0.2 命名习惯
    
            (1) lhs / rhs: 形参名
                
                left/right-hand side
    
                对 mem func
                    左侧实参 由 this 表现, 只用 rhs
    
            (2) 指针/引用 缩写: 只取 `每个单词 首字母`
            
                ——————————————————————————————————
                [1] pt: 指向 `T 型 对象` 的 指针
                    |
                    |/
                    pointer to T
                
                [2] rw: reference to Widget
                ——————————————————————————————————
                
                Widget* pw;
    
                class GameCharacter;
                GameCharacter* pgc;
    
        0.3 线程 thread
    

    part1 让自己 习惯 C++

    1 视 C++ 为 1个 语言联邦

    1 C++ 是 `5 重范式`
    
        —————————————————————
        [1] Procedural
        [2] OO
        [3] GP
        [4] Functional
        [5] Metaprogramming
        —————————————————————
        
    2 C++ 可视为 `4个 子语言` (策略不同)
    
        (1) C
        
            preprocessor
            
            block
            statement
        
            built-in data type
            array
            pointer
    
        (2) OO C++
        
            class
            
            encapsulate
            inheritance
            polymorphism
                vf
    
    
        (3) Template C++ 
            
            GP / TMP
    
        (4) STL
        
            是 template 程序库
            
        值传递 还是 引用传递 ? (通常情况)
            ————————————————————————————————————————————————————————————————————————————————
            [1] C: build-in type     pass-by-value 更高效
    
            [2] OO C++               user-defined 存在 ctor 和 dtor, 
                                        pass-by-reference-to-const 往往更好
                                            |\
                                            |
            [3] Template C++        尤其如此, 因为你 `甚至不知道 所处理的 类型`
    
            [4] STL                 iterator / function object 都是在 C pointer 上 塑造出的
                                        pass-by-value 更高效
            ————————————————————————————————————————————————————————————————————————————————
        
    

    2 尽量以 const / enum / inline 替换 #define, 即 优选 compiler 而不是 preprocessor

    1 const 
        
        (1) 对 纯 `常量`, #define 常量名 常量值 / define Ratio 1.6
        
            问题
                常量名 不出现在 symbol talbe 
                    
                    => 预处理器 移走 `常量名` 
                    
                        => 编译错误 中的 常量值 很难排错
    
    
            解决
                `const 对象` // 定义(初始化) 通常放 头文件
    
                    const double Ratio = 1.6;
    
        ——————————————————————————————————————————————————————————————————————————————
        (2) #define + const pointer + const content 
                |
                |   替换为 
                |/
            const pointer + const content 
                
                + 想让 `const ptr(也是1种 object) 具有 global scope` (#define 的 本意) 
                                        |
                                        |
                                        |/ 
                                `定义/初始化 必须放 头文件`            
                
                #define NAME "lilei"
                        |
                        |/
                const char* const name = "lilei";
        ——————————————————————————————————————————————————————————————————————————————
        
        
            ———————————————————————————————————————————————————————————————————————
            Note:
            
            1)  const object 想具有 `global scope` -> `定义/初始化 必须放 头文件`
                                  |\
                                  | <=>
                                  |/
                                被 多源文件使用
    
                原因: const `默认`为 `内部链接` 
                        
                        =>              
                            1] 不能在 other 编译单元 ( 通过 extern 方式 ) 访问
    
                            2] 放 `头文件` 被 `多 源文件 #include`, 
                                    
                                不会报错 `重定义`
                                            |
                                            |   
                                            |/
                                    `外部链接 object` 定义/初始化放 `头文件` 
                                        
                                        + .h 被 >= 2 个源文件 #include 
                                        
                                            => 重定义
            ———————————————————————————————————————————————————————————————————————
            2) const 对象 `特殊性`
                
                [1] `默认`为 `内部链接` 
                        
                [2] 用 `extern (显式)声明` 可赋予其 `外部链接`
                
                [3] 其值不可改变 => 必须初始化
    
                [4] 2种初始化: 编译期 / 运行期初始化
            ———————————————————————————————————————————————————————————————————————
        
        (3) #define 无法创建 `class static 成员数据`, 不能提供 封装`
                                |
                                |   [1] 例外
                                |/      
                                为 `整型(int char bool)` 
    
                                + `不取地址`
    
            =>  可
                ————————————————————————————————————
                // 头文件
                1] `类 内 带初值 的 声明`, 而非定义
                
                2] `类内 使用` 而不用提供 定义式
                
                // 实现(源)文件
                3] `类外定义式 + 初值 从 声明式获取` 
                ————————————————————————————————————
    
                    // 头文件 
                    class A
                    {
                    private:
                        static const int N = 5; // 带初值 的 声明, 而非定义
                        int arr[N];             // 类内 使用 
                    };
    
                    // 实现(源)文件
                    const int A:N; // 类外定义式 + 初值 从 声明式获取  
    
        [2] 非例外: class static 成员数据 `初值 必须放 定义式` 
    
            // 头文件
            class B
            {
            private:
                static const double Factor; // 声明 头文件
            };
    
            // 实现文件
            const double B::Factor = 1.5;   // 定义, 放 实现文件
            
            |
            |   该 情形下, 若想在 `编译期获` 得 `class 内 常量值`
            |/
        solution: the enum hack
    
    2 enum hack
    
        class A
        {
        private:
            enum {N = 5}; // the enum hack
            int arr[N];
        };
    
        (1) enum hack 更像 #define 而不是 const
    
            enum/#define 取地址 不合法
    
                => `防止 通过 ptr/ref 访问 整型常量`
                
        (2) `enum 类型 值 可当 int 用`
        
        (3) enum hack 是 TMP 基础技术
    
    3 template inline func 代替 宏
    
        (1) 宏 像 函数, 却 `不招致` 函数调用 带来的 `额外开销`
    
        // 以 a b 较大值 调 func
        #define call_with_max(a, b) func( (a) > (b) ? (a): (b) )
    
        (2) `宏 中 所有实参 即使加 小括号, 也可能有问题` // 无聊事
    
        int a = 5, b = 0;
        call_with_max(++a, ++b);    // a 递增
        int a = 0, b = 5;
        call_with_max(++a, ++b);    // b 递增
            |
            |   solution
            |/
        template inline func 兼顾 效率 + type safety
        
            // 不知 T 是何 type, 所以用 pass by reference to const
            template <typename T>
            inline void call_with_max(const T& a, const T& b) 
            {
                f(a > b ? a: b);
            }
    

    3 尽可能 用 const

    1 const + returnType / paraType / mem_func
    
        (1) const + returnType  : 预防 `没意思的赋值` 等 client 意外错误
    
                (a * b) = c; // a*b 的 result 上 调 operator=  
    
        (2) const + paraType    : pass by reference to const
    
        (3) const mem func      : 用于 const object
    
            1) `2个 同名 mem func 的 const/non-const 版本 是 重载`
            
            2) mem func 为 const 的 2 层含义 & mutable
            
                [1] bitwise const: const mem func 不可改变 object 内 任何 non-static 成员 data
                        |
                        |   问题: bit const 语义可能不合适 
                        |/
                    1] bit const 语法对 + 语义不对
                        
                        指针成员 + 不改指针, 但 通过 `返回 ref` 的 `下标运算符` 改变 `指针 所指位置上的值` 
                
                    2] bit const 语法不对 + 语义不对 
                        
                        成员 `不算` 对象 `真正状态`
                        |
                        |   引出 
                        |/
                [2] logical constness: 逻辑含义上 const -> mutable 成员变量 可 修改 
        
            // 1)
            class Text
            {
            public:
                // const 版本: 用于 const object
                const char& operator[] (std::size_t pos) const { return text[pos]; }
              
                // non-const 版本: 返回类型 是 reference to char, 不是 char, 否则, tb[0] = 'x' 编译不过
                char& operator[] (std::size_t pos)  { return text[pos]; }
               
            private:
                std::string text;
            };
    
            void print(const Text& ctb)
            {
                std::cout << ctb[0];    // const 对象 => 调 const 版本
                    ctb[0] = 'x';       // 写 const char& => error      
            }
    
            Text tb("hello");
            std::cout << tb[0];         // non-const 对象 => 调 non-const 版本
    
            // 2-1-1) bit const 语法对 + 语义不对  
            class CText
            {
            public:
                char& operator[] (std::size_t pos) const { return text[pos]; } // bitwise const 声明, 但不恰当
            private:
                char* ptext;  // 与 C API 沟通
            };
    
            const CText ctb("hello"); 
            char* pc = &ctb[0];       
            *pc = 'I';               
    
            // 2-1-2) bit const 语法不对 + 语义不对 
            class CText
            {
            public:
                std::size_t length() const;
            private:
                char* ptext;
                std::size_t textLength;
                bool lengthValid;   
            };
    
            std::size_t
            CText::length() const
            {
                if( !lengthValid )
                {
                    textLength = std::strlen(ptext); // error, const mem func 内
                    lengthValid = true;              // 不能 改 non-static mem data
                }
            }
    
            // [2]
            class CText
            {
            public:
                std::size_t length() const;
            private:
                char* ptext;
                mutable std::size_t textLength;
                mutable bool lengthValid;
            };
    
            std::size_t
            CText::length() const
            {
                if( !lengthValid )
                {
                    textLength = std::strlen(ptext); 
                    lengthValid = true;              
                }
            }
    
    `2 当 const 和 non-const mem func 实现等价` 时, `用 const 版本 实现 non-const 版本 以 `避免代码重复`
    
        [1] static_cast 转型 non-const obj 为 const obj
            
            *this 从 C& 转型为 const C&
                
        [2] const_cast 移除 return type (const T&) 中的 const
        
        class Text
        {
        public:
            const char& operator[] (std::size_t pos) const { return text[pos]; }
            char& operator[](std::size_t pos) { return text[pos]; }
        private:
            std::string text;
        };
            |
            |   const 版本 实现 non-const 版本
            |/
        class Text
        {
        public:
            const char& operator[](std::size_tpos) const { return text[pos]; }
            
            char& operator[](std::size_t pos)
            {
                return
                    const_cast<char&>(
                        static_cast<cosnt Text&>(*this)
                            [pos]
                    );
            }
        private:
            std::string text;
        };
    
    3 const + ptr/iterator
    
        [1] const T* = T const *
    
        [2] Note
            
            const std::vector<int>::iterator // 迭代器 const 
    
            std::vector<int>::const_iterator // 内容 const 
    

    4 object 使用前 要先初始化

    `1 ctor 最好 用 initialization list 初始化`
        
        class Phone{ ... };
    
        class ABEntry // "Address Book Entry"
        {
        public:
            ABEntry(const std::string& addr_,
                    const std::list<Phone>& phones_);
    
        private:
            std::string addr;
            std::list<Phone> phones;
            int num;
        };
    
        ABEntry::ABEntry(const std::string& addr_,
                const std::list<Phone>& phones_)
        {
            addr = addr_;
            phones = phones_;
            num = 0;
        }
    
        // 等价于
        ABEntry::ABEntry(conststd::string&addr_,
                conststd::list<Phone>&phones_)
            : addr(), phones(), num(undefined_val)
        {
            addr = addr_;
            phones = phones_;
            num = 0;
        }
    
        // 等价较佳写法: 效率较高
        ABEntry::ABEntry(conststd::string&addr_,
                conststd::list<Phone>&phones_)
            :addr(addr_), // 调 copy ctor
            phones(phones_),
            num(0)
        { }
    
    
        // init. list 中 可 default 构造: nothing 作 arg
        ABEntry::ABEntry(conststd::string&addr_,
                conststd::list<Phone>&phones_)
            :addr(), // 调 default ctor
            phones(),
            num(0)
        { }
    
        `(1) 总是用 init. list: 有时 绝对必要, 又往往比 assignment 更高效`
        
        `(2) built-in type 成员变量` 若为 `const 或 reference, 只能用 init. list 而不能用 assignment`
    
            built-in type 成员变量 non-const / non-reference 时, 
            init. list / assignment 均可用, 且 成本相同
    
            class A
            {
            public:
                A(): n(5), name("") {}
                A(int N, std::string& name_)
                    : n(N), name(name_) { }
            private:
                const int n;
                std::string& name;
            };
    
        `(3) init. list 中 要列出 所有 mem variable, 遗漏 built-in variable 将导致 undefined behavior`
    
            ABEntry::ABEntry(conststd::string& addr_,
                    conststd::list<Phone>& phones_)
                // or // :addr(), phones()
            { }
    
            // 等价于
            ABEntry::ABEntry(conststd::string& addr_,
                    conststd::list<Phone>& phones_)
                : addr(), phones(), num(undefined_val)
            { }
    
        `(4) 多份 init. list 导致 code 重复 -> assignment 表现 像 initialize 一样好
            
                的 mem data 从 initialization list 移到 assignemnt -> 公共 private func`
    
                    尤适于`成员变量 初值 由 文件 或 数据库 读入`
                    
    `2 初始化次序`
                
        (1) 5 种 static 对象
            file scope 内
            namespace scope 内
            global
            class 内
            func 内
    
    
            // your file
            class FileSystem
            {
            public:
                std::size_t nunDisks() const;
            };
    
            extern FileSystem tfs; // 给 client 使用的 对象 
    
            // client file
            class Directory
            {
            public:
                Directory( param );
            };
    
            Directory::Directory( params )
            {
                std::size_t disks = tfs.numDisks(); // 使用 fs 对象
            }
    
            Directory tempDir( params );
                |
                |   单例模式
                |/
            // 
            class FileSysten { ... };
    
            FileSystem& tfs() 
            {
                static FileSystem fs; 
                return fs;
            }
    
            // 
            class Directory { ... };
            Directory::Directory (params)
            {
                std::size_t disks = tfs().numDisks();
            }
            Directory& tempDir(params)
            {
                static DIrectory td(params);
                return td;
            }
    
            tempDir(params)
    
        (2) `引用返回` 函数 可 inline
    
            而 `non-const static 对象, 不论 local 还是 non-local, 多线程 下 "等待某事发生" 都有麻烦`
                |
                |   解决 
                |/
    
            `单线程启动阶段 (single-thread startup portion) 手工调所有 reference 函数`, 
                
                可 `消除 初始化` 相关的 `race condition`
    
                `set_new_handler`
    
                ```
                new_handler 是个 function pointer, 
                所指 函数 无参 / 无 return_value
    
                set_new_handler:
                    para 
                         new_handler: 指向 operator new 无法分配 足够内存时, 该被调用的函数
                    return value
                         new_handler: 指向 set_new_handler 被调用前 的 new_handler 函数
    
        (3) base class 先于 derived class 初始化
    
        (4) class mem data 以其 声明顺序 初始化
        
    3 build-in / 之外, 要手工初始化 / 由 ctor 初始化
    
        原因
            C / non-C part of C++: C++ 不保证 / 保证 初始化 它们
    
        eg: array / vector
    

    相关文章

      网友评论

          本文标题:Effective C++ 1: 让自己 习惯 C++

          本文链接:https://www.haomeiwen.com/subject/uilrmltx.html