美文网首页
chapter13 异常处理: vector 的 实现

chapter13 异常处理: vector 的 实现

作者: my_passion | 来源:发表于2022-06-22 08:43 被阅读0次

    13.1 概述

                检查错误: callee
             /      |       无法处理 throw
            /       |       希望处理 catch
    1 分离            | 传递
            \       |
             \      |/
                处理错误: caller
                
    2 抛出异常
        
        栈展开
            异常 从 `抛出点` 开始, `向上` 传递到 处理程序
        
        空 异常说明 throw() 作用等价于 noexcept, 即 若抛出异常, 则 程序终止
            void f(int) throw();
    

    13.2 vector 的实现

        异常安全代码的 范例
    

    1 简单 vector

        (1) data
        
            1 个 分配器 alloc
            
            3个指针: elem / space / last 分别指向 
                
                分配空间   start
                
                可扩展空间 start == 元素序列 end
                
                分配空间   end 
    
        (2) ctor
            
            template<class T, class A = allocator<T> >
            ...
            vector(size_type n, const T& val = T(), const A& a = A() );
            
            [1] 分配内存 elem = alloc.allocate(n)
            
            [2] 另 2 个指针 赋值
            
            [3] 逐元素 `构造 新值的 copy`
                    
                for(T* p) 
                    alloc.construct(p, val);
            
            2处可能引发 异常
                
                [1] allocate() 抛出异常
                
                [2] T `copy ctor 无法 copy 新值`
                    construct(p, val) ...
        
        (3) 带 异常处理 的 ctor
                
                try{}
                catch(...)
                {
                    逐元素销毁 // for (Iter q = beg, ...) 
                               //    (&*q)->~T();
                    重新抛出   // throw;
                }
            }
    

    经验: try 书写 异常安全代码难度 远远超出人们的预期

    (1) vector 实现 没用 try 块 ( 除了 隐藏在 原始_copy() 中那个 之外 )

        |
        |   替换为 更简单有效的方法 
        |/
    

    (2) RAII + 小心 设计操作顺序 以 确保, 抛出异常 时,

    vector 不被改变 ( push_back() ) 或 至少处于 (我们认为的) 有效状态 ( copy assignent )

    2. 引入 辅助 struct (vector_base) 以 directly 处理 T 类型的 memory

        好处: 简化代码 + 降低 忘记释放内存的 可能性
            
        (1) ctor 
        
                alloc.allocate(size) + 3Ptrs 指向相应位置 
    
            dtor 
                alloc.deallocate(elem, last - elem)
            
        (2) copy delete
            
            那 vector copy 如何实现 ? 
                vector copy ctor/assignement
                
        (3) move
            
            [1] move ctor
                
                lhs 
                    ptrs/alloc 初始化(copy) 为 rhs's ptrs/alloc 
                    
                rhs 
                    ptrs 置 nullptr 
                
            [2] move assignment 
                
                lhs 已存在 => swap(*this, rhs);        
    

    直接 move vector_base, 即 move 任意已分配 memory 的 ownership : 优化效率/性能

    3 vector 实现

        template <class T, class A = alloctor<T> >
        class vector
        {
        private:
            // 只有1个 成员数据 
            vector_base<T, A> vb;
            
            // dtor 的 辅助(销毁元素的)函数
            destory_elements(); 
        
        public:
            // (1) 6 大函数 
                
            explicit vector(size_type n, const T& val = T(), const A& a = A() );
            
            ~vector() { destory_elements(); }
                
            // (2) 尺寸/容量函数 
            
            size_type size() const { return vb.space - vb.elem; }
            size_type capacity() const { return vb.last - vb.elem; };
                
            // (3) 改变尺寸 的 4 大函数
            
            // 10. 第 10 大函数
            void clear() { resize(0); }
            
        };
    
    (1) dtor 辅助函数
        
        逐成员销毁: 显式调 T 的 dtor
        
        note
            没有哪种方法 能 切实有效地 防止 dtor 抛出异常
        
    (2) ctor
    
        调 成员 vb 的 ctor: vb{a, n}
    
        原始_fill();
            
    (3) copy ctor
    
        调 成员 vb 的 ctor: vb{v.alloc, v.size() }
            
        uninitialized_copy() from rhsVecMemory 
    
    (4) 移动 ctor
        
        对 `成员 vb` 用 `std::move` 转移 所有权 
    
    (5) 移动 赋值
        
        销毁 所有元素
        对 `vector` 用 `swap` 转移 所有权 
    

    (6) copy assignement

        version1    `强保障`   缺点: 难维护
            midVecBase 
                uninitialized_copy from rhsVecMemory        : uninitialized_copy(rhs.begin(), rhs.end(), midVecBase.elem)
                move memory's ownership 到 dstVec's vecBase : swap(vb, midVecBase)
            scope 结束时, midVecBase 的 dtor 隐式 `释放 (被转移给的) 旧空间`
            |
            |
            |/
        version2    `强保障`   易维护, 与 version1 性能接近
            midVec         : 从 rhs copy ctor 
            move ownership : std::swap(*this, midVec)
            |               
            |                   效率高于        
            |   元素 (copy) 赋值 —— —— ——> 析构 (1 个元素) + 构造 (1个元素 )
            |/  
        version3    `基本保障`  复杂度高
            if dstVecCapacity < rhsVecSize 
                version2
            else // >=
                if dstVecSize >= rhsVecSize 
                    copy() rhsVecSize
                    多余 elem: 逐个销毁  
                        p->~T();
                        
                else // < 
                    copy() dstVecSize
                    额外 elem:
                        uninitialized_copy() from rhsVec
                                    
            基本保障: 抛出异常时, `正被 copy 的 elem 可能 既非旧值 也非新值` -> 视 vector 为 有效状态 
                 
        ————————————————————————————————————————————————————————————————————————————————
        copy 赋值 vs. 移动 赋值 
        ————————————————————————————————————————————————————————————————————————————————                            
        copy 赋值 |  const 引用形参   |  copy 后 右对象(左值 / 右值) 可能 不会被 立即销毁 
            
        移动 赋值 |  右值  引用形参   |  移动 后 右对象(右值)               会被 立即销毁 
        ————————————————————————————————————————————————————————————————————————————————            
    

    (7) reserve()

        if newCapacity 更小
            doNothing, return
        else 
            对 vector 扩展 capacity = 分配 newSpace + 元素 移入
        
        template<class T, class A>
        void vector<T, A>::reserve(size_type newCapacity);
    
        1) version1 
        
            [1] 建 newVector   
            [2] copy() 旧 elem 到 newSpace                                                                                                    
            [3] move ownership                                                                                                                      
            [4] scope 结束: newVector dtor 隐式 `销毁 旧值 + 释放 旧空间`                    
                                                                                                                                                                                    
        2) version2 : `基本保障: 要 避免 move 操作 抛出异常`                                                                          
                                                                                                        
            [1] 建 newVecBase 
            
            [2] 自定义 uninitialized_move() 算法, 在 各 newElemPos 用 oldElemValue `移动 构造` newElem
            
            [3] move memory's ownership: swap(newVecBase, vb)
    
            [4] scope 结束: newVecBase 的 dtor 隐式 仅 `释放 旧空间`
            
            template<typename In, typename Out>                                 
            Out uninitialized_move(In b, In e, Out oo)                           
            {                                                                   
                for(; b != e; ++b, ++oo)                                        
                {                                                               
                    new (static_cast<void*>(&*oo) ) T { std::move(*b) };  
                                                                                
                    b->~T();  // `销毁/析构 旧空间中 元素`
                }
            }
        
            reserve 一旦移动构造了元素, vector 迭代器 可能 失效 
    

    (8) resize()

        template<class T, class A>
        void vector<T, A>::resize(size_type newSize, const T& x);
            
        [1] 先调 reverse(newsize)
        
        [2]
            if newSize 更大
                uninitialized_fill(): 构造 额外 elem 
            else 
                destory():            销毁 多余 elem  
        
        [3] 更新 space&last 指针: 均指向 memory newsize 尾
        
        
            template<typename In>
            void destory(In b, In e)
            {
                for(; b != e; ++b)
                    b->~T();
            }
    

    (9) push_back()

        从 异常安全 角度看, push_back() `与 copy assignment 相似`
        
            若 copy 构造 抛出异常, 仍保证了 vector 内容没变 
                            
            原因: 
                copy 构造 后的 更新 spacePtrPos 没被执行
                vector 因 reserve() 可能 扩容而 换了 space, 但 data/content 却不变 
                    
        template<class T, class A>
        void vector<T, A>::push_back(const T& x);
        
        [1]
            if(capacity() == size() )   // 若 满载 
                reserve(sz ? 2*sz : 8); //  扩capacity 1 倍 或 分配为 8 (若为 初始分配)
        
        [2] 在 pushedPos 用指定值 `copy 构造` 新值  
            
            vb.alloc.construct(&vb.elem[size()], x) 
        
        [3] 更新 spacePtrPos
        
            ++vb.space;
    
    325.jpg 326.jpg 327.jpg 328.jpg 329.jpg 330.jpg 331.jpg 332.jpg 333.jpg 334.jpg

    相关文章

      网友评论

          本文标题:chapter13 异常处理: vector 的 实现

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