美文网首页程序员
一步步看懂STL源码(1)--准备工作

一步步看懂STL源码(1)--准备工作

作者: ElephantKing | 来源:发表于2019-10-21 11:18 被阅读0次

    大纲

    一直以来就想好好把STL源码通读一遍,限于水平,每每想扎进去,又被高深的模板,前后复杂的调用给震飞,不得窥其全貌,最近鼓起勇气,用超慢速度,边看边写,冒死整理出一个比较easy的进军路线,供大家参考。本篇内容如下:

    1. 对象的构造与析构
    2. 对象的拷贝和移动
    3. 一些辅助的栗子

    第一部分介绍编译器如何给你找对象的(构造),又是如何劝你们分手的(析构),需要理解构造和析构的两个步骤和四个关键函数。第二部分需要精读,细致讲述了如何将对象拷贝和腾挪,STL为了提高效率,极尽其能,手段横飞,是STL中十分精彩的一部分,需细品。第一篇比较简单,但是也很关键,是后续篇章的基础,开始来吧!

    对象的构造与析构

    class Foo {...}
    Foo *pf = new Foo;
    delete pf;
    

    上述代码究竟帮你做了什么,在内存中发生了什么变化?
    new是C++的一个操作符,分为了两个阶段:(1)调用::operator new配置内存,让对象有安身之所。(2)在安身之所的起始地址调用Foo::Foo()进行基建工作,形成Foo对象的结构。更为通俗说法是:先买一块地皮,然后开始盖房子。
    delete自然也需要分为两个阶段,不过是反着来的:(1)调用Foo::~Foo()将对象析构。(2)调用::operator delete归还内存。也用一句通俗的话来讲:先将用不上的房子拆除,再把地皮交还给国家。

    上面这四个阶段引出了下面这四个函数

    alloc::allocate()
    alloc::deallocate()
    ::construct()
    ::destroy()
    

    其中分配空间(allocate)和回收空间(deallocate)是由编译器做的,我们不要去深究,先把精力放在constructdestroy上,集中精神,下面是一剂猛药。

    template <class T1, class T2>
    inline void construct(T1 *p, const T2& value)
    {
        // 这句语法可能从没见过,无所谓
        // 意思就是在p所指地址的后续空间,构造出一个T2类型的对象,其值value
        new (p) T(value);
    }
    template <class T>
    inline void destroy(T *pointer)
    {
        pointer->~T();
    }
    // 下面要开始脑壳痛了,坚持一下,参照我指定的路标123走
    template <class ForwardIterator>
    inline void destroy(ForwardIterator first, ForwardIterator last)
    {
        // step 2
        // 这一步主要的困惑在于value_type(first)
        // 这个使用的是STL中的萃取机(文末链接[STL萃取机]),后面会讲述
        // value_type(first)是first所指对象的类型的指针,这么做仅为了把这个类型保留下去
        __destroy(first, last, value_type(first));
    }
    template <class ForwardIterator, class T>
    inline void __destroy(ForwardIterator first, ForwardIterator last, T*)
    {
        // step 1
        // td这个对象有且仅有两个选项:__false_type __true_type
        // 这个参数用来在编译期进行分支判断,在编译期决定调用哪个函数
        // 所以这个td是什么意思呢,它是用来在编译期告诉编译期类型T里面的has_trivial_destructor是__false_type还是__true_type
        // 用来决定如何析构,例子:如果一个类的has_trivial_destructor是__true_type
        // 那么在析构的时候可以完全不做任何事情,见步骤3 4
        typedef typename __type_traits<T>::has_trivial_destructor td;
        __destroy_aux(first, last, td());
    }
    // STL中后缀是_aux的函数才是真正干事的函数,其余的都是转发
    template <class ForwardIterator>
    inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type)
    {
        // step 3
        // 如果该对象的析构函数很特别(比如包含指针),那么需要自己做析构
        // 因为他析构对象本身内存的同时,还需要析构对象中的指针指向的内容
        for (; first < last; ++first)
            destroy(&*first);
    }
    // step 4
    // 如果该对象的析构函数没什么特别(比如成员都是普通的内置类型,那么只需要归还内存就可以了嘛,至于留下点痕迹在内存上,等下个来用这块内存的人来收拾吧)
    template <class ForwardIterator>
    inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __true_type)
    {
        // 啥也不用干,等着调用alloc::deallocate()归还空间就好了
    }
    inline void destroy(char*, char*){}
    inline void destroy(wchar_t*, wchar_t*){}
    

    通俗易懂时刻:好比你租了一间房子,房子到期的时候,该交房了,如果你还有水电没交(内部指针指向的空间没有归还),那自然要你来做扫尾工作,否则你就可以不用管了,留下你的家具啥的,等下个租房的人来收拾吧。

    对象的拷贝和移动

    这部分由三个函数带头搞事,引发效率优化后,带来的复杂局面,这三个函数分别是uninitialized_copy()uninitialized_filluninitialized_fill_n。下面分别介绍
    函数uninitialized_copy系列

    // uninitialized_copy(first, last, result),后面的函数省略模板头声明
    template <class InputIterator, class ForwardIterator>
    ForwardIterator
    uninitialized_copy(InputIterator first,InputIterator last,ForwardIterator result)
    {
        // step 1
        return __uninitialized_copy(first, last, result, value_type(first));
    }
    // step 2
    ForwardIterator
    __uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result, T*)
    {
        // 关于什么是POD类型见文末链接[通俗易懂POD]
        typedef typename __type_traits<T>::is_POD_type is_POD;
        return __uninitialized_copy_aux(first, last, result, is_POD());
    }
    // step 3.1
    ForwardIterator
    __uninitialized_copy_aux(InputIterator first, InputIterator last, ForwardIterator result, __true_type)
    {
        // 现在还不敢用memmove,因为可能不是连续的空间
        // 还要交付一层进行分发
        return copy(first, last, result);
    }
    // step 3.2
    ForwardIterator
    __uninitialized_copy_aux(InputIterator first, InputIterator last, ForwardIterator result, __false_type)
    {
        // 必须一个个亲自构造
        ForwardIterator cur = result;
        for (; first != last; ++first, ++cur)
            construct(&*cur, *first);
        return cur;
    }
    // step 1.1
    // 如果是char*版本那么直接无需多废话,直接上最快的memmove
    // wchar_t*版本也一样,代码省略
    char* uninitialized_copy(const char* first, const char* last, char* result)
    {
        memmove(result, first, last - first);
        return result + (last - first);
    }
    

    函数uninitialized_fill_n系列

    template <class ForwardIterator, class Size, class T>
    ForwardIterator
    uninitialized_fill_n(ForwardIterator first, Size n, const T& x)
    {
        // step 1
        return __uninitialized_fill_n(first, n, x, value_type(first));
    }
    ForwardIterator
    __uninitialized_fill_n(ForwardIterator first, Size n, const T&x, T1*)
    {
        // step 2
        typedef typename __type_traits<T1>::is_POD_type is_POD;
        return __uninitialized_fill_n_aux(first, n, x, is_POD());
    }
    // step 2.1
    ForwardIterator
    __uninitialized_fill_n_aux(ForwardIterator first, Size n, const T& x, __true_type)
    {
        /*
         template <class OutputIterator, class Size, class T>
         OutputIterator fill_n(OutputIterator first, Size n, const T& x)
        {
            for (; n > 0; --n, ++first)
                *first = value;
            return first;
        }
        */
        return fill_n(first, n, x);
    }
    // step 2.2
    ForwardIterator
    __uninitialized_fill_n_aux(ForwardIterator first, Size n, const T& x, __false_type)
    {
        ForwardIterator  cur = first;
        for (; n > 0; --n, ++cur)
            construct(&*cur, x);
        return cur;
    }
    

    函数uninitialized_fill系列

    template <class ForwardIterator, class T>
    inline void
    uninitialized_fill(ForwardIterator first, ForwardIterator last, const T& x)
    {
        // step 1
        // __uninitialized_fill(first, last, x, value_type(first));
    }
    // step 2
    inline void
    __uninitialized_fill(ForwardIterator first, ForwardIterator last, const T&x, T1*)
    {
        typedef typename __type_traits<T1>::is_POD_type is_POD;
        __uninitialized_fill_aux(first, last, x, is_POD());
    }
    // step 2.1
    inline void
    __uninitialized_fill_aux(ForwardIterator first,ForwardIterator last,const T& x, __true_type)
    {
        fill(first, last, x);
    }
    // step 2.2
    inline void
    __uninitialized_fill_aux(ForwardIterator first,ForwardIterator last,const T& x, __false_type)
    {
        ForwardIterator cur = first;
        for (; cur != last; ++cur)
            construct(&*cur, x);
    }
    

    最终调用的copy()

    // 唯一入口函数copy()
    // 如果只原生指针类型的copy,wchar_t*版本代码一致
    inline char*
    copy(const char* first, const char* last, char* result)
    {
        // 直接内存拷贝,速度超快
        memmove(result, first, last - first);
        return result + (last - first);
    }
    template <class InputIterator, class OutputIterator>
    inline OutputIterator
    copy(InputIterator first, InputIterator last, OutputIterator result)
    {
        // step 1
        // [first, last)迭代器区间,需要进一步考察
        // 这里是个对象,会调用__copy_dispatch的operator()
        return __copy_dispatch(InputIterator,OutIterator>()(first,last,result);
    }
    template <class InputIterator, class OutputIterator>
    struct __copy_dispatch
    {
        OutputIterator operator()(InputIterator first, InputIterator last, OutputIterator result)
        {
            // step 2.1
            // 转发到哪,视迭代器类型(文末链接[迭代器类型])而定
            return __copy(first,last,result,iterator_category(first));
        }
    };
    // 如果从step 1调用时,发现迭代器竟然是指针类型
    // 所以这里是step 2.2
    template <class T>
    struct __copy_dispatch<T*, T*>
    {
        T* operator()(T* first, T* last, T* result)
        {
            typedef typename __type_traits<T>::has_trivial_assignment_operator t;
            return __copy_t(first, last, result, t());
        }
    };
    // step 2.2.1
    template <class T>
    inline T* __copy_t(const T* first, const T* last, T* result, __true_type)
    {
        memmove(result, first, sizeof(T) * (last - first));
        return result + (last - first);
    }
    // step 2.2.2
    template <class T>
    inline T* __copy_t(const T* first, const T* last, T* result, __false_type)
    {
        return __copy_d(first,last,result,(ptrdiff_t*)0);
    }
    // step 2.1.1
    // 普通输入迭代器如list<int>::iterator
    // 只能用迭代器++进行遍历操作,一个个移动,慢速
    inline OutputIterator __copy(InputIterator first, InputIterator last, OutputIterator result, input_iterator_tag)
    {
        for (; first != last; ++result, ++first)
            *result = *first;
         return result;
    }
    // step 2.1.2
    // 随机访问迭代器如vector<int>::iterator
    // 用数字++进行遍历操作,也是一个个移动,速度较快
    inline OutputIterator __copy(RandomAccessIterator first, RandomAccessIterator last, OutputIterator result, random_access_iterator_tag)
    {
        return __copy_d(first, last, result, distance_type(first));
    }
    inline OutputIterator
    __copy_d(RandomAccessIterator first, RandomAccessIterator last, OutputIterator result, Distance*)
    {
        for (Distance n = last - first; n > 0; --n, ++result, ++first)
            *result = *first;
        return result;
    }
    // 另外两个简单的函数
    template <class ForwardIterator, class T>
    void fill(ForwardIterator first, ForwardIterator last, const T& value)
    {
        for (; first != last; ++first)
            *first = value;
    }
    template <class OutputIterator, class Size, class T>
    OutputIterator fill_n(OutputIterator first, Size n, const T& value)
    {
        for(; n > 0; --n, ++first)
            *first = value;
        return first;
    }
    

    通俗易懂时刻:在内存的拷贝时,我们想尽可能的快,这是我们最主要的目标。由于整数加减法运算要比迭代器移动要快,所以对于随机访问迭代器(包括原生指针),我们采用整数加减法来控制遍历,这是其一。对于对象,如果该对象有简单的赋值操作符运算(has_trivial_assignment_operator=__true_type),我们直接使用memmove这种超快速的拷贝,否则这种对象的拷贝只能交由对象本身的赋值操作符(operator=)来进行了。在后面的栗子中我们能看的很清楚。

    一些辅助的栗子

    如果你能看到这里,那算是我的成功了,说明前面的内容没有把你震飞,下面将进入比较舒适的区域,放松放松发烫的脑壳吧。

    class C
    {
    public:
        C() : _data(3){}
        int _data;
    };
    int main()
    {
        // example 1
        const char ccs[5] = {'a','b','c','d','e'};
        char ccd[5];
        copy(ccs, ccs+5,ccd);
        // 调用copy(const char*, const char*, char*);
    
        // example 2
        int ia[5] = {1,2,3,4,5};
        copy(ia, ia+5, ia);
        // 调用step 1:copy()
        //        step 2.2:__copy_dispatch(T*, T*)
        //            step 2.2.1:__copy_t(T*, T*, T*, __true_type)
    
        // example 3
        list<int> ilists(ia, ia+5);
        list<int> ilistd(5);
        copy(ilists.begin(), ilists.end(), ilistd.begin();
        // step 1:copy()
        // step 2.1 __copy_dispatch(InputIterator, InputIterator, OutputIterator)
        // step 2.1.1 __copy(InputIterator,InputIterator,OutputIterator,input_iterator_tag)
    
        // example 4
        vector<int> ivecs(ia, ia+5);
        vector<int> ivesd(5);
        copy(ivecs.begin(), ivecs.end(), ivecd.begin());
        // step 1:copy()
        // step 2.2:__copy_dispatch(T*,T*)
        // step 2.2.1:__copy_t(T*,T*,T*,__true_type)
        // 并不是以下调用方式
        // copy()->__copy_dispatch(InputIterator)->__copy(random_access_iterator_tag)->__copy_d(Distance)
    
        // example 5
        C c[5];
        vector<C> cvs(c, c+5);
        vector<C> cvd(5);
        copy(cvs.begin(), cvs.end(), cvd.begin());
        // 用户自定义类型,默认拥有non-trivial ctor/dtor/operator=
        // 调用过程如下
        // step 1:copy()
        // step 2.2:__copy_dispatch(T*,T*)
        // step 2.2.2:__copy_t(T*,T*,T*,__false_type)
        // __copy_d(Distance)
    
        // example 6
        deque<C> cds(c, c+5);
        deque<C> cdd(5);
        copy(cds.begin(),cds.end(),cdd.begin());
        // step 1:copy()
        // step 2.1 __copy_dispatch(InputIterator, InputIterator, OutputIterator)
        // step 2.1.1 __copy(RandomAccessIterator,RandomAccessIterator,OutputIterator,random_access_iterator_tag)
        // __copy_d(Distance)
    
        // example 7
        vector<string> strvs(5);
        vector<string> strvd(5);
        copy(strvs.begin(), strvs.end(), strvd.begin());
        // step 1:copy()
        // step 2.2:__copy_dispatch(T*,T*)
        // step 2.2.2:__copy_t(T*,T*,T*,__false_type)
        // __copy_d(Distance)
    }
    

    以上就是copy()的全部故事,一个无所不用其极强化效率的故事。

    参考链接

    STL萃取机
    通俗易懂POD
    迭代器类型

    相关文章

      网友评论

        本文标题:一步步看懂STL源码(1)--准备工作

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