STL(九)——算法(Algorithm)(二):copy

STL包含众多算法,不过很多算法实现其实很简单,看源码即可理解。但也有部分简单的函数,实现版本做了很多工作来加快效率,copy函数就是一个例子。我们挑sgi stl实现的copy函数作为例子谈一谈。

è¿éåå¾çæè¿°

概述

copy函数是调用比较频繁的函数,由于copy进行的是复制操作,而复制操作不外乎运用赋值运算符或复制构造函数实现,但有但元素拥有trivial assignment operator,因此,如果能够直接使用内存直接复制行为(如memmove,memcpy)能节省很多时间。为此SGI STL但copy算法用了多种办法,包括函数重载(function overloading)、型别特性(type traits)、偏特化(partial specialization)等编程技巧加强效率。上表就是copy函数一览。

实现细节

copy算法可以将输入区间[first,last]内的元素复制到输出区间[result, result+(last - first)。也就是说,它会执行赋值操作*result = *first , *(result+1)=*(first+1),...。返回一个迭代器result+(last-first)。copy对其template的参数非常宽松,其输入区间只需要由InputIterator构成即可,输出区间只需由ourputIterator构成即可。

copy函数涉及一个区间是否重叠的问题。如果输入区间的头部包含输出区间的尾部,那么正向赋值不会出现问题。但如果输入区间的尾部包含输出区间的头部,那么正向赋值会导致错误的赋值,这时候需要反过来赋值。

下面是部分源码

template<class InputIterator, class OutputIterator>
inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result)
{
    return __copy_dispatch<InputIterator, OutputIterator>()(first, last, result);
}


//特化版本 const char*
inline char* copy(const char* first, const char* last, char*result)
{
    memove(result, first, last-first);
    return result + (last - first);
}

//完全泛化版本
template<class InputIterator, class OutputIterator>
struct __copy_dispatch
{
    OutputIterator operator()(InputIterator first, InputIterator last, 
                                OutputIterator result)
{
    return __copy(first, last, iterator_category(first));
}
};

//偏特化版本1,两个参数都是T*
template<class T>
struct __copy_dispatch<T*,T*>
{
    T* operator()(T* first, T* last, T* result)
{ typedef typename __type_traits<T>::has_trival_assignment_operator t;
    return __copy_t(first, last, result, t());}
};

这里__copy_dispatch的完全泛化版本根据迭代器种类不同,调用来不同的_cop(),为的是不同迭代器所使用的循环条件不同,有快慢区别。

//InputIterator
template<class InputIterator, class OutputIterator>
inline OutputIterator __copy(InputIterator first, InputIterator last, OutputIterator result, input_iterator_tag)
{
    //以迭代器是否相等决定循环是否继续,速度慢
    for(; first != last; ++result, ++first)
    {
        *result = *first;
    }
    return result;
}

//RandomAccessIterator版本
template<class RandomAccessIterator, class OutputIterator>
inline OutputIterator __copy(RandomAccessIterator first, 
                            RandomAccessIterator  last, 
                            OutputIterator result, 
                            random_access_iterator_tag)
{
    //又划分出一个函数,为的是其他地方也能用到
    return __copy_d(first, last, result, distance_type(first));
}

template<class RAI, class OPI, class Distance>
inline OutputIterator __copy_d(RAI first, RAI last, OPI result, Distance*)
{
    //以n决定循环次数,速度快
    for(Distance n = last - first; n > 0; --n, ++result, ++first)
        *result = *first;
    return result;
}

上面是__copy_dispatch()完全泛化的内容。现在回到它的两个特化版本。两个偏特化版本在“参数是原生指针形式”的前提下,希望进一步探测“指针所指之物是否具有trivial assignment operator(平凡赋值操作符)。这对效率有很大影响,因为这里的复制操作是由assignment负责的,如果指针所指向的对象拥有non_trivial assignment operator,复制操作就一定得通过它来执行。但如果指针指向但是trivial assignment operator的对象,复制操作就可以不通过它,直接以最快速的内存对拷方式(memmove)完成。c++语言无法让你侦测到某个对象的型别是否具有trivial assignment operator, 但是SGI STL采用的type_trait技巧可以弥补。代码如下:

//以下版本适用于指针所指向之物有trivial assignment operator
template<T>
inline T* __copy_t(const T* first, const T* last, T* result, __true_type)
{
    memove(result, first, sizeof(T)*(last - first));
    return result + (last - first);
}

//非trivial assignment operator
template<T>
inline T* __copy_t(const T* first, const T* last, __false_type)
{
    return __copy_d(first, last, result, (ptrdiff_t*)0); //这里传0也是为来效率考虑,这样
                                                         //可以少调用构造函数,而且让编
                                                         //译器知道类型              
}

现在又有一个问题。因为stl只记录了c++的型别,而对我们自己定义的有trivial assignment operator的类没有记录,所以即使有triveal assignment operator ,c++也是按照non_trivral来调用的。那怎么让c++知道我们的类有trivial assignment operator呢?

我们需要将<type_trains.h>中的__type_traits<T>做一个特化版本:

__STL_TEMPLATE_NULL struct __type_traits<C>
{
    typedef __false_type has_trivial_default_constructor;
    typedef __true_type  has_trivial_copy_constructor;
    typedef __true_type  has_trivial_assignment_operator;
    typedef __true_type  has_trivral_destructor;
    typedef __true_type  is_POD_type;
}

现在编译器就知道了。

猜你喜欢

转载自blog.csdn.net/Pokemon_Master/article/details/82626858