Effective C++ 2: ctor / dtor / a

2021-07-20  本文已影响0人  my_passion

1 know C++ 默认 生成 并 call 哪些 functions

1 empty class 何时不 empty ?

            答: C++ compiler 隐含自动生成 4 大函数

                `只有当 这些函数 被调用` 时, `才会被 compiler 创建出来`
                    => 此时 empty class 不再 empty

2 compiler 拒绝 implicitly 自动生成 赋值运算符 的 case

            `[1] class 内含 reference 成员 / const 成员` 
                
                原因

                    C++ `不允许` 
                        `reference 改 指 不同对象 / const 成员 被修改`

            `[2] base class 的 assignmenat 是 private`

2 若 不想 use compiler 自动生成 的 func, 就 明确拒绝

1 compiler implicitly 生成 的 4 大 函数 ( 均 public ) , 如何 阻止编译通过?

    本节只 考虑 `copy ctor / assignment`, 对 default ctor 和 dtor 分析同理

    思路: 阻止其 implicitly 生成

    3 方法: 

    `(1) explicitly 声明 + 定义为 private 版本`
            |                       |
            |                       |
            |/                      |/
        阻止 compiler         [1] client 调用 => compile 不通过
        implicitly 生成       [2] member func 和 friend func 调用 => success
                                    => not safe

    `(2) 只 声明 ( compile 通过 ) + 不 定义 ( link 不通过 ) -> 调用 -> linkage error`
                                            
    `(3) 链接期 错误 转移至 编译期`
            
            private 声明` 提取 到 设计的 `base class
            
                `Uncopyable 作 base class` + ( public 甚至 `private` ) `继承`

                                            |
                                            |
                                            |/      
                        任何 client(mem func/friend func), 尝试 copy / assignment 
      
                            -> `compiler 尝试 生成 copy ctor / assignment`

                                -> `尝试 调相应 `base class` 中 对应 func
        
                                    -> `调用 被 compiler 拒绝`

3 多态 base class 要声明 virtual dtor

1 base class 设计是为了 多态 时, 才需要 virtual dtor

`某些 base class 的 设计并不是为了 多态, 则 不需要 virtual dtor`
        
    如 `Uncopyable`

2 derived object 经由 base class 指针 被 delete + base class 的 dtor 非虚

=> undefined behavior
            通常是

                `只 调 base class 的 dtor => 仅 derived obj 的 base part 被 销毁`
                    
                    [1] `局部销毁 的 object`
                    
                    [2] derived 类管理的 `资源泄漏`
                    
                        |
                        |   解决
                        |/
                base class 要用 virtual dtor

        `=> 对任何 不带 virtual dtor 的 class ( std::string + STL 容器 vector / list 等 ), 不要企图去 继承 它`

3 vf 目的

允许 `derived class 实现` 得以 `客制化`
        ————————————————————————————————————————————————————————————
        `[1] class   含 vf`, 几乎确定    `应该有 1个 virtual dtor`
        ————————————————————————————————————————————————————————————
        `[2] class 不含 vf`, 通常表示 它 `不企图被用作 base class` 
        ————————————————————————————————————————————————————————————
                |
                |
                |/
            `令 dtor 为 virtual 是个馊主意`
                
                对象
                    内存 增加, 
                        => 本不用 virtual 还可以 传给 C 函数
                               用 virtual 却不能 传给 C 函数                   

4 别让 异常 逃离 dtor

[1] dtor 绝不要 抛出异常

    `dtor 所调 函数 f()` 可能 `抛出异常` 时, 
        
        `dtor 应 捕捉` 异常, 并 `吞下 ( 不传播 ) 异常` 或 `结束程序`

[2] client 若想 处理异常

    则 class 应该 `provide a public interface 去 `调 函数 f()`
        
    // 例: DBConn 类 管理 DBConnection      

5 ctor/dtor 期间 Never 调 vf

1 derived class object 的 base part 构造/析构 期间, object 的 type 是 base class 而非 derived class

        [1] vf 被 compiler 解析至 base class 
        
            => ctor/dtor 中 调 vf, 达不到 预想的 (多态) 结果
                      |
                      |/
                    vf 调 dynamic_cast / typeid 想 达到多态效果
        
            => ctor / dtor 调用链 中 不要含 vf`
        
        [2] RTTI
            
            dynamic_cast / typeid 视 object 为 base class type

    `2 base class func 想 处理 Derived class 的 static mem data, 怎么办?`
    
                                                    |
                                                    |/
                                                LogInfo: 不含 未初始化 non-static mem data
                                    
        `无法 用 vf 从 base class 向下 调用`
            
                        调 Base ctor( 引用传递 )`
                      /     |\
        `Derived ctor`      | 作 实参 
                      \     |
                        调 自身 private static mem func` 返回 LogInfo  

6 令 operator= 返回 reference to *this

1 为实现 连续赋值

```
    operator= 必须 return a ref to lhs 实参: operator= 为 class mem 时, lhs 实参 为 *this`
```

7 在 operator= 中 处理 自我赋值

    1 Note `别名` 引起的 `隐晦/潜在 的 自我赋值`
             |
             |
             |/
            [1] 指针 / 引用
            [2] 指针 / 引用 + 类层次       

            [1] *px = *py;                           // px 和 py 可能指向 同一 obj 时
            
            [2] void f(const Base& rb, Derived* pd); // rb 和 *pd 可能指向 同一对象
            
    2. `保证 self-assignment-safe` 的 3 种方法 

        `(1) [1] 等价 test 
             [2] delete (原) internal ptr 
             [3] (新) internal ptr 指向 copy of rhs's pointed content`
        
        `(2) [1] record (原) internal ptr
             [2] (新) internal ptr 指向 copy of rhs's pointed content 
             [3] delete (原) internal ptr`

        `(3) copy and swap`: 类 提供 swap 成员函数 

8 Copy 对象时 勿忘其 每个 part

    1 copying 函数
        ——————————————————————————————————————————————————————————————————————————————————————————————————————————————
                        |       若没 `explicitly 传参` 调 `base class 相应函数
        copying 函数  |                                   
                        |                       默认
        ——————————————————————————————————————————————————————————————————————————————————————————————————————————————          
        copy ctor       |       implicitly 调 `Base class 某个 default ctor` 去 初始化 Derived object 的 base part`
        ——————————————————————————————————————————————————————————————————————————————————————————————————————————————
        copy assignment |       不修改 base part mem data
        ——————————————————————————————————————————————————————————————————————————————————————————————————————————————
        问题          |       默认操作可能 `潜藏危机`
        ——————————————————————————————————————————————————————————————————————————————————————————————————————————————
        解决          |       显式 调 Base part 的 适当函数 
                        |       
                        |       // 继承: copying 函数 调 base class 适当 函数
                        |       D::D(const D& rhs)
                        |           : B(rhs), 
                        |             derivedPart(rhs.derivedPart) {}
                        |
                        |       D& D::operator=(const D& rhs)
                        |       {
                        |           B::operator=(rhs); 
                        |           derivedPart = rhs.derivedPart;
                        |           return *this;
                        |       }
        ——————————————————————————————————————————————————————————————————————————————————————————————————————————————  
        
    2 copy ctor / assignment 相同 code` 段 `去重`

            不应该 让两者中一个调另一个, 
                而应该 提供1个  private common func 给两者调  
=== 详细

1 know C++ 默认 生成 并 call 哪些 functions

    `1  empty class 何时不 empty ?`

        若 class 没 explicitly 声明, compiler 会为 class implicitly 自动生成 
        1个 copy ctor 
        1个 copy assignment 
        1个 dtor 

        若 class 没 explicitly 声明 任何 ctor, compiler 会 implicitly 自动生成 
            1个 default ctor

        class Empty {};

        <=>

        class Empty
        {
        public:
            Empty() { ... }
            Empty(const Empty& rhs) { ... }
            ~Empty() {...} // 若 Empty 的 base class dtor 为 virtual, 
                           // 则 此处 Empty dtor 也 virtual
            Empty& operator=(const Empty& rhs){ ... }

        };

    2   4 大函数 做什么

    (1) default ctor 和 dtor

        调 base class 和 non-static 成员函数 的 default ctor 和 dtor

    (2) copy ctor 和 copy assignment

        将 源对象 的 每个 non-static 成员变量 copy 到 目标对象

        `1) copy assignment 与 copy ctor 行为` 通常 `如出一辙`

            template <typename T>
            class A
            {
            public:
                A(const char* name_, const T& value_);
                A(const std::string& name_, const T& value_);
            private:
                std::string name;
                T value;
            };

            // "xian" : const char*
            A<int> a1("xian", 10); // 调 A(const char* name_, const T& value_);

            A<int> a2(a1);         // 调 copy ctor

        `2) compiler 拒绝 implicitly` 自动生成 `赋值运算符` 的 case

            `1> class 内含 reference 成员 / const 成员` 
            
                原因

                    C++ `不允许` 
                        `reference 改 指 不同对象 / const 成员 被修改`

                template <typename T>
                class A
                {
                public:
                    A(const std::string& name_, const T& value_);
                private:
                    std::string& name; // reference
                    const T value;     // const
                };

                std::string addr1("xian");
                std::string addr2("xianyang");

                A<int> a1(addr1, 10);
                A<int> a2(addr2, 8);

                a1 = a2;

            `2> base class 的 assignmenat 是 private`

2 若 不想 use compiler 自动生成 的 func, 就 明确拒绝

    `1 compiler implicitly 生成 的 4 大 函数 ( 均 public ) , 如何 阻止编译通过?`           

        class Uncopyable
        {
        protected:
            Uncopyable() {}
            ~Uncopyable() {} // 非必须为 virtual
        private:
            // 只声明 + 不定义 
            Uncopyable(const Uncopyable&); 
            Uncopyable& operator=(const Uncopyable&);
        };

        class HomeForSale: private Uncopyable // 非必须为 public 继承
        {
            ... // 不再声明 copy ctor / assignment
        };
        
        1) Uncopyable 实现 的 `微妙之处:`

            [1] 不一定非得 public 继承

            [2] dtor 不一定非得 virtual
        
        2) Uncopyable 不含 data
            `empty base class optimization`

            `Uncopyable 作 base class` 被继承 的 技术 可能导致 
                
                `多重继承`
                    |
                    |   有时会
                    |/
                `阻止` empty base class optimization

        通常 可忽略这些 微妙点, 只像 上面那样 使用 Uncopyable 
        
    2 非 compiler 自动生成 函数, 如何阻止 编译通过?

        class 内 不 explicitly 声明
        
    3 不可 copy / assignment 的 东西: `独一无二`

        如 要卖的房子

        class HomeForSale { ... }

        HomeForSale h1;
        HomeForSale h2;
        HomeForSale h3(h1); // 企图 调 copy ctor : 不该 编译通过
        h1 = h2;            // 企图 调 assignment: 不该 编译通过

3 多态 base class 要声明 virtual dtor

`1 base class 设计是为了 多态 时, 才需要 virtual dtor`

`2 derived object` 经由 `base class 指针 被 delete` + `base class 的 dtor 非虚` 

    => undefined behavior

`3 vf 目的` 
    
    允许 `derived class 实现` 得以 `客制化`

`4 若希望拥有 abstract class, 但 手上没任何 pure vf` 
        |
        |   解决
        |/
    `pure virtual dtor`

4 别让 异常 逃离 dtor

    `1 STL 容器 所放 class 的 dtor 可能 抛出异常` 时, 程序可能 过早结束 或 `undefined behavior`

        class Widget
        {
        public:
            ~Widget() {} // 假设这里可能 抛出异常
        };

        void dosth()
        {
            std::vector<Widget> v;
        }   // v 自动销毁

    `2 若 dtor 中 动作 f() 可能 抛出异常, 该怎么办?`

        `答: 将 调 f()` 的 `责任` 从 dtor `转移` 到 client

                `1) undefined behavior 没转移, 而是 限制了`
     
                    `dtor 抛出 异常`, 会带来 `undefined behavior`
                        而 `client` 可 `处理异常`
                
                2) 2处 调 f() 并不违反 `让接口容易被正确使用`
                
                    1> 供 client 的 f() 
                        
                        让 client 间接调 f(), 
                        并 `对异常` 做出相应 `处理`

                    2> dtor 中 f(), 
                        
                        `不处理异常` 
                            只是 在 发生异常时, 结束程序 或 吞下异常
                            
        class DBConnection // [1] 负责 数据库连接 的 class
        {
        public:
            static DBConnection create(); // return DBConnection object
            
            void close(); 
        };

        class DBConn      // [2] 用于 管理 DBConnection object 的 class
        {
        public:
            ~DBConn()
            {
                db.close();
            }
        private:
            DBConnection db;
        };

        // client
        {
            DBConn dbc(DBConnection::create() );
        }   // DBConn object 销毁 -> 调 DBConn dtor -> 调 DBConnection 的 close()

        `DBConn dtor 调 DBConnection close() 时,发生异常, 则 DBConn dtor 会 传播该异常 ( 允许 异常 离开 DBConn dtor ) -> 抛出 难以驾驭的 麻烦: undefined behavior`

        (1) 解决1: `抛出异常时, 结束程序`

            `dtor 中 抛出异常` -> `std::abort() 强迫结束程序` 是 `合理选择`

            ~DBConn()
            {
                try
                {
                    db.close();
                }
                catch(...)
                {
                    log close 调用失败;
                    std::abort();
                }
            }
            
        (2) 解决2: `吞下 异常`

            ~DBConn()
            {
                try
                {
                    db.close();
                }
                catch(...)
                {
                    log close 调用失败;
                }
            }

            前2个 solution 都 `没什么吸引力`, 因为 都 `无法处理 抛出的异常`

            较佳策略:

        (3) 解决3

            `将 调 close()` 的 `责任` 从 DBConn dtor `转移` 到 DBConn client

                `1) undefined behavior 没转移, 而是 限制了`
     
                    `dtor 抛出 异常`, 会带来 `undefined behavior`
                        而 `client` 可 `处理异常`
                
                2) 2处 db.close() 并不违反 `让接口容易被正确使用`
                
                    1> 供 DBConn client 的 close() 
                        
                        让 DBConn client 间接调 db.close(), 
                        并 `对异常` 做出相应 `处理`

                    2> dtor 中 db.close(), 
                        
                        `不处理异常` 
                            只是 在 发生异常时, 结束程序 或 吞下异常

            class DBConnection // 负责 数据库连接 的 class
            {
            public:
                static DBConnection create(); // return DBConnection object
                
                void close(); 
            };

            class DBConn // 管理 DBConnection object 
            {
            public:
                void close() // 供 DBConn 的 client 去 间接调 DBConnection close()
                {
                    db.close();
                    closed = true;
                }
                
                ~DBConn()
                {
                    if( !closed )
                    {
                        try
                        {
                            db.close();
                        }
                        catch(...)
                        {
                            log close 调用失败;
                            ... // 结束程序 或 吞下异常
                        }
                    }
                }
            private:
                DBConnection db;
                bool closed;
            };

5 ctor/dtor 期间 Never 调 vf

    `1 derived class object 的 base part 构造/析构 期间, object 的 type 是 base class 而非 derived class`
        
        #include <iostream>
        #include <typeinfo>

        class B // 交易
        {
        public:
            B() { vf(); }
            virtual void vf() const = 0;
        };

        void B::vf() const
        {
            std::cout << typeid(*this).name() << "\n"; // class B
        }

        class D : public B
        {
        public:
            virtual void vf() const { }
        };

        int main()
        {
            D d;
        }

        // ctor / dtor 调用链 中 不要含 vf`
        class B
        {
        public:
            B() { init(); } // 调 non-virtual func
            virtual void vf() const = 0;
        private:
            void init()
            {
                vf();     // 调 vf
            }
        };

    `2 base class func 想 处理 Derived class 的 static mem data, 怎么办?`
                                                    |
                                                    |/
                                                LogInfo: 不含 未初始化 non-static mem data
                                    
        `无法 用 vf 从 base class 向下 调用`
            
                        引用方式 调 Base ctor`
                      /     |\
        `Derived ctor`      | 作 实参 
                      \     |
                        调 自身 private static mem func` 返回 LogInfo  

    class B
    {
    public:
        explicit B(const std::string& logInfo)
        {
            vf(logInfo);
        }
        
        void vf(const std::string& logInfo) const; // non-virtual
    };

    class D : public B
    {
    public:
        D()
            : B( createLog( params ) ) // logInfo 传给 base class ctor
        { ... }
        
    private:
        static std::string createLog( params );  
    };                                          

6 令 operator= 返回 reference to *this

    1 为实现 

        `连续赋值, operator= 必须 return a ref to lhs 实参: operator= 为 class mem 时, lhs 实参 为 *this`

    ```
        A& 
        operator=(const A& rhs)
        {
            ...
            return *this; // return reference to current object (*this)
        }

    2 该协议 也适用于 其他 `赋值 运算` / `参数 非 const A&`

    A& operator+=(const A& rhs) // 适用于: += 等
    {
        ...
        return *this; 
    }

    A& 
    operator=(int rhs)       // 适用于: 参数 非 const A&
    {
        ...
        return *this; 
    }

    3 这只是个 协议, 并无强制性

    只是 标准库中
    string / vector / complex / shared_ptr 都遵守

7 在 operator= 中 处理 自我赋值

    1 `自我赋值:` 发生在 `对象被赋值给自己` 时

        愚蠢 但 合法

    (1) `显而易见 的 自我赋值`

        class A { /// };
        A a;
        ...
        a = a; // 赋值给自己, 没人会写出这样的 code

    `(2) 别名 ( aliasing )` 引起的 `隐晦的 自我赋值`

        `别名: 用 多个方法 指涉 同一对象`


        a[i] = a[j]; // 潜在的自我赋值: i = j 时

        *px = *py; // 潜在的自我赋值: px 和 py 指向 同一 obj 时


        `1) 操作 ptr 或 ref:` 它们 `指向 多个 同类型 对象`
    
                要考虑 对象是否为 同一个


        `2) 两个 对象 来自 同一继承体系, 类型不同` 也可能 造成 `别名`

            pointer / reference to base class 可 指向 derived object

            class Base { ... };

            class Derived: public Base { ... };

            // rb 和 *pd 可能是 同一对象
            void dosth(const Base& rb,
                       Derived* pd);


    `2 管理资源 时, 要保证 self-assignment-safe:` avoid `在停止使用 资源前, 意外释放它`

        `自行 管理资源 / 用对象 管理资源` 时, 都要保证 `self-assignment-safe`
            
            ——————————————————————————————————————————————————————————————————
            1) 自行  管理资源 |   `client code 要保证` ...
            ——————————————————————————————————————————————————————————————————
            2) 用对象管理资源  |   对象 相应 `class 的 operator= 要保证` ...
            ——————————————————————————————————————————————————————————————————
            
            
            例: 用 class 保存 pointer to a `动态/heap 分配` 的 bitmap

                class Bitmap { ... };

                class A
                {
                private:
                    Bitmap* pb; // ptr: 指向 heap 分配的 bitmap
                };

            `1) operator= delete lhs.ptr -> new use rhs's ptr 所指 obj 的 copy -> return *this`

                若 this == &rhs 
                    -> rhs's internal ptr/memory 先被 delete 

                    `return 的 *this 的 ptr指向  已 deleted 对象`


                A&
                A::operator=(const A& rhs)
                {
                    delete pb;

                    pb = new Bitmap(*rhs.pb);
                    
                    return *this;
                }

            |
            |   `保证 self-assignment-safe` 的 3 种 方法
            |/

        `(1) [1] 等价 test -> [2] delete (原) internal ptr -> [3] (新) internal ptr 指向 copy of rhs's pointed content`

            A&
            A::operator=(const A& rhs)
            {
                if(this == &rhs) // identity test
                    return *this;
                
                delete pb;
                
                pb = new Bitmap(*rhs.pb);
                
                return *this;
            }

        `(2) [1] record (原) internal ptr -> [2] (新) internal ptr 指向 copy of rhs's pointed content -> [3] delete (原) internal ptr`

                => 保证 `exception-safe` => 往往 `自动获得 self-assignment-safe`

                `1) copy rhs's ptr 所指 content 前, 别 删除 rhs's ptr`

                `2) new Bitmap 抛出异常 => delete pOrig 不执行 => lhs 的 原 internal ptr (pb) 及 lhs 保持原状`

                3) 可不用 identity test: 
                    加上 identity test 效率 可能 `更高 ( self- 发生频率高 ) / 低 ( test 要成本 )`

                    A&
                    A::operator=(const A& rhs)
                    {
                        Bitmap* pOrig = pb;       // [1] record `原 internal ptr`
                        
                        pb = new Bitmap(*rhs.pb); // [2] `新 internal ptr` 指向 rhs's *pb 的 copy
                        
                        delete pOrig;             // [3] delete 原 internal ptr
                        
                        return *this;
                    }

        `(3) copy and swap`

                class A
                {
                    void swap(A& rhs); // 交换 *this 和 rhs's data
                };

                A& A::operator=(const A& rhs) // pass by reference: rhs 是 = 右侧对象
                {
                    A tmp(rhs); // make a copy of rhs's data
                    
                    swap(tmp);  // 交换 *this 和 copy 的 data
                    
                    return *this;
                }

8 Copy 对象时 勿忘其 每个 part

    // B
    class B
    {
    public:
        B(const B& rhs);
        B& operator=(const B& rhs);
    private:
        std::string name;
    };

    B::B(const B& rhs)
        : name(rhs.name){ }

    B& B::operator=(const B& rhs)
    {
        name = rhs.name;
        return *this;
    }

    class B
    {
    public:
        ...
    private:
        std::string name;
        Date da;            // class Date { ... };
    };
    // copying 函数 要修改: 这里不再赘述

    // 继承: copying 函数 应 调 Base 适当 函数
    class D: public B
    {
    public:
        D(const D& rhs);
        D& operator=(const D& rhs); 
    private:
        int derivedPart;
    };

    D::D(const D& rhs)
        : derivedPart(rhs.derivedPart){}

    D& D::operator=(const D& rhs)
    {
        derivedPart = rhs.derivedPart;
        return *this;
    }

    // 继承: copying 函数 调 base class 适当 函数
    D::D(const D& rhs)
        : B(rhs), 
          derivedPart(rhs.derivedPart) {}

    D& D::operator=(const D& rhs)
    {
        B::operator=(rhs); 
        derivedPart = rhs.derivedPart;
        return *this;
    }

    => Derived 的 copying 函数 应调 Base 适当 函数`

        copy 每个 part
            1) copy local mem data
            
            2) 调 base class 内 适当 copying 函数 

    `2 copy ctor / assignment 相同 code` 段 `去重`
上一篇下一篇

猜你喜欢

热点阅读