美文网首页
Effective C++ 2: ctor / dtor / a

Effective C++ 2: ctor / dtor / a

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

    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` 段 `去重`
    

    相关文章

      网友评论

          本文标题:Effective C++ 2: ctor / dtor / a

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