美文网首页
Effective C++ 5: Implement

Effective C++ 5: Implement

作者: my_passion | 来源:发表于2021-07-22 23:20 被阅读0次
适当 提取 class (template) 定义 和 function (template) 声明, 
    是花费心力 最多的 2件事, 但 完成它们,
        实现 大多就 直接了当

1 尽可能 postpone/延后 object definition

    1   `延后 对象 定义, until 必须使用 对象 前一刻`   
        
            `对象 定义过早`
                             |
                             |  如 因 
                             |/                         
                ————————————————————————————————————————————            
                避免 
                    ————————————————————————————————————————
                    [1] 构造/析构 不必要的 对象   
                    
                        可能因 `抛出异常, 而 没被 真正使用`
                    ————————————————————————————————————————
                    [2] `毫无意义 的 default 构造`
                ————————————————————————————————————————————
                
    2   `真正动作 提取到 函数`
    
    3   `构造时 初始化 效率高` 于 `default 构造 + 赋值`
                                        |
                                        |   可能 
                                        |/
                                      毫无意义 
                                      
    4   `对象 只在 循环内 使用, 定义于 循环内还是外 更好?`
    
            外: 1 default ctor + 1 dtor + n assignment
            内: n ctor + n dtor

2 少 转型

    转型(cast) 破坏了 type system, 可能导致 `任何种类 的 麻烦`

    1   类型转换之 `新/旧 style

        ——————————————————————————————————————————————————————————————————
        old-style
            C 风格
                (T) expression
                
            函数风格
                T (expression) // T 可为 C++ 中 class
        ——————————————————————————————————————————————————————————————————
        new-style / C++ style
            ——————————————————————————————————————————————————————————————
            const_cast<T>( expression )
                `去除 常量性`
            ——————————————————————————————————————————————————————————————
            static_cast<T>( expression )
                强迫 隐式转换
                    `无法实现 去除常量性: const obj -> non-const obj`
            ——————————————————————————————————————————————————————————————  
            dynamic_cast<T>( expression )
                安全 向下转型
                    唯一无法由 old-style cast 实现的 new-style cast
            ——————————————————————————————————————————————————————————————
            reinterpret_cast<T>( expression )
                欲 低级转型, 不可移植
        ——————————————————————————————————————————————————————————————————
        
        new-style 更受欢迎的原因:

            1) code 易 被 (人工 / grep 等 工具 ) 辩识

            2) 转型目标越窄, compiler 越可能诊断出错误
            
    2   `唯一需  旧式转型 的 时机: 单 para explicit ctor`

    3   `显式转型 未必需要`
    
    4   类型转换 往往令 `compiler 编译出 运行期 执行的 code` 
        
            `对象 ( Derived )` 可能拥有 `> 1个 地址`
                
                以 Base*/Derived* 指向    

                    offset 在 运行期 被施行于 Derived* 身上, 以 取得 正确的 Base* 

                        => `不要假设 对象 在 C++ 中 如何布局`
            
                                `除非 从 某个 compiler 说明文档里 明确得知`

    5   对象 `转型结果` 是 `被转 part` 的 `临时 copy`

        `derived class 内 vf 第1个动作 是调用 base class 相应函数`
            
            如 static_cast<Base>(*this).vf
                
                对 *this 对象之 `base part 的 临时 copy 调 vf` 
                    
                    Note
                        `base part 没被改, derived part 被改` 
                    
                            => `当前对象` 成 `伤残` 状态

                                |
                                |   解决 
                                |/
                        Base class name 限定
                        
    6   `dynamic_cast 之 低效 / 可能需要的场景 / 可能的 替代方案 / 必须避免 的 场景`
        
        (1) 低效
    
            至少有1个 实现 基于 `class name 之 字符串比较`

                => n 层单继承体系
                    
                    可能需 n 次 strcmp 调用: 比较 class name
        
        (2) 可能需要的场景
            
            ————————————————————————————————————————————————————————————————————————
            [1] `多重继承 之 恢复接口` 
            
            [2] `用 non-virtual function + base class ptr/ref 处理 继承体系 中 obj`
            ————————————————————————————————————————————————————————————————————————
            
        (3) 可能的 替代方案

            [1] 用 vf + base class 中 vf 用 缺省实现 (如 do nothing)`

                    + 各 Derived class 的 vf override
                    
            [2] 用 多个容器, 每个 放 一种 派生类 SP
            
        (4) 必须避免 的 场景: 连串/if-else dynamic_cast
        
                继承体系 变, client code 就要变

3 避免返回 handles to object internals

    `handle: ref / ptr / iterator`
    
        用来 访问 对象
    1   `return 指向 internal handle`
        
        ————————————————————————————————————————————————————————————————————————
        3 大风险
            ————————————————————————————————————————————————————————————————————
            [1]  `封装性` 降低
            
                Note: `不该 令 mem func 返回的 handle 指向 访问权限更严的 mem`
            
                    原因: 该 mem 访问权限 放松为 和 mem func 一样 
                
                        如: public mem func 却 return hanlde 指向 private mem data / func

                            => mem data / func 的 访问权限由 private 放松为 public
                        |
                        |   优化 
                        |/
                    `蓄意` + `有限度` 地 `放松封装性`
                      |         |
                      |         |/
                      |     2] return handle to const => 不允许修改 
                      |/
                    1] 愿意让 client 看到 对象 internal
            ————————————————————————————————————————————————————————————————————        
            [2] const mem func + 
                
                    `return handle to non-const` 可 `改变 对象状态` 
                                    |
                                    |   如 
                                    |/
                                non-const 引用                                            
            ————————————————————————————————————————————————————————————————————                                            
            [3] handle `lifetime 长于 所指 对象`  
                
                    `handle 悬挂`
                             |
                            指向 不再存在的对象
            ————————————————————————————————————————————————————————————————————
            
    2   有时必须 让 mem func 返回 internal handle

            STL 容器: string / vector
            
                下标运算符 operator[] 返回 ref指向 `容器内数据`

4 为 exception-safe 尽力

    1   `抛出异常` 时 `3 种 保证`

        (1) `基本承诺`

                不败坏数据 + `程序状态 任意(半构造状态)`

        (2) `强烈保证`
        
                `程序状态 不变`
            
                    `失败 时 回滚` 到 之前状态

        (3) `nothrow 保证: 承诺 绝不抛出异常
        
                内置类型 上操作
                    
                    空白异常明细 empty exception specification // int f() throw();

                        不是指 不抛出异常
                        
                        而是指 `抛出异常 时, 会有 意想不到的结果`
    
    2   `异常安全 2 个条件`

            [1] `不 泄漏资源`
                        |
                        |   场景 
                        |/
                    new 抛出异常 + `unlock 被跳过`     
                    
                        => mutex 永远被锁住
                        |
                        |   解决 
                        |/
                    `资源管理 class ( RAII 类 ) Lock: 确保 mutex 被及时释放`
                        
            [2] `不 败坏数据`
                        |
                        |   场景
                        |/
                    `delete p + count 递增 + new 抛出异常
                    
                        => p 指向 已删除对象
                        |
                        |
                        |/
                    [1] `raw pointer -> shared_ptr ( 其实也属于 资源管理: 避免 delete 被跳过 ) 
        
                    [2] 重排语句次序: lock -> 操作 ( new ) -> ++ 

    3   `强烈保证: copy and swap`

            `copy 欲 修改对象` -> `修改 副本` -> 在 不抛出异常的 `swap 中 置换 原件 和 副本` 
            
                + `pImpl 手法` 
                    
                    [1] 将 `属于对象 的 data` 从 `源对象 放到 另一对象`

                    [2] 用 struct 而不是 class: 封装性 已由 spImpl 是 private 而获得

5 深入理解 inlining

    `inlining: 编译期` 将 `函数调用` 替换为 `函数本体`

    1   inline 函数 `优缺点`

        (1) 优
        
            [1] 比 `宏` 好
            
            [2] 无 函数调用 之 `额外开销`
            
            [3] 让 compiler 有 `编译优化` 机会

        (2) 缺
            
            [1] `代码膨胀`
                
                函数本体 比 函数调用 的 compiler 产出码 多
                 
            [2] `换页 paging -> 效率损失`
            
                inline 过多

    2   `inline vs. template`

        (1) (通常) 均放 `头文件`

            原因
                `编译期行为`

                    compiler 编译期 `必须知道 inline 函数 / template 长什么样子`

        `(2) template 实例化 与 inlining 无关`

            若想 `template 所有可能 实例化 版本
                都 inline`, 才 将 template 声明为 inline

    3   `compiler 拒绝 inline 的 case`

        [1] 函数太复杂

        [2] vf

            virtual: 等到 运行期 才确定 调用哪个函数

            inline : 执行前 先将 函数调用 替换为 函数本体

            两者矛盾

        [3] `函数指针` 之 函数调用

        [4] `ctor / dtor`

6 最小化 文件 间 编译依赖


                     含 `实现细节`    不含 `实现细节 ( 数据 )`
                           |\         |\
                           |          |
                           |          |
            `定义式` 必须 `直接` 或 `间接` 列出 实现细节`
                |\
                |
                |
    1   `class 定义文件` 与 `#include 文件` 的 `编译依赖`
                                |
                                |   若 含 
                                |/
                            [1] class 定义 的 实现细节 
                                
                                    => 实现细节 变, 则 class 的 client 要变 
                                        |                |
                                        |   如           |   class 定义 文件 
                                        |/               |/
                                    成员 Date     Interface(类)
                                    
                            [2] #include 间 可能 `连串 依赖` 
                            
                            [3] #include 标准库 头文件 不太可能成 编译瓶颈                     

        (1) `class 定义式` 含 `实现细节`
            
                => `修改 class 的 implement` 时, class 的 client 就` 需要 `重新编译`
        
        (2) `class 定义式` 不含 `实现细节` 
            
                `修改 class 的 interface 时, class 的 client 才` 需要 `重新编译`
                    
                    `pImpl` 手法 实现 真正的 `接口 与 实现 分离`

    2   `最小化 编译依赖` 
            
            本质

                `声明 的 依赖性` 替换 `定义 的 依赖性`

                    头文件 应 尽可能 `自我满足`, 万一做不到,
                    
                        则 尽量依赖 other 文件内 声明式 (而非 定义式)
                        
            方法

                [1] `用 ref/ptr` 能完成 任务, 就不要用 object

                [2] `尽量用 class 声明式`, 除非 `真正需要 class 定义式`

                        `by value` 方式 `传参 或 返回值` 并 `不需要 class 定义式`

                            原因: `client 调用 function 前, 必先曝光( 实现出 ) class 定义式`

                                class Date;
                                Date today();
                                void clearAppointment(Date d);

                [3] `为 声明式 和 定义式 提供不同的 头文件`

                    // 3    Date 的 client.cpp
                    #include "dateDecl.h" // 而不是 #include "date.h"
                    Date today();
                    void clearAppointment(Date d);

    3   2 种 Handle class 
        
        3.1 `Handle class & HandleImpl class` 
            
            将其 所有函数 `转交` 给相应 `实现类 的 相应函数`

                两者 成员函数 接口完全相同

            (1) 文件结构 
                ————————————————————————————————————————————————————————————————————————————————————————————————————————————
                1   Interface.h         Interface 类定义式
                ————————————————————————————————————————————————————————————————————————————————————————————————————————————
                2   InterfaceImpl.h     InterfaceImpl 类定义式
                ————————————————————————————————————————————————————————————————————————————————————————————————————————————
                3   Interface.cpp       Interface 实现文件
                
                                            Interface 成员函数 `转交` 给 InterfaceImpl 成员函数 
                                            
                                                Interface::Interface(const std::string& name, const Data& birthday) : spImpl(
                                                    new InterfaceImpl(name, birthday) 
                                                    ) {}

                                                std::string Interface::name() const { 
                                                    return spImpl->name(); }
                ————————————————————————————————————————————————————————————————————————————————————————————————————————————
                
        3.2 `Interface class 即 abstract base class `
                
            (1) 无 成员变量 + ctor
                
                    只有 1个 virtual dtor + 一组 pure virtual 函数, 
                        
                        描述 derived class 的 接口

                Note
                    C++ Interface class / abstract base class 与 Java 中 Interface 区别:

                        C++ Interface class 中 
                        
                            `允许实现 成员变量 和 成员函数
                                                    |
                                                    |    
                                                    |/
                                                non-virtual function
                                                    |
                                                    |   如 
                                                    |/
                                                Factory Method: static mem func 

            (2) `client 必须 以 Interface class 的 ptr / ref 写程序`
                
                    原因: `无法` 对 `内含 pure vf` 的 Interface class `构造 新对象`

            (3) `client 必须` 能为 相应 `derived class 创建对象`

                    `Factory Method`, 扮演 `真正将被 实例化` 的 `derived class` 的 `ctor` 的角色
                                |
                                |    
                                |/
                        [1] 也叫 virtual ctor
                        [2] 通常是 static mem func 

                `支持 Interface class 接口 的` 

                    `Implement class 必须被定义 出来` + `真正的 ctor 必须被调用`
                        |
                        |       这一切都在 `Factory Method 实现文件` 中 发生
                        |
                        |/
                    Interface 的 Derived 类 

            (4) 源文件结构 
            
                ————————————————————————————————————————————————————————————————
                1   client.cpp:     创建 对象, 支持 Interface 接口
                ————————————————————————————————————————————————————————————————
                2   Interface.h:    Interface 声明文件: 
                                        virtual dtor
                                        Factory Method (non-virtual func) 
                                        pvf
                ————————————————————————————————————————————————————————————————
                3   Interface.cpp:  Interface 实现文件
                                        只含 dtor
                ————————————————————————————————————————————————————————————————
                4   Imp.h:          Imp 声明 (头) 文件
                                        放 实现细节( 数据  )
                ————————————————————————————————————————————————————————————————
                5   Imp.cpp:        Imp 实现 源文件
                ————————————————————————————————————————————————————————————————
                6   factory.cpp:    Factory function 实现文件 : 
                                        调 真正 ctor:  new Derived 
                                        
                                            return
                                                std::shared_ptr<Interface>( 
                                                    new Imp(name, birthday) );
                ————————————————————————————————————————————————————————————————            
                            
        3.3 `Handle class 比较 Interface class`

            (1) 利 / 弊 / 替代为 concrete class / inline

                1) 利
                
                    `解耦 interface 与 implement`
                        
                        => 降低文件间 编译依赖`

                2) 弊
                    
                    ———————————————————————————————————————
                    [1] `运行期 速度损失`: 间接 访问/调用
                    
                    [2] `每个对象 多付 若干内存`
                    ———————————————————————————————————————

                3) 当 弊 > 利 
                    
                        => 用 concrete class 替换 两者
                        
                4) 一旦脱离 inline 均 无法有 太大作为
                
            (2) Handle class

                1)  `间接访问` 
                    
                        代价 
                            `间接层: 必须通过 pImpl ptr`

                2) `对象 内存` 增加 
                    
                        `pImpl ptr`

                3) pImpl ptr 必须 在 Handle class 内 初始化
                    
                        指向 `动态分配的 impl object`

                            动态内存`分配 / 释放` 的 `额外开销`
                            `bad_alloc` 的 可能性

            (3) Interface class

                1) 调 vf 
                    
                        代价: `间接跳跃`

                2) derived 对象 内存增加 
                        
                        vptr        
=== 详细 

1 尽可能 postpone/延后 object definition

        std::string encryptPassword(const std::string& password)
        {
            std::string encrypted; // 定义过早
            if( password.length() < MinPwLen )
                std::throw std::logic_error("password is too short");

            ... // 真正加密动作, 比如 将 加密后的密码放 encrypted 内

            return encrtpted;
        }
            |
            |   1   `延后 对象 定义, until 必须使用 对象 前一刻`   
            |/
        std::string encryptPassword(const std::string& password)
        {
            if( password.length() < MinPwLen )
                std::throw std::logic_error("password is too short");
            
            std::stringencrypted;//定义过早

            ... // 真正加密动作

            return encrtpted;
        }
            |
            |   2   `真正动作 提取到 函数`
            |/
        void encrypt(std::string& s); 

        std::string encryptPassword(conststd::string& password)
        {
            ...
            std::string encrypted; // 用 default ctor 定义并初始化
            encrypt = password; // assignment

            encrypt(encrypted);

            return encrtpted;
        }
            |
            |   3   `构造时 初始化 效率高` 于 `default 构造 + 赋值`
            |/
        std::string encryptPassword(conststd::string& password)
        {
            ...
            std::string encrypted(password); // 用 copy cotr 定义并初始化

            encrypt(encrypted);

            return encrtpted;
        }

    4   `对象 只在 循环内 使用, 定义于 循环内还是外 更好?`

        // 定义于 循环 外
        Widget w;
        for (int i = 0; i < n; ++i )
        {
            w = 取决于 i 的 某值;
        }

        // 定义于 循环 内
        for (int i = 0; i < n; ++i )
        {
            Widget w(取决于 i 的 某值);
        }

        成本:
            外: 1 default ctor + 1 dtor + n assignment
            内: n ctor + n dtor

                => 若 1 assignment 成本 < 1 ctor + dtor 成本,
                则 内做法 好
            
            可通过 test 两种实现的 运行时间 来比较

2 少 转型

    1   类型转换之 `新/旧 style
    
    2   `唯一需  旧式转型 的 时机: 单 para explicit ctor`

        class Widget
        {
        public:
            explicit Widget(int size)
        };

        void f(const Widget& w);

        f( Widget(5) );  // 旧式风格

        f(static_cast<Widget>(5) ); // C++ 风格

    3   `显式转型 未必需要`

        int x, y;
        double d = static_cast<double>(x) / y;
            |
            |
            |/
        double d = x;
        d = d / y;

    4   类型转换 往往令 `compiler 编译出 运行期 执行的 code` 
        
            `对象 ( Derived )` 可能拥有 `> 1个 地址`
                
                以 Base*/Derived* 指向    

                    offset 在 运行期 被施行于 Derived* 身上, 以 取得 正确的 Base* 

                        => `不要假设 对象 在 C++ 中 如何布局`
            
                                `除非 从 某个 compiler 说明文档里 明确得知`
            
        Derived d;
        Base* pb = &d; // Derived* 隐式转换为 Base*

    5   对象 `转型结果` 是 `被转 part` 的 `临时 copy`

            static_cast<Base>(*this).vf
                
                对 *this 对象之 `base part 的 临时 copy 调 vf` 
                    
                        `base part 没被改, derived part 被改` 
                    
                            => `当前对象` 成 `伤残` 状态

                        
                        
        class B
        {
        public:
            virtual void vf() { /* ... */ }
        };

        class D: public B
        {
        public:
            virtual void vf()
            {
                static_cast<B>(*this).vf();
            
                // process D part
            }
        };
            |
            |   Base class name 限定
            |/
        class D: public B
        {
        public:
            virtual void vf()
            {
                B::vf(); // (*this).B::vf(); 
            
                // process D part
            }
        };

    6   `dynamic_cast 之 低效 / 可能需要的场景 / 可能的 替代方案 / 必须避免 的 场景`
        
        (1) 低效
    
            至少有1个 实现 基于 `class name 之 字符串比较`

                => n 层单继承体系
                    
                    可能需 n 次 strcmp 调用: 比较 class name

        (2) 可能需要的场景
            
            ————————————————————————————————————————————————————————————————————————
            [1] `多重继承 之 恢复接口` 
            
            [2] `用 non-virtual function + base class ptr/ref 处理 继承体系 中 obj`
            ————————————————————————————————————————————————————————————————————————
            
            class B { ... };

            class D: public B
            {
            public:
                void f();
            };

            typedef
                std::vector<std::shared_ptr<B> > Vspb;
            Vspb vspb;
            // ...

            for(Vspb::iterator = vspb.begin(), iter != vspb.end(), ++iter)
            {
                if(D* pd = dynamic_cast<D*>(iter->get() ) )
                    pd->f();
                // ...
            }

        (3) 可能的 替代方案

            [1] 用 vf + base class 中 vf 用 缺省实现 (如 do nothing)`

                    + 各 Derived class 的 vf override
                    
            [2] 用 多个容器, 每个 放 一种 派生类 SP
            
                    // [1]
                    class B 
                    { 
                    public:
                        virtual void vf() { } // 缺省实现: 啥也不干
                    };

                    class D: public B
                    {
                    public:
                        virtual void vf() { /* ... */ }
                    };

                    typedef
                        std::vector<std::shared_ptr<B> > VSPB;

                    VSPB vspb; 
                    // ...

                    for(VSPB::iterator = vspb.begin(), iter != vspb.end(), ++iter)
                        (*iter)->vf();

        (4) 必须避免 的 场景: 连串/if-else dynamic_cast
        
                继承体系 变, client code 就要变
            
            typedef
                std::vector<std::shared_ptr<B> > Vspb;
            Vspb vspb;
            // ...

            for(Vspb::iterator = vspb.begin(),
                iter != vspb.end(), ++iter)
            {
                if(D* pd = 
                    dynamic_cast<D*>(iter->get() ) )
                    // ...
                else if(D2* pd =
                    dynamic_cast<D2*>(iter->get()))
                    // ...
                // ...
            }

3 避免返回 handles to object internals

    1   3 大 风险
    
        [1] `封装性` 降低 + [2]
        
            class Point
            {
            public:
                Point(int x, int y);
                void setX(int x_);
            };

            struct RectData
            {
                Point ulhc; // upper left-hand corner
                Point lrhc;
            };

            class Rectangle
            {
            private:
                std::shared_ptr<Rectangle> pData;
            public:
                Point& upperLeft() const { return pData->ulhc; }
            };

            Point p1(0,0);
            Point p2(100, 100);
            
            const Rectangle rec(p1, p2);
            rec.upperLeft().setX(50); 


        [2] `蓄意` + `有限度` 地 `放松封装性`

            class Rectangle
            {
            public:
                const Point& upperLeft() const { return pData->ulhc; }
            };

        [3] `handle dangling`

    2   有时必须 让 mem func 返回 internal handle

        STL 容器: string / vector
        
            下标运算符 operator[] 返回 ref指向 `容器内数据`

4 为 exception-safe 尽力

    1   `抛出异常` 时 `3 种 保证`

        (1) `基本承诺`

                不败坏数据 + `程序状态 任意(半构造状态)`

        (2) `强烈保证`
        
                `程序状态 不变`
            
                    `失败 时 回滚` 到 之前状态

        (3) `nothrow 保证: 承诺 绝不抛出异常
        
                内置类型 上操作
                    
                    空白异常明细 empty exception specification // int f() throw();

                        不是指 不抛出异常
                        
                        而是指 `抛出异常 时, 会有 意想不到的结果`
    
    2   `异常安全 2 个条件`

            [1] `不泄漏资源`

            [2] `不败坏数据`

                class PMenu
                {
                public:
                    void f(std::istream& img);
                private:
                    Mutex mutex;
                    Image* p;
                    int count;
                };

                void PMenu::f(std::istream& img);
                {
                    lock(&mutex);
                    delete p;
                    ++count;
                    p = new Image(img);
                    unlock(&mutex);
                }

                1) 泄漏资源
                
                    new 抛出异常 + `unlock 被跳过` 
                        
                        => mutex 永远被锁住

                2) 败坏数据
                
                    `delete p + count 递增 + new 抛出异常
                        
                        => p 指向已删除对象

    3   解决 `资源泄漏`

        `资源管理 class ( RAII 类 ) Lock: 确保 mutex 被及时释放`

            void PMenu::f(std::istream& img);
            {
                Lock lk(&mutex);
                delete p;
                ++count;
                p = new Image(img);
            }

    4   解决 `数据败坏`

        [1] `raw pointer -> shared_ptr ( 其实也属于 资源管理: 避免 delete 被跳过 ) 
        
        [2] 重排语句次序: 操作 ( new ) 后再 ++ `

            class PMenu
            {
            public:
                void f(std::istream& img);
            private:
                Mutex mutex;
                std::shared_ptr<Image> sp;
                int count;
            };

            void PMenu::f(std::istream& img);
            {
                Lock lk(&mutex);
                sp.reset(new Image(img) );
                ++count;
            }

    5   `强烈保证: copy and swap`

            `copy 欲 修改对象` -> `修改 副本` -> 在 不抛出异常的 `swap 中 置换 原件 和 副本` 
            
                + `pImpl 手法` 
                    
                    [1] 将 `属于对象 的 data` 从 `源对象 放到 另一对象`

                    [2] 用 struct 而不是 class: 封装性 已由 spImpl 是 private 而获得
                
        struct Impl
        {
            std::shared_ptr<Image> sp;
            int count;
        };

        class PMenu
        {
        public:
            void f(std::istream& img);
        private:
            Mutex mutex;
            std::shared_ptr<Impl> spImpl;
        };

        void PMenu::f(std::istream& img);
        {
            using std::swap;
            
            Lock lk(&mutex);
            
            // (1) copy
            std::shared_ptr<Impl> 
                spImplCpy( new Impl(*spImpl) );
            
            // (2) modify copy
            spImplCpy->sp.reset( new Image(img) );
            ++spImplCpy->count;
            
            // (3) swap(orignal, copy)
            swap(spImpl, spImplCpy);
        }

5 深入理解 inlining

    `inlining: 编译期` 将 `函数调用` 替换为 `函数本体`

    1   inline 函数 `优缺点`

        (1) 优
        
            [1] 比 `宏` 好
            
            [2] 无 函数调用 之 `额外开销`
            
            [3] 让 compiler 有 `编译优化` 机会

        (2) 缺
            
            [1] `代码膨胀`
                
                函数本体 比 函数调用 的 compiler 产出码 多
                 
            [2] `换页 paging -> 效率损失`
            
                inline 过多

    2   `inline vs. template`

        (1) (通常) 均放 `头文件`

            原因
                `编译期行为`

                    compiler 编译期 `必须知道 inline 函数 / template 长什么样子`

        `(2) template 实例化 与 inlining 无关`

            若想 `template 所有可能 实例化 版本
                都 inline`, 才 将 template 声明为 inline

                // inline template 都放 头文件的例子
                class Interface
                {
                public:
                    int age() const { return age; }
                private:
                    int age;
                };

                template<typename T>
                inline const T& std::max(const T& a, const T& b)
                {
                    return a < b ? b : a;
                }
                
        (3) inline 只是申请, compiler 可 拒绝

    3   `compiler 拒绝 inline 的 case`

        [1] 函数太复杂

        [2] vf

            virtual: 等到 运行期 才确定 调用哪个函数

            inline : 执行前 先将 函数调用 替换为 函数本体

            两者矛盾

        [3] `函数指针` 之 函数调用

        [4] `ctor / dtor`

            class Base
            {
            private:
                std::string b1;
            };

            class Derived: public base
            {
            public:
                Derived() {} // Derived ctor 并不是空的
            private:
                std::string d1, d2;
            };
        
            `compiler 真正造出来的 code` 可能为
        
            Derived::Derived
            {
                Base::Base();
                try { d1.std::string::string(); }
                catch(...)
                {
                    Base::~Base();
                    throw;   // 销毁 base part 并 传播异常 
                }

                try { d2.std::string::string(); }
                catch(...)
                {
                    d1.std::string::~string();
                    Base::~Base();
                    throw;  
                }
            }

    4   何时不该 inline

        (1) `升级版 函数` 不该 inline
            
            inline 函数 改变 -> 其 client code 必须重新编译

            non-inline 改变 -> 其 client code 只需 重新连接

            动态链接: 更不知不觉

        (2) `debug 版 code` 禁止 inline

6 最小化 文件 间 编译依赖


                     含 `实现细节`    不含 `实现细节 ( 数据 )`
                           |\         |\
                           |          |
                           |          |
            `定义式` 必须 `直接` 或 `间接` 列出 实现细节`
                |\
                |
                |
    1   `class 定义文件` 与 `#include 文件` 的 `编译依赖`
                                |
                                |   若 含 
                                |/
                            [1] class 定义 的 实现细节 
                                
                                    => 实现细节 变, 则 class 的 client 要变 
                                        |                |
                                        |   如           |   class 定义 文件 
                                        |/               |/
                                    成员 Date     Interface(类)
                                    
                            [2] #include 间 可能 `连串 依赖` 
                            
                            [3] #include 标准库 头文件 不太可能成 编译瓶颈                     

        (1) `class 定义式` 含 `实现细节`
            
                => `修改 class 的 implement` 时, class 的 client 就` 需要 `重新编译`

            #include <string>
            #include "date.h"

            class Interface
            {
            public:
                Interface(const std::string& name,
                       const Date& birthday);             
                std::string name() const;
                std::string birthDate() const;          
            private:
                std::string _name;     // 实现细节
                Date        _birthday; // 实现细节
            };
        
        (2) `class 定义式` 不含 `实现细节` 
            
                `修改 class 的 interface 时, class 的 client 才` 需要 `重新编译`
                    
                    `pImpl` 手法 实现 真正的 `接口 与 实现 分离`

            #include <string> // 标准库组件 不该被前置声明
            #include <memory> // std::shared_ptr

            class InterfaceImpl; // 本 class 的 实现类 前置声明
            class Date;       // 本 class 的 interface 用到的 class 前置声明

            class Interface
            {
            public:
                Interface(const std::string& name,
                       const Date& birthday);
                      
                std::string name() const;
                std::string birthDate() const;          
            private:
                std::shared_ptr<InterfaceImpl> spImpl; // pImpl 
            };

    2   `最小化 编译依赖` 
            
            本质

                `声明 的 依赖性` 替换 `定义 的 依赖性`

                    头文件 应 尽可能 `自我满足`, 万一做不到,
                    
                        则 尽量依赖 other 文件内 声明式 (而非 定义式)
                        
            方法

                [1] `用 ref/ptr` 能完成 任务, 就不要用 object

                [2] `尽量用 class 声明式`, 除非 `真正需要 class 定义式`

                        `by value` 方式 `传参 或 返回值` 并 `不需要 class 定义式`

                            原因: `client 调用 function 前, 必先曝光( 实现出 ) class 定义式`

                                class Date;
                                Date today();
                                void clearAppointment(Date d);

                [3] `为 声明式 和 定义式 提供不同的 头文件`

                    // 1    date.h 定义式 头文件 
                    class Date
                    {
                    private:
                        int day;
                        int month;
                    };

                    // 2    dateDecl.h  声明式 头文件
                    class Date;

                    // 3    Date 的 client.cpp
                    #include "dateDecl.h"
                    Date today();
                    void clearAppointment(Date d);

    3   2 种 Handle class 
        
        3.1 `Handle class & HandleImpl class` 
            
            将其 所有函数 `转交` 给相应 `实现类 的 相应函数`

                两者 成员函数 接口完全相同

            //  3   Interface.cpp:  Interface 实现文件
            #include "Interface.h"      // #include Interface 类定义式
            #include "InterfaceImpl.h"  // #include InterfaceImpl 类定义式, 否则 无法调用其 成员函数

            Interface::Interface(const std::string& name,
                           const Data& birthday)
                : spImpl( new InterfaceImpl(name, birthday) ) {}

            std::string 
                Interface::name() const { return spImpl->name(); }
                
        3.2 `Interface class 即 abstract base class `
                
            (1) 无 成员变量 + ctor
                
                    只有 1个 virtual dtor + 一组 pure virtual 函数, 
                        
                        描述 derived class 的 接口

                Note
                    C++ Interface class / abstract base class 与 Java 中 Interface 区别:

                        C++ Interface class 中 
                        
                            `允许实现 成员变量 和 成员函数
                                                    |
                                                    |    
                                                    |/
                                                non-virtual function
                                                    |
                                                    |   如 
                                                    |/
                                                Factory Method: static mem func 

            (2) `client 必须 以 Interface class 的 ptr / ref 写程序`
                
                    原因: `无法` 对 `内含 pure vf` 的 Interface class `构造 新对象`

            (3) `client 必须` 能为 相应 `derived class 创建对象`

                    `Factory Method`, 扮演 `真正将被 实例化` 的 `derived class` 的 `ctor` 的角色
                                |
                                |    
                                |/
                        [1] 也叫 virtual ctor
                        [2] 通常是 static mem func 

                `支持 Interface class 接口 的` 

                    `Implement class 必须被定义 出来` + `真正的 ctor 必须被调用`
                        |
                        |       这一切都在 `Factory Method 实现文件` 中 发生
                        |
                        |/
                    Interface 的 Derived 类 

            (4) 源文件结构 
            
                ————————————————————————————————————————————————————————————————
                1   client.cpp:     创建 对象, 支持 Interface 接口
                ————————————————————————————————————————————————————————————————
                2   Interface.h:    Interface 声明文件: 
                                        virtual dtor
                                        Factory Method (non-virtual func) 
                                        pvf
                ————————————————————————————————————————————————————————————————
                3   Interface.cpp:  Interface 实现文件
                                        只含 dtor
                ————————————————————————————————————————————————————————————————
                4   Imp.h:          Imp 声明 (头) 文件
                                        放 实现细节( 数据  )
                ————————————————————————————————————————————————————————————————
                5   Imp.cpp:        Imp 实现 源文件
                ————————————————————————————————————————————————————————————————
                6   factory.cpp:    Factory function 实现文件 : 
                                        调 真正 ctor:  new Derived 
                                        
                                            return
                                                std::shared_ptr<Interface>( 
                                                    new Imp(name, birthday) );
                ————————————————————————————————————————————————————————————————            
                            
                //  1   client.cpp: 创建 对象, 支持 Interface 接口
                #include <iostream>
                #include <string>
                #include "date.h"
                #include "Interface.h"
                
                int main()
                {
                    // client 可能这样用
                    std::string name = "lilei";
                    Date date;

                    // 创建 对象, 支持 Interface 接口
                    std::shared_ptr<Interface> 
                        spP(Interface::create(name, date) );
                        
                    std::cout << spP->name() << "\n";
                }

                // 2    Interface.h:    Interface 声明文件: virtual dtor + Factory Method (non-virtual func) + pvf
                #ifndef Interface_H
                #define Interface_H

                #include <string>
                #include <memory>  //std::shared_ptr
                #include "date.h"
                
                class Interface
                {
                public:
                    // virtual Dtor 声明  Note:   virtual Dtor 实现 放 Interface.cpp
                    virtual ~Interface();           

                    // Factory Method 声明    Note:   Factory Method 实现 factory.cpp
                    static std::shared_ptr<Interface> 
                    create(const std::string& name, const Data& birthday);
                    
                    virtual std::string name() const = 0;
                    virtual std::string birthDate() const = 0;
                };
                
                #endif

                // 3    Interface.cpp:  Interface 实现文件, 只含 dtor
                #include "Interface.h"
                Interface::~Interface() {}

                // 4    Imp.h:  Imp 声明 (头) 文件: 放 实现细节( 数据  )
                #ifndef Imp
                #define Imp

                #include "Interface.h"
                #include <string>
                #include "date.h"

                class Imp: public Interface
                {
                public: 
                    Imp(const std::string& name, const Date& birthday)
                        : _name(name), _birthday(birthday) {}
                    virtual ~Imp() {}   
                    
                    std::string name() const;
                    std::string birthDate() const;              
                private:
                    std::string _name;     // 实现细节 
                    Date        _birthday; // 实现细节 
                };
                #endif

                // 5    Imp.cpp:Imp 实现 源文件
                #include "Imp.h"
                #include <string>
                
                std::string Imp::name() const{ return _name; }
                std::string Imp::birthDate() const { return " ";  }

                // 6    factory.cpp: Factory function 实现文件 :    调 真正 ctor
                #include <memory>  //std::shared_ptr
                #include "Interface.h"
                #include "Imp.h"
                #include "date.h"

                std::shared_ptr<Interface> 
                Interface::create(const std::string& name, const Data& birthday)
                {
                    return
                        std::shared_ptr<Interface>( 
                            new Imp(name, birthday) );
                }

                // 7    date.h
                #ifndef DATE_H
                #define DATE_H

                class Date
                {
                private:
                    int day;
                    int month;
                };
                #endif

        3.3 `Handle class 比较 Interface class`

            (1) 利 / 弊 / 替代为 concrete class / inline

                1) 利
                
                    `解耦 interface 与 implement`
                        
                        => 降低文件间 编译依赖`

                2) 弊
                    
                    ———————————————————————————————————————
                    [1] `运行期 速度损失`: 间接 访问/调用
                    
                    [2] `每个对象 多付 若干内存`
                    ———————————————————————————————————————

                3) 当 弊 > 利 
                    
                        => 用 concrete class 替换 两者
                        
                4) 一旦脱离 inline 均 无法有 太大作为
                
            (2) Handle class

                1)  `间接访问` 
                    
                        代价 
                            `间接层: 必须通过 pImpl ptr`

                2) `对象 内存` 增加 
                    
                        `pImpl ptr`

                3) pImpl ptr 必须 在 Handle class 内 初始化
                    
                        指向 `动态分配的 impl object`

                            动态内存`分配 / 释放` 的 `额外开销`
                            `bad_alloc` 的 可能性

            (3) Interface class

                1) 调 vf 
                    
                        代价: `间接跳跃`

                2) derived 对象 内存增加 
                        
                        vptr        

相关文章

网友评论

      本文标题:Effective C++ 5: Implement

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