1.简介
不论是对客户端或对STL内部而言,copy() 都是一个常常被调用的函数。由于copy进行的是复制操作,而复制操作不外乎运用赋值运算符(assignment operator)或复制构造函数(copy constructor),但是某些元素的类型是trivial assignment operator,因此如果能使用内存直接进行复制(例如使用C标准函数memmove、memcpy),便能节约大量时间。为此,copy算法用尽各种办法,包括函数重载(function overloading)、型别特性(type
traits)、偏特化(partial specialization)等编程技巧来加强效率。
copy算法可将输入区间 [first, last ) 内的元素复制到输出区间 [ result, result + ( last – first ) )内,并返回迭代器:result + (last – first ) 。copy对template参数所要求的条件非常宽松,输入区间有InputIterator 构成,输出由OutputIterator 构成。
由上图可知,copy算法从以下方面强化复制性能:
1. 迭代器指针类型,一般分为对象指针和原生指针。原生指针可直接使用memmove方式进行拷贝。像memmove这里的底层库函数,在汇编层面上做了优化,复制速度极快,有兴趣的童鞋请自行查阅相关资料;
2. 赋值运算符(assignment operator)类型,指针指向的对象,其赋值运算符可以分为trivial operator和non-trivial operator(trivial 和non-trivial 的概念请移步:stack overflow、CSDN),trivial
赋值运算符之间调用memmove,non-trivial的比较悲剧,只能挨个复制;
3. 迭代器类型,分为InputIterator、RandomAccessIterator。InputIterator类型的需要由迭代器来判断复制结束,我们知道,判断iter != contianer.end()需要调用operator!=重载函数,速度慢。RandomAccessIterator以距离是否相等来决定复制结束,速度较快。
2.设计与实现
我用VS2013写的程序(github),数值算法的实现版本的位于cghSTL/version/
cghSTL-0.5.2.1.rar
本文介绍的算法实现需要以下文件:
1. cghStl_algobase.h,本文的主角:算法的源码,位于cghSTL/algorithms/
2. cghUtil.h:包含copy算法用到的memmove算法,位于cghSTL/cghUtil/
3. cghSTL_type_traits.h,特性萃取机,可以判断对象是否为trivial或non-trivial;
4. cghSTL_iterator.h,迭代器的基类,共五种,所有迭代器都要继承于这五种基类,继承自不同基类的迭代器,拥有不同特性,在算法实现中,性能也不一样;
5. test_algorithms_algobase_copy.cpp,测试文件,位于cghSTL/test/;
6. cghVector.h,自己实现的vector,用于测试copy算法的容器,位于cghSTL/sequence
containers/cghVector/,想了解vector实现细节的同学请移步:STL简单vector的实现
7. cghDeque.h,自己实现的deque,用于测试copy算法的容器,位于cghSTL/sequence
containers/cghDeque/,想了解vector实现细节的同学请移步:STL简单deque的实现
为了增强代码的可读性,我用region把各个算法隔开,如下图所示
童鞋们可以借助上图和上上图来理解copy是框架和流程,直接上代码吧,注释已经说明了一切~
cghStl_algobase.h
/******************************************************************* * Copyright(c) 2016 Chen Gonghao * All rights reserved. * * [email protected] * * 文件内容:equal、fill、fill_n、iter_sawp、max、min、 lexicographical_compare、mismatch、copy 算法的实现 ******************************************************************/ #ifndef _CGH_STL_ALGOBASE_ #define _CGH_STL_ALGOBASE_ #include "cghUtil.h" #include "cghSTL_type_traits.h" #include "cghSTL_iterator.h" namespace CGH{ #pragma region copy #pragma region 第一层:copy 算法入口 /* copy 算法唯一的对外接口(完全泛化版)*/ template<class InputIterator, class OutputIterator> inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result) { return copy_dispatch<InputIterator, OutputIterator>()(first, last, result); } #pragma region copy的特化版(specialization) /* copy 算法针对const char* 版本的特化版 */ inline char* copy(const char* first, const char* last, char* result) { // 采用 memmove 的方式直接复制数据,cgh_memmove 函数位于 cghUtil.h 文件 cgh_memmove(result, first, last - first) ; return result + (last - first) ; } /* copy 算法针对const wchar_t* 版本的特化版 */ inline wchar_t* copy(const wchar_t* first, const wchar_t* last, wchar_t* result) { // 采用 memmove 的方式直接复制数据,cgh_memmove 函数位于 cghUtil.h 文件 cgh_memmove(result, first, sizeof(wchar_t) * (last - first)) ; return result + (last - first) ; } #pragma endregion #pragma endregion #pragma region 第二层:copy_dispatch template<class InputIterator, class OutputIterator> struct copy_dispatch{ InputIterator operator()(InputIterator first, InputIterator last, OutputIterator result) { return _copy(first, last, result, CGH::iterator_category(first)); } }; #pragma region copy_dispatch 的偏特化版本 /* 在参数为原生指针的情况下,进一步探测指针指向的对象是否具有 trivial assignment operator assignment operator 对效率的影响不小 1.如果指针指向的对象具有 non-trivial assignment operator,复制操作就必须通过 trivial assignment operator 进行 2.如果指针指向的对象具有 trivial assignment operator,我们完全可以通过最快的内存拷贝(memmove进行) */ template<class T> struct copy_dispatch<T*, T*>{ T* operator()(T* first, T* last, T* result) { // 通过特性萃取机(cghSTL_type_traits),获得对象 assignment operator 类型 typedef typename cghSTL_type_traits<T>::has_trivial_assignment_operator t; return _copy_t(first, last, result, t()); } }; /* 在参数为原生指针的情况下,进一步探测指针指向的对象是否具有 trivial assignment operator assignment operator 对效率的影响不小 1.如果指针指向的对象具有 non-trivial assignment operator,复制操作就必须通过 trivial assignment operator 进行 2.如果指针指向的对象具有 trivial assignment operator,我们完全可以通过最快的内存拷贝(memmove进行) */ template<class T> struct copy_dispatch<const T*, T*>{ T* operator()(T* first, T* last, T* result) { // 通过特性萃取机(cghSTL_type_traits),获得对象 assignment operator 类型 typedef typename cghSTL_type_traits<T>::has_trivial_assignment_operator t; return _copy_t(first, last, result, t()); } }; #pragma endregion #pragma endregion #pragma region 第三层 template<class InputIterator, class OutputIterator> inline OutputIterator _copy(InputIterator first, InputIterator last, OutputIterator result, CGH::input_iterator_tag) { // 以迭代器是否到达末尾,决定循环结束,速度慢 for (; first != last; ++result, ++first) { *result = *first ; } return result ; } template<class RandomAccessIterator, class OutputIterator> inline OutputIterator _copy(RandomAccessIterator first, RandomAccessIterator last, OutputIterator result, CGH::random_access_iterator_tag) { return _copy_d(first, last, result, distance_type(first)) ; } /* 如果对象拥有 non-trivial assignment operator 那么直接进行内存拷贝 */ template<class T> inline T* _copy_t(const T* first, const T* last, T* result, true_type) { cgh_memmove(result, first, sizeof(T) * (last - first)) ; return result + (last - first) ; } /* 如果对象拥有 trivial assignment operator 那么调用_copy_d,用自定义的 assignment operator,挨个对象拷贝 */ 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); } template<class RandomAccessIterator, class OutputIterator, class Distance> inline OutputIterator _copy_d(RandomAccessIterator first, RandomAccessIterator last, OutputIterator result, Distance*) { // 以 n 决定循环次数,速度比用迭代器决定循环次数更高效 for (Distance n = last - first; n > 0; --n, ++result, ++first) { *result = *first ; } return result ; } #pragma endregion #pragma endregion } #endif
3.测试
Copy算法的测试工作量不小,要从一下方面入手:
1. 原生指针的测试;
2. trivial和non-trivial 赋值运算符的测试;
3. InputIterator、RandomAccessIterator迭代器;
测试环节的主要内容已在注释中说明
/******************************************************************* * Copyright(c) 2016 Chen Gonghao * All rights reserved. * * [email protected] * * 文件内容:cghStl_algobase.h 中的copy的测试 ******************************************************************/ #include "stdafx.h" #include "cghVector.h" #include "_cghList.h" #include "cghDeque.h" #include "cghStl_algobase.h" class C { public: C() :_data(0){} C(int n) :_data(n){} int _data; }; int _tmain(int argc, _TCHAR* argv[]) { using namespace::CGH; std::cout << "********************** 测试 copy 针对const char* 的特化版 *********************" << std::endl << std::endl; std::cout << "调用的版本:copy ( const char* )" << std::endl; const char srcChar[5] = {'a', 'b', 'c', 'd', 'e'}; char destChar[5]; CGH::copy(srcChar, srcChar + 5, destChar); for (int i = 0; i < 5; ++i) { std::cout << "srcChar[" << i << "] = " << srcChar[i] << "\t\t" << "destChar[" << i << "] = " << destChar[i] << std::endl ; } std::cout << std::endl; std::cout << "********************* 测试 copy 针对const wchar_t* 的特化版 ********************" << std::endl; std::cout << "调用的版本:copy ( const wchar_t* )" << std::endl; const wchar_t srcWchar[5] = {'a', 'b', 'c', 'd', 'e'}; wchar_t destWchar[5]; CGH::copy(srcWchar, srcWchar + 5, destWchar); for (int i = 0; i < 5; ++i) { std::cout << "srcWchar[" << i << "] = " << srcWchar[i] << "\t\t" << "destWchar[" << i << "] = " << destWchar[i] << std::endl ; } std::cout << std::endl; std::cout << "********************* 测试 int[] ***********************************************" << std::endl; std::cout << "依次调用:copy() --> copy_dispatch() --> _copy( input_iterator )" << std::endl; int arrSrc[5] = {0, 1, 2, 3, 4}; int arrDest[5]; CGH::copy(arrSrc, arrSrc + 5, arrDest); for (int i = 0; i < 5; ++i) { std::cout << "arrSrc[" << i << "] = " << arrSrc[i] << "\t\t" << "arrDest[" << i << "] = " << arrDest[i] << std::endl; } std::cout << std::endl; std::cout << "********************* 测试 deque<C> (C为自定义的类)***************************" << std::endl; std::cout << "依次调用:copy() --> copy_dispatch() --> _copy( random_access_iterator_tag )\n\t\t\t\t\t\t\t --> _copy_d()" << std::endl; C cDequeElem[5]; cghDeque<C> cSrcDeq; cDequeElem[0]._data = 100; cDequeElem[1]._data = 101; cDequeElem[2]._data = 102; cDequeElem[3]._data = 103; cDequeElem[4]._data = 104; cSrcDeq.push_back(cDequeElem[0]); cSrcDeq.push_back(cDequeElem[1]); cSrcDeq.push_back(cDequeElem[2]); cSrcDeq.push_back(cDequeElem[3]); cSrcDeq.push_back(cDequeElem[4]); cghDeque<C> cDestDeq(5, cDequeElem[0]); copy(cSrcDeq.begin(), cSrcDeq.end(), cDestDeq.begin()); int m_cDeq = 0; for (cghDeque<C>::iterator it = cDestDeq.begin(); it != cDestDeq.end(); ++it) { std::cout << "cSrcDeq[" << m_cDeq << "] = " << it->_data << "\t\t" << "cSrcDeq[" << m_cDeq << "] = " << it->_data << std::endl; m_cDeq++; } std::cout << std::endl; std::cout << "********************* 测试 vector<int> *****************************************" << std::endl; std::cout << "依次调用:copy() --> copy_dispatch( T*, T* ) --> _copy( true_type )" << std::endl; cghVector<int> vecIntSrc; vecIntSrc.push_back(1); vecIntSrc.push_back(2); vecIntSrc.push_back(3); vecIntSrc.push_back(4); vecIntSrc.push_back(5); cghVector<int> vecIntDest(5); CGH::copy(vecIntSrc.begin(), vecIntSrc.end(), vecIntDest.begin()); for (int i = 0; i < 5; ++i) { std::cout << "vecIntSrc[" << i << "] = " << vecIntSrc[i] << "\t\t" << "vecIntDest[" << i << "] = " << vecIntDest[i] << std::endl; } std::cout << std::endl; std::cout << "********************* 测试 vector<C> (C为自定义的类)**************************" << std::endl; std::cout << "依次调用:copy() --> copy_dispatch( T*, T* ) --> _copy_t( false_type )\n\t\t\t\t\t\t\t --> _copy_d()" << std::endl; C cVecSrcElem[5]; cVecSrcElem[0]._data = 0; cVecSrcElem[1]._data = 1; cVecSrcElem[2]._data = 2; cVecSrcElem[3]._data = 3; cVecSrcElem[4]._data = 4; cghVector<C> cVecSrc; cVecSrc.push_back(cVecSrcElem[0]); cVecSrc.push_back(cVecSrcElem[1]); cVecSrc.push_back(cVecSrcElem[2]); cVecSrc.push_back(cVecSrcElem[3]); cVecSrc.push_back(cVecSrcElem[4]); cghVector<C> cVecDest(5); copy(cVecSrc.begin(), cVecSrc.end(), cVecDest.begin()); int m_cVec = 0; for (cghVector<C>::iterator it = cVecDest.begin(); it != cVecDest.end(); ++it) { std::cout << "cVecSrc[" << m_cVec << "] = " << it->_data << "\t\t" << "cVecDest[" << m_cVec << "] = " << it->_data << std::endl; m_cVec++; } std::cout << std::endl; system("pause"); return 0; }
结果如下图所示: