不论是对客户端或对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以距离是否相等来决定复制结束,速度较快。
我用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
inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result)
{
return copy_dispatch()(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
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
struct copy_dispatch{
T* operator()(T* first, T* last, T* result)
{
// 通过特性萃取机(cghSTL_type_traits),获得对象 assignment operator 类型
typedef typename cghSTL_type_traits::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
struct copy_dispatch{
T* operator()(T* first, T* last, T* result)
{
// 通过特性萃取机(cghSTL_type_traits),获得对象 assignment operator 类型
typedef typename cghSTL_type_traits::has_trivial_assignment_operator t;
return _copy_t(first, last, result, t());
}
};
#pragma endregion
#pragma endregion
#pragma region 第三层
template
inline OutputIterator _copy(InputIterator first, InputIterator last, OutputIterator result, CGH::input_iterator_tag)
{
// 以迭代器是否到达末尾,决定循环结束,速度慢
for (; first != last; ++result, ++first)
{
*result = *first ;
}
return result ;
}
template
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
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
inline T* _copy_t(const T* first, const T* last, T* result, false_type)
{
return _copy_d(first, last, result, (ptrdiff_t*)0);
}
template
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
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为自定义的类)***************************" << 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 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 cDestDeq(5, cDequeElem[0]);
copy(cSrcDeq.begin(), cSrcDeq.end(), cDestDeq.begin());
int m_cDeq = 0;
for (cghDeque::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 *****************************************" << std::endl;
std::cout << "依次调用:copy() --> copy_dispatch( T*, T* ) --> _copy( true_type )" << std::endl;
cghVector vecIntSrc;
vecIntSrc.push_back(1);
vecIntSrc.push_back(2);
vecIntSrc.push_back(3);
vecIntSrc.push_back(4);
vecIntSrc.push_back(5);
cghVector 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为自定义的类)**************************" << 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 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 cVecDest(5);
copy(cVecSrc.begin(), cVecSrc.end(), cVecDest.begin());
int m_cVec = 0;
for (cghVector::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;
}
结果如下图所示: