美文网首页
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

相关文章

  • 要读的书书名

    1, 重读《Effective C++》《 More Effective C++》《by Scott Meye...

  • 2018-09-16

    Effective c++第三版 让自己习惯C++ 条款01:视C++为一个语言联邦 C++是一个同时支持过程形式...

  • 【Effective C++(1)】让自己习惯C++

    01 视C++为一个语言联邦 一开始C++只是C加上OOP特性,但随着C++成熟就不再只是C with class...

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

    part1 让自己 习惯 C++ 1 视 C++ 为 1个 语言联邦 2 尽量以 const / enum / i...

  • 《Effective C++》的做法原理剖析

    在本篇文章中,会去写一些小实验,以实现Effective C++中提到的一个原则。 让自己习惯C++ 1.视C++...

  • Effective C++ 让自己习惯C++

    1.C++可视为一个由相关语言组成的联邦,而不是单一语言。每一个次语言都有自己的规约,编程守则也视情况而变化。 C...

  • C++ ---- 条款

    摘抄自Effective C++ 改善程序与设计的55个具体做法(第三版 中文版) 让自己习惯C++ 条款01:视...

  • 任务列表

    C++ 《C++ primer》、《STL源码解析》、《effective C++》、《深度搜索c++对象模型》 ...

  • (Boolan) Week1

    1.前言 c++是由 c++语言和标准库两部分组成。 推荐书籍: Effective c++ The c++ st...

  • C++笔记

    C++相关书籍:C++ primer,c++标准程序库,Effective C++(Scott Meyers),C...

网友评论

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

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