美文网首页
17章 构造、清理、拷贝、移动: lifetime + 资源管理

17章 构造、清理、拷贝、移动: lifetime + 资源管理

作者: my_passion | 来源:发表于2022-06-23 23:52 被阅读0次
    (1) 对象 作 `异常`  也可能会被 copy/move
    
    (2) 类有 `引用/指针成员`, 可能需要 `copy / dtor + 非默认 copy`

17.1 ctor 和 dtor

1 ctor 
    
    (1) 必须建立 `类的不变式`: 成员函数 (从 类外) 被调用时, 必须保持的 something
    
    (2) 显式调用 
    
        placement new
    
        void C::push_back(const X& a)
        {
            new(p) X {a}; 
        }
2 dtor 

    (1) 阻止析构
        应用: 阻止 隐式销毁, 只允许 显式销毁
        [1] = delete
        [2] 放 private -> 更 灵活 

        class Nonlocal
        {
        public:
            void destory() { this->~Nonlocal(); } // 显式析构 
            
        private:
            ~Nonlocal();                          // 不能 隐式析构 
        };
        
        void user()
        {
            Nonlocal x;                 // error
            Nonlocal* p = new Nonlocal; // ok
            
            delete p;                   // error 
            p->destory();               // ok 
        }
        
    (2) 显式调用 
            
        void C::pop_back()
        {
            p->~X();
        }
        
    (3) 隐式调用 
    
        1] scope 变量 离开 scope 
            
        2] delete 动态分配的指针
    
    (4) virtual dtor: 

        若 `dtor 非虚` => delete 多态指针 pBase 调用的是 `Base dtor`       
        1) Derived 对象 own 的 `resource 泄露`   
        2) Derived 对象 本身仅 basePart 析构 

17.2 初始化 类对象

1 不使用 ctor 的 case: {} 列表初始化: 逐成员 初始化

vector<int> v1{10}; // 用 值 10 初始化 1个元素 
vector<int> v2(10); // Note: 调 Ctor, 将 10个元素 初始化为 0                                      

2 默认 ctor

(1) 内置类型 
            
    1) `非静态分配 (local non-static / 自由存储 + not 列表初始化 {} )` 变量
    
        `不初始化`
    
    2) `静态分配` 
        
        调 默认 ctor 进行初始化 
            
        ————————————————————————
        默认值 
            
            整型      0
            浮点      0.0
            指针类型    nullptr
        ————————————————————————
                    
        void f()
        {
            int* p1 = new int; // 未初始化 
            
            int* p2 = new int{}; // 自由存储 + 列表初始化 {} => 初始化 为 0
        }         

17.3 初始化 成员和基类

1 ctor 会 初始化其 (直接) 基类 + 成员

2 成员 初始化 vs 赋值

1) 必要性: `引用成员 / const 成员` 必须初始化 

2) 性能优势 

3 static 成员: 类内初始化 的 条件

    ————————————————————————————————————————————————
    const + 整型 / 枚举类型 
    ————————————————————————————————————————————————
    字面值类型 的 constexpr + 初始化器 是 常量表达式 
    ————————————————————————————————————————————————
    
    template<class T, int N>
    class A
    {
    public:
        static const int c = 1;
        
        static constexpr int max = N;
        // ...
    private:
        T a[max];
    };

17.4 copy & move

1 copy

(1) copy ctor 应确保 copy 了 基类和成员: 从 copy 目的 看, 基类 就是 成员

(2) copy virtual 基类 
        
    [1] 初始化义务 被 "冒泡" 到 最终派生类: 默认 copy ctor 能正确 copy 它 
            
    [2] `自定义 copy ctor: 重复 copy ( 可能 更高效)

(3) `浅 copy` 
        |
        |   令 `2 个对象` 进入 
        |/
    1) `共享状态` => `对象纠缠`
        |
        |   lifetime 问题 
        |
    2) shared_ptr: 好管理, 但并 `没有完全解决` 对象纠缠
            [1] `更新`: 谁负责 ? 如何 进行 ? 何时 进行 ?
            [2] `多线程`: 需要 `同步` 机制 吗 ? 如何确认 ?
        |
        |/
    3) `不可变 的 共享状态` -> FP
            |
            |/
    4) 写前 copy / COW (copy on write)
        
        思想:
            共享状态 被 修改前, 副本并不真的需要 独立性 
                
                => 可 `延迟 共享对象 的 copy`, `直到 修改副本前` 才 `真正进行 copy` -> `修改 副本`
        
        不是万能灵药, 但能 
            
            有效地 `结合` `真 copy 的 简单性` 与 `浅 copy 的 性能` 

(4) copy ctor & copy 赋值 的 `区别`
    
    ————————————————————————————————————————————
    copy ctor       copy 赋值 
    ————————————————————————————————————————————
    X(const X&)     X& operator=(const X&)

    原始内存        要处理 dstObj own resource
    ————————————————————————————————————————————
            
    `copy 赋值` 抛出异常 -> 基本保障 / 强保障
        
        基本保障 
            目标对象 = 旧值和新值 `混合状态`
            
        强保障
            先创建 `副本` -> 再 `移动交换 (未使用 赋值)` 内容 
            
        A& A::operator=(const A& rhs)
        {
            A tmp{rhs};
            swap(tmp, this); // move 交换 
            return *this;
        }

2 move

(1) 交换 2个 对象值

    // version1: 3 个对象 + 3 次 copy 
    template<class T>
    void swap(T& a, T& b)
    {
        const T tmp = a;
        a = b;
        b = tmp;
    }
        |
        |   大对象 => 代价高  
        |   `只想 交换 一对值`, 不想 copy
        |/
    // version2: 3 个对象 + 3 次 move 
    template<class T>
    void swap(T& a, T& b) // "几乎是" 完美的 
    {
        T tmp = std::move(a); // move ctor 
        a = std::move(b);
        b = std::move(tmp);
    }

(2) move 不能 抛出异常 -> copy 能抛出异常: 因为 copy 可能需要 获取资源

(3) `compiler 如何知道` 它可以 `优先使用 move 语义`
    
    1) 一侧 `右值` => `语言规则` 告知  
    2) std::move(x) 返回 `左值实参 x 的 右值引用` 
                                
(4) move 后 `源对象` 处于 `可 析构 & 赋值` 状态 (如 "空")  

(5) 标准库具有 move 语义的
    
    1) 类型: 容器 / unique_ptr / pair
    2) 操作: insert() / push_back(): 接受 `右值引用` 的 version 

17.5 (compiler)生成 默认操作

1 默认会生成, 只要 code 用到相应操作; coder 显式自定义则 禁止生成

    ——————————————————————————————————————————————————————————————  
        coder 显式自定义 |   禁止生成
    ——————————————————————————————————————————————————————————————      
    ctor (含 copy ctor)  |   默认 ctor
    ——————————————————————————————————————————————————————————————      
    copy / move 操作      |   相应版本
    ——————————————————————————————————————————————————————————————                  
    dtor                    |   `copy ctor 和 move ctor` 
    ——————————————————————————————————————————————————————————————      
    
    显式 dtor => 禁止默认 copy ctor -> 若需要 copy ctor: 显式(自定义)
    
    copy    
        需禁止  : 类 是 基类  & copy from Derived 被禁止      
        需自定义: 类 含 ptr -> 默认 copy 逐成员(指针) copy -> Dtor 中重复 delete -> undefined
    dtor 
        需自定义: 类含 ptr -> 需 释放 resource

    默认操作: 逐成员 copy/默认构造

2 资源不变式

    1) 单参数 Ctor: 给定 newed 的 resource ptr => `禁止了 默认 Ctor`
    2) 显式 dtor: delete 资源 
    3) 显式 copy ctor                             
    3) 解引用 * ->: 访问 resource
    
    template <class T>
    class Handle
    {
        T* p;
    public:
        Handle(T* p_): p{p_} {}
        // 显式 dtor => 禁止默认 copy ctor -> 若需要 copy ctor: 显式(自定义)
        ~Handle() { delete p; } 
        T& operator*() { return *p; }
        Handle(const T& rhs)
            : p { new T{*a.p} } {} // clone
    };

    void f()
    {
        Handle<int> h { new int{10} }
        Handle<int> h1;     // error: 无 默认 ctor 
        Handle<int> h2(h);  //  
    }

3 delete

(1) 禁止切片

    copy (from Derived) delete  
        Base(const Derived& rhs)

(2) 控制在哪里(stack 还是 heap)分配类对象
    
    dtor delete 
        obj 禁止在 stack 分配            
        obj 可 new, 但不能 delete: 
            因为 delete 会调 dtor, 而 detor 又被 deleted 了 -> 解决 -> dtor 放 private & p 调 public destory()

    operator new delete & 没有 global operator new
        obj 禁止在 heap 分配 
        obj 可在   stack 分配 
                
    class NotOnStack
    {
        ~NotOnStack() = delete;
    };
    
    class NotOnFreeStore
    {
        void* operator new(size_t) = delete;
    };
    
    void f()
    {
        NotOnStack v1;                          // error: 不能销毁  
        NotOnFreeStore* p = new NotOnFreeStore; // error 
        
        NotOnFreeStore v1;                      // ok 
        NotOnStack* p = new NotOnStack;         // ok 
    }
        |
        |/
    #include <iostream>

    class NotOnStack
    {
    public:
        void destory()
        {
            delete this; // 效果 <=> delete p; 
        }
    private:
        ~NotOnStack() 
        {
            std::cout << "dtor\n";
        };
    };

    int main()
    {
        NotOnStack* p = new NotOnStack; 
        p->destory();
    }
4 = default: 显式声明默认操作       
    compiler 不再了解 函数语义 => 不再 优化 
    
5 部分不变式
                        
    自定义 copy assignment + 魔数下标 -> 不安全: 没检查是否满足不变式就访问  -> 解决: 用 默认版本
             
    A& A::operator= (const A& rhs)
    {
        for(int i = 0; i < 9; ++I)
            vec[i] = rhs.vec[i]
    }           

6 copy move dtor 逻辑上如何联系?答: 保持类的不变式 
        
    ———————————————————————————————————————————————————————————————
    Ctor             :  建立 ( `获取资源`)    
    Copy 和 Move 操作:     保持
    Dtor             :  清理工作( 含 `释放资源` )
    ——————————————————————————————————————————————————————————————— 
共享状态/对象纠缠.jpg COW/写前拷贝.jpg

相关文章

网友评论

      本文标题:17章 构造、清理、拷贝、移动: lifetime + 资源管理

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