1. 程式人生 > >STL 簡單 copy 演算法的實現

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.設計與實現

本文介紹的演算法實現需要以下檔案:

1.    cghStl_algobase.h,本文的主角:演算法的原始碼,位於cghSTL/algorithms/

2.    cghUtil.h:包含copy演算法用到的memmove演算法,位於/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演算法的容器,位於/sequence containers/cghVector/想了解vector實現細節的同學請移步:STL簡單vector的實現

7.    cghDeque.h,自己實現的deque,用於測試copy演算法的容器,位於//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;
}


結果如下圖所示:


相關推薦

STL 簡單 copy 演算法實現

1.簡介          不論是對客戶端或對STL內部而言,copy() 都是一個常常被呼叫的函式。由於copy進行的是複製操作,而複製操作不外乎運用賦值運算子(assignment operator)或複製建構函式(copy constructor),但是某些元素的型別

STL 簡單 deque 的實現

Vector是單向開口的連續線性空間,而Deque是雙向開口的連續線性空間,可以在頭尾兩端插入和刪除元素,而且deque沒有容量(capacity)的概念,因為deque動態地以分段連續空間組合而成,隨時可以增加一段新的空間並連結起來。          Deque採用一

STL 簡單 iterator 的實現(含原始碼)

        我使用vs2015寫的程式(原始碼下載) STL的中心思想在於將容器(container)和演算法(algorithms)分開,彼此獨立設計,最後再以一貼膠著劑將它們撮合在一起,而這個膠著劑就是迭代器(iterator)。          迭代器是訪問容器

STL 簡單 list 的實現

相較於vector的連續線性空間,list就顯得複雜許多,它的好處是每次插入或刪除一個元素,就配置或釋放一個元素空間。因此,list對於空間的運用有絕對的精準,一點不浪費。Vector的插入操作可能造成記憶體的重新配置,但是List不會。          List不再能

STL原始碼分析之copy演算法

前言 在前面分析順序容器和關聯容器時, 總會遇到copy這個函式, 當時並沒有去分析這個函式, 畢竟都能知道他是什麼功能, 本節就來揭開它面紗. copy分析 copy函式原始碼在stl_algobase.h中, 該結構中還有很多其他的演算法實現, 我只是從中挑選出了copy

七種排序演算法簡單分析與實現

{    int nFirst = nLow;    int nMid = (nLow + nHigh) /2;    int nSecond = nMid +1;    int* p = (int*)malloc(sizeof(int) * (nHigh - nLow +1));    int nIndex

PHP使用hash演算法實現一個簡單的資料庫程式碼例項

咱們這次主要是使用PHP語言,結合hash演算法,來實現一個簡單的資料庫例項,它主要有四個功能,連線資料庫,查詢操作,插入操作,刪除操作,關閉資料庫連線操作,其它的大家可以後期補充完善下,咱廢話不多說,先來看程式碼: <?php header('Content-type:text/ht

CAS演算法實現資料更新的簡單理解和ABA問題

注:本文是在http://www.toutiao.com/i6421671637946466817/?wxshare_count=2&pbid=1498719626基礎上整理!CAS:Compare and Swap,即比較再交換。是無鎖操作,也有人說他是樂觀鎖,也許

Python基於Kmeans演算法實現文字聚類的簡單練習

接觸機器學習時間不長,也一直有興趣研究這方面的演算法。最近在學習Kmeans演算法,但由於工作的原因無法接觸到相關的專案實戰。為了理清思路、熟悉程式碼,在參照了幾篇機器學習大神的博文後,做了一個簡單的Kmeans演算法的簡單練習。作為一枚機器學習的門外漢,對於文中的一些錯誤和

使用二進位制和位移運算實現簡單演算法

1.將一個二進位制數向左位移n位就等於該數乘以2的n次方,當乘法運算中的某個數符合這個特點的時候,可以用位移運算代替乘法運算,從而提高效率。 package pp.suanfa; /** * 乘法運算轉位移運算 * * @author xiaoGd * */

第六章(1.3)自然語言處理實戰——使用tf-idf演算法實現簡單的文字聚類

一、原理 使用jieba切詞 用td-idf計算文字的詞頻和逆文件詞頻 排序選出最重要的2個或3個詞作為這段文字的id 具有相同id的文字被歸為一類 二、使用python實現簡單的文字聚類,其中使用了tf-idf演算法,jieba分詞,把相似的文字聚合在

stl中list的sort演算法實現

STL中有一個std::sort演算法,但它是不支援std::list的,因為list不提供RandomIterator的支援,但list自己提供了sort演算法,把list的元素按從小到大的方式來排序,程式碼長度到不長,但真是難以讀懂,後來扣持了一下午終於搞明白了,貼個總

STL 簡單 set 和 multiset 的實現

         set的特性是所有元素都會根據鍵值自動排序,set的元素不像map那樣同時擁有實值(value)和鍵值(key),set元素的鍵值就是實值,實值就是鍵值。Set不允許兩個元素擁有相同的鍵值。不能通過迭代器修改set元素的值。          multi

STL原始碼分析----神奇的 list 的 sort 演算法實現

STL中有一個std::sort演算法,但它是不支援std::list的,因為list不提供RandomIterator的支援,但list自己提供了sort演算法,把list的元素按從小到大的方式來排序,程式碼長度到不長,但真是難以讀懂,後來扣持了一下午終於搞明白了,貼個

STL 簡單紅黑樹的實現

1.紅黑樹簡介 二叉搜尋樹能夠提供對數的元素插入和訪問。二叉搜尋樹的規則是:任何節點的鍵值一定大於其左子樹的每一個節點值,並小於右子樹的每一個節點值。 常見的二叉搜尋樹有AVL-tree、RB-tree(紅黑樹)。紅黑樹具有極佳的增、刪、查效能,故我們選擇紅黑樹作為關聯式容

簡單的LRU演算法實現,執行緒安全的

LRU演算法用途之廣就不說了,凡是要用cache的地方都可以見到它的身影。特別是執行緒多,併發高,資料量大的環境下。jdk1.5真好,在LinkedHashMap.java的原始碼中直接有這樣的字樣、“This kind of map is well-suited to bu

使用C++STL中的deque實現作業系統FIFO、LRU頁面置換演算法

#include <iostream> #include <deque>//雙端佇列所在的標頭檔案 #include <algorithm>//find()函式所在的標頭檔案 using namespace std; cons

簡單測試--C#實現中文漢字轉拼音首字母

esp chart htm foreach ext ads linq 類庫 play 第一種: 這個是自己寫的比較簡單的實現方法,要做漢字轉拼音首字母,首先應該有一個存儲首字母的數組,然後將要轉拼音碼的漢字與每個首字母開頭的第一個漢字即“最小”的漢字作比較,這裏的最小指的是

Java 簡單的RPC 實現

com java多態 http 技術 images object ice ima framework 借用了網上某大神的例子。。。。 目錄結構是這樣的。。。 RpcFramework 主要是兩個方法。一個是暴露服務,一個為引用服務。暴露服務的主要作用是聲明一個接口的實現類。

設計模式之單例模式以及簡單代碼實現

以及 應用 安全 設計模式 div 代碼實現 測試類 加載類 實例 單例模式 保證整個應用某個實例只有一個 單例模式包括懶漢模式和餓漢模式 餓漢模式 懶漢模式 測試類 區別: 餓漢模式的特點加載類時比較慢,但運行獲取對象速度比較快 線程安全 懶漢模式加