1. 程式人生 > >c++ 原始碼 之 辨準庫swap

c++ 原始碼 之 辨準庫swap

寫這篇文章的原因,希望自己能在技術的道路上越走越遠,在一年前使用c語言寫了些資料結構後,學會了使用工具的stl,但是希望自己能在原始碼上對自己使用的stl更加的深入瞭解,由於本人的學識淺薄,如有錯誤,還望摘正

首先我們要有stl source code 使用github下載

git clone https://github.com/gcc-mirror/gcc

如果沒有安裝git使用上述的網站即可

開啟gcc-master後 然後我們的目光聚焦在libstdc++-v3資料夾裡的內容

所以我們的路徑是gcc-master/libstdc++-v3/include/std

關於順序,因為筆者是邊看原始碼邊寫理解所以順序不一定是最好的

我們先看std的algorithm部分

***** algorithm header *****
#ifndef _GLIBCXX_ALGORITHM
#define _GLIBCXX_ALGORITHM 1

#pragma GCC system_header

#include <utility> // UK-300.
#include <bits/stl_algobase.h>
#include <bits/stl_algo.h>

#ifdef _GLIBCXX_PARALLEL
# include <parallel/algorithm>
#endif #endif /* _GLIBCXX_ALGORITHM */
#ifndef 
#define 


#endif 

簡單解釋下 , 如果同一個標頭檔案被include兩次,其中定義的ADT可能會導致重定義的錯誤,使用include guard 保證編譯器可以跳過XXX,當XXX已經被定義。

#pragma GCC system_header
這一行程式碼告訴編譯器 整個檔案後面的內容都視作 系統標頭檔案

接下來的內容我們發現 平時我們#include < algorithm >真正的實現內容在接下來的標頭檔案中

接著 基礎的真正的演算法實現部分
#include <utility> // UK-300.
#include <bits/stl_algobase.h> #include <bits/stl_algo.h> 下面 如果定義了_GLIBCXX_PARALLEL巨集則包含<parallel/algorithm> 大概就是如果 巨集定義了一個並行的 標誌 則需要多include 一個實現 #ifdef _GLIBCXX_PARALLEL # include <parallel/algorithm>

我們先看#include < bits/stl_algobase.h >熱熱身 這也是 algorithm標頭檔案真正實現的部分

***** bits/stl_algobase.h *****
標頭檔案先忽略  

巨集定義也先忽略

如果c++版本小於11的swap先忽略

/**    我們扯到現在正兒八經看的第一段程式碼
*  @brief Swaps the contents of two iterators. 行為
*  @ingroup mutating_algorithms 分組
*  @param  __a  An iterator. 引數
*  @param  __b  Another iterator.
*  @return   Nothing. 返回
*
*  This function swaps the values pointed to by two iterators, not the iterators themselves.
*/
template<typename _ForwardIterator1, typename _ForwardIterator2>
inline void
iter_swap(_ForwardIterator1 __a, _ForwardIterator2 __b)
{
  // concept requirements
  __glibcxx_function_requires(_Mutable_ForwardIteratorConcept<
  _ForwardIterator1>)
  __glibcxx_function_requires(_Mutable_ForwardIteratorConcept<
  _ForwardIterator2>)

#if __cplusplus < 201103L
    ....c++ 11 以下的版本我們暫時忽略
#else
  swap(*__a, *__b);
#endif
}

先解釋__glibcxx_function_requires
__glibcxx_function_requires是巨集展開

#define __glibcxx_function_requires(...)                                 __gnu_cxx::__function_requires< __gnu_cxx::__VA_ARGS__ >();

__gnu_cxx::__function_requires是__gnu_cxx::名字空間下的函式

template <class _Concept>
inline void __function_requires()
{
  void (_Concept::*__x)() _IsUnused = &_Concept::__constraints;
}

對於下面的這段程式碼而言

__glibcxx_function_requires(_Mutable_ForwardIteratorConcept<  _ForwardIterator1>)

會在推斷不是可以修改的前向迭代器時終止編譯

最後看swap的實現

上述 swap在stl_algobase.h中的實現
template<typename _Tp>
inline void
swap(_Tp& __a, _Tp& __b)
{
  // concept requirements
  __glibcxx_function_requires(_SGIAssignableConcept<_Tp>)
  _Tp __tmp = __a;
  __a = __b;
  __b = __tmp;
}

首先我們認為迭代器是指標
然後使用把指向的內容取引用進行交換

接著看

把 一個容器【B,E】與另外一個容器交換
/**
*  @brief Swap the elements of two sequences.
*  @ingroup mutating_algorithms
*  @param  __first1  A forward iterator.
*  @param  __last1   A forward iterator.
*  @param  __first2  A forward iterator.
*  @return   An iterator equal to @p first2+(last1-first1).
*
*  Swaps each element in the range @p [first1,last1) with the
*  corresponding element in the range @p [first2,(last1-first1)).
*  The ranges must not overlap.
*/
template<typename _ForwardIterator1, typename _ForwardIterator2>
_ForwardIterator2
swap_ranges(_ForwardIterator1 __first1, _ForwardIterator1 __last1,
_ForwardIterator2 __first2)
{
    // concept requirements這三個型別檢查 需要可以修改的前向迭代器
    __glibcxx_function_requires(_Mutable_ForwardIteratorConcept<
    _ForwardIterator1>)
    __glibcxx_function_requires(_Mutable_ForwardIteratorConcept<
    _ForwardIterator2>)
    __glibcxx_requires_valid_range(__first1, __last1);

    for (; __first1 != __last1; ++__first1, (void)++__first2)
    std::iter_swap(__first1, __first2);呼叫我們上面說的迭代器交換 
    return __first2;
}

我們把前面的內容提煉一下
於是現在我們可以自己編寫屬於我們自己的swap

#include <vector>
#include <iostream>
//使用不同的名字空間 這是實現的核心
namespace lthstd{
  template <typename myTp>
  inline void
  myswap( myTp& __a , myTp& __b ){
    myTp __tmp = __a ;
    __a = __b;
    __b = __tmp ;
  }
  template<typename _forwardit1 , typename _forwardit2>
  inline void
  myiter_swap ( _forwardit1 __a , _forwardit2 __b){
    myswap( *__a , *__b );
  }
  template<typename _forwardit1, typename _forwardit2>
    _forwardit2
    swap_ranges(_forwardit1 __first1, _forwardit1 __last1,
    _forwardit2 __first2)
    {
        for (; __first1 != __last1; ++__first1, (void)++__first2)
            iter_swap(__first1, __first2);//呼叫我們上面說的迭代器交換 
        return __first2;
    }
}
int main(){
    std::vector< int > test { 3, 8 ,10, 24 } ;
    for( auto &x : test ) {
      std::cout << x << ' ' ;
    }
    std::cout <<'\n' ;
    auto it1 = test.begin() ;
    auto it2 = test.begin()+2;
    //確保呼叫的是我們自己的swap

    lthstd::myiter_swap( it1 ,it2 ) ;
    for( auto &x : test) {
      std::cout << x <<' ';
    }
    cout <<'\n';
    std::vector< int > test2 {123,23,12,324,32525};
    lthstd::swap_ranges( test.begin(), test.end() , test2.begin() ) ;  
    for( auto &x : test) {
      std::cout << x <<' ';
    }
    cout <<'\n';
    for( auto &x :test2) {
        std::cout << x <<' ';
    }
    /*最後結果希望讀者自己編寫
    因為vector的初始化使用了init list
    所以版本推薦c++ 11 以上*/
    return 0 ;
}

相關推薦

c++ 原始碼 swap

寫這篇文章的原因,希望自己能在技術的道路上越走越遠,在一年前使用c語言寫了些資料結構後,學會了使用工具的stl,但是希望自己能在原始碼上對自己使用的stl更加的深入瞭解,由於本人的學識淺薄,如有錯誤,還望摘正 首先我們要有stl source code 使

c++ 原始碼 標準min與max

我覺得學習是需要正反饋了 如果覺得標準庫原始碼很難讀懂的話 是時候出現一些給予自己正反饋的內容 來看在 < bits/stl_algobase.h >裡的min與max

C++ PRIMER3 標準型別

1 using 宣告 一次只能用於一個名稱空間 儘量不要在標頭檔案中使用 2 string 型別 #include<string> 構造: string s1("test") s2(n, 'c') getline 獲取一行 直到換行符 重要操作: empty

C++Boost標準配置

下載安裝 進入官網下載地址:https://www.boost.org/users/download/ 本教程直接下載官方已編譯庫,不涉及原始碼手動編譯 點選官方編號好的連結,然後進入一個下載地址:https://sourceforge.net/projects/boost/files/boost-

C#中操作數據技術ExecuteNonQuery用法

pen cte assign == ted for 返回 簡單的 splay 最近在補基礎知識,剛好補到C#中對數據庫操作的一些技術,今天學習了ExecuteNonQuery的東西,看自己項目維護項目的代碼和網上資料查詢,基本上搞懂了ExecuteNonQuery的用法,小

C++標準程式

標準程式庫 C++中的標準程式庫是類庫和函式的集合,其使用核心語言寫成。標準程式庫提供若干泛型容器、函式物件、泛型字串和流(包含互動和檔案I/O),支援部分語言特性和常用的函式,如開平方根。C++標準程式庫也吸收了ISO C90 C標準程式庫。標準程式庫的特性聲明於std名稱空間之中。

ICTCLAS2016 linux C++版本 使用教程 Eclipse下開發C/C++程式標頭檔案,檔案引用

歡迎使用NLPIR/ICTCLAS2014分詞系統 NLPIR分詞系統前身為2000年釋出的ICTCLAS詞法分析系統,從2009年開始,為了和以前工作進行大的區隔,並推廣NLPIR自然語言處理與資訊檢索共享平臺,調整命名為NLPIR分詞系統。張華平博士先後傾力打造十餘

C++學習分支語句和邏輯運算子(if語句、邏輯表示式、字元函式(cctype)和?:運算子)

1.當C++程式必須決定是否執行某個操作時,通常使用if語句來實現操作。if有兩種格式:if和if else. if(test-condition) statement 如果測試條件為true,則if語句將載入程式執行語句或語句塊;如果條件是false,程式將跳

動態規劃最長單調遞增子序列(C++原始碼

動態規劃之最長單調遞增子序列 問題: L={a1,a2,a3,…,an}既L是由n個不同的實陣列成的序列,求L的最長單調遞增子序列(下標可不連續)。 分析: 設輔助陣列b,b[i]表示以a[i]為結尾的最長遞增子序列的長度,最長遞增子序列的長度,就是陣列b的最大

動態規劃最長公共子序列(C++原始碼

動態規劃之最長公共子序列 問題: 在序列X={x1,x2,…,xm}與Y={y1,y2,…,yn}中查詢長度最長的公共子序列,往往不是一個。例如:X={A,B,C,B,D,A,B},Y={B,D,C,A,B,A},則公共子序列有Z={B,C,B,A},Z1={B

SpringBoot原始碼ApplicationContextInitializer《C extends ConfigurableApplicationContext》

回撥介面,用於在{@linkplain ConfigurableApplicationContext#refresh() refreshed}之前初始化Spring配置的應用程式上下文。 通常在需要應用程式上下文初始化程式設計的Web應用程式中使用。 支援發現 Sprin

jstlc標籤、Function函式

JSTL組成: JSTL –Core 核心標籤庫。 - 這是本部分的重點 JSTL – I18N - 國際化標籤庫。Internationalization- I18N JSTL - Functions – 函式庫。 JSTL – SQL – 資料

c++ 自己寫函式 (靜態

c++ 庫函式,分為靜態庫,靜態庫的好處就是沒有太多約束,靜態庫寫的類,可以被整合。缺點是,程式碼是直接編譯到目標檔案裡面,所以程式碼量會增加 。 庫函式分為 Win32 Application和Win32 Console Application 和MFC  庫。 Win3

基於VS 2010下圖形版本的 C++程式推箱子

/*** win32控制檯版本的推箱子 ****/ /**********  2017.2.21   make by  qiu **********/ /***牆 1,箱子 4,目的地 3,人 5,空地0 ********/ /*********箱子在目地地 4+3 ***

1.Python呼叫C語言如何呼叫動態連結

平時用C&C++和Python比較多,喜歡鼓搗點小玩意兒。之前在《數學之美》這本書裡面看見布隆過濾器這個東西,簡直是爬蟲利器!所以當時用C++寫了一個簡單的,後來封裝成了動態連結庫拿來給爬蟲用。所以就研究了一下怎麼用Ptython呼叫C語言,寫個博文記錄一下! Py

P2PUDP穿透NAT的原理與實現(附C++原始碼)[轉載]

論壇上經常有對P2P原理的討論,但是討論歸討論,很少有實質的東西產生(原始碼)。呵呵,在這裡我就用自己實現的一個原始碼來說明UDP穿越NAT的原理。 首先先介紹一些基本概念:     NAT(Network Address Translators),網路地址轉換:網路地址轉換是在IP地址日益缺乏的情況下產生

JNI學習二C原始碼中Log輸出及常見錯誤)

瞭解jni JNI 即Java Native Interface ,Java本機介面。可以實現Java和C/C++之間的相互呼叫。 為什麼使用JNI? 擴充套件了Java虛擬機器的能力,C語言可以進行驅動開發,比如wifi共享熱點的驅動 Native c

C++學習路(13)---std(標準)和STL(標準模板)的關係

C++標準庫的所有標頭檔案都沒有副檔名。C++標準庫的內容總共在50個標準標頭檔案中定義,其中18個提供了C庫的功能。 形式的標準標頭檔案【 例外】其內容與ISO標準C包含的name.h標頭檔案相同,但容納了C++擴充套件的功能。在 形式標準的標頭檔案中,與巨集

ext2檔案系統原始碼dir.c

今天我們來看ext2原始碼的dir.c檔案,這個檔案是做和目錄有關的操作的,也是很重要的一個檔案,大家認真看哦,有什麼和我不一致的大家一起探討~~ 在講這個之前我覺得還是先說下ext2檔案系統的目錄方面的基礎比較好,這樣會更容易看明白。 ext2檔案系統分為inode和bl

Java學習通過JNI呼叫C/C++編寫的dll連結(圖文教程)

    看了網上幾個主要的教程,都會導致出現各種錯誤,對於初學者來說會造成一些困擾。在這裡詳細記錄一下JNI呼叫過程。本案例的基本配置:Eclipse+VS2015,Win10  64位 (1)編寫J