STL简单 copy 算法的实现

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 overflowCSDN),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;
}

结果如下图所示:

时间: 2024-10-05 04:43:08

STL简单 copy 算法的实现的相关文章

STL简单&lt;stl_algorithms.h&gt;算法的实现

1.简介 STL标准中,没有区分基本算法和复杂算法,然而SGI STL却把常用的算法定义在<stl_algorithms.h>中.本文介绍部分<stl_algorithms.h>算法的实现,给出实现代码和测试代码. 本文介绍的算法包括: 1.      mismatch:比较两个序列,指出两者之间第一个不匹配的点,返回一对迭代器,分别指向两序列中不匹配的点: 2.      equal:如果两个序列在 [first, last ] 区间内相等,equal() 返回true,忽略第二

探讨排序算法的实现

排序算法是我们工作中使用最普遍的算法,常见的语言库中基本都会有排序算法的实现,比如c标准库的qsort,stl的sort函数等.本文首先介绍直接插入排序,归并排序,堆排序,快速排序和基数排序等比较排序算法,然后介绍计数排序,基数排序等具有线性时间的排序算法.本文主要讨论算法的实现方法,并不会过多介绍基本理论. 评价一个排序算法优劣适用与否,一般需要从三个方面来分析 时间复杂度.用比较操作和移动操作数的最高次项表示,由于在实际应用中最在乎的是运行时间的上限,所以一般取输入最坏情况的下的运行时间作为

搜索引擎--范例:中英文混杂分词算法的实现--正向最大匹配算法的原理和实现

纯中文和中英文混杂的唯一区别是,分词的时候你如何辨别一个字符是英文字符还是孩子字符, 人眼很容易区分,但是对于计算机来说就没那么容易了,只要能辨别出中文字符和英文的字符,分词本身就不是一个难题 1:文本的编码问题: utf8:windows下,以utf8格式保存的文本是一个3个字节(以16进制)的BOM的,并且你不知道一个汉字是否是用3位表示,但是英文适合ascii编码一样的 ascii:英文一位,中文两位,并且中文的第一个字节的值是大于128和,不会和英文混淆,推荐 unicode:中文基本是

Bug2算法的实现(RobotBASIC环境中仿真)

移动机器人智能的一个重要标志就是自主导航,而实现机器人自主导航有个基本要求--避障.之前简单介绍过Bug避障算法,但仅仅了解大致理论而不亲自动手实现一遍很难有深刻的印象,只能说似懂非懂.我不是天才,不能看几遍就理解理论中的奥妙,只能在别人大谈XX理论XX算法的时候,自己一个人苦逼的面对错误的程序问为什么... 下面开始动手来实现一下简单的Bug2避障算法.由于算法中涉及到机器人与外界环境的交互,因此需要选择一个仿真软件.常用的移动机器人仿真软件主要有Gazebo.V-rep.Webots.MRD

软考笔记第六天之各排序算法的实现

对于前面的排序算法,用c#来实现 直接插入排序: 每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序.第一趟比较前两个数,然后把第二个数按大小插入到有序表中: 第二趟把第三个数据与前两个数从前向后扫描,把第三个数按大小插入到有序表中:依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程.直接插入排序属于稳定的排序,最坏时间复杂性为O(n^2),空间复杂度为O(1).直接插入排序是由两层嵌套循环组成的.外层循环标识并决定待比较的数值.内层循环为待比较数值确定其最终位

数组正负元素前后移动算法的实现(以0为分界线)

mnesia在频繁操作数据的过程可能会报错:** WARNING ** Mnesia is overloaded: {dump_log, write_threshold},可以看出,mnesia应该是过载了.这个警告在mnesia dump操作会发生这个问题,表类型为disc_only_copies .disc_copies都可能会发生. 如何重现这个问题,例子的场景是多个进程同时在不断地mnesia:dirty_write/2 mnesia过载分析 1.抛出警告是在mnesia 增加dump

RMQ问题总结,标准RMQ算法的实现

RMQ问题:对于长度为N的序列,询问区间[L,R]中的最值 RMQ问题的几种解法: 普通遍历查询,O(1)-O(N) 线段树,O(N)-O(logN) DP,O(NlogN)-O(1) RMQ标准算法,O(N)-O(1) 简单介绍: 朴素的查询,不需要任何预处理,但结果是没有任何已知的信息可以利用,每次都需要从头遍历到尾. 线段树,区间问题的神器,用线段树做比起朴素的暴力查询要快得多,关键在于线段树使用了分治思想,利用了区间问题的可合并性.任何一个区间最多只需要logN个线段树上的区间来合并,线

最小生成树之Prim算法的实现

Prim算法的思想是,首先从任意一个节点出发,逐渐生成,直至该树覆盖了所有的V中的节点. 如下图: 图中的黑色的边即是最小生成树中的边. 实现Prim算法的关键便是,如何选择一条 "权值较小并且对于已有生成树中的点集合S来说是安全的边",此处的安全指的是: 加入该边e之后,S仍然是一个树. 于是:对于图 G = (V, E) Prim的执行步骤大致为: 1   从任意节点r开始,因此将原来的顶点集合分为两部分: S = {r}, T = V-{s}, 2   计算T中的节点到S中的节点

C#下RSA算法的实现(适用于支付宝和易宝支付)

C#下RSA算法的实现(适用于支付宝和易宝支付) 目录(?)[-] RSA算法代码 RSA算法测试代码 RSA算法代码: [csharp] view plain copy using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Security.Cryptography; namespace RSA.Class { /// <summary> /// 类名: