C++11的for迴圈,以及範圍Range類的實現

来源:http://www.cnblogs.com/androidshouce/archive/2016/06/24/5613014.html
-Advertisement-
Play Games

C++11支持range-based for迴圈。這是一個很方便的特性,能省挺多代碼。以下代碼就能很方便的遍歷vector中的元素,並列印出來: 1 2 3 4 5 6 7 8 std::vector<int> int_vec; int_vec.push_back(1); int_vec.push_ ...


C++11支持range-based for迴圈。這是一個很方便的特性,能省挺多代碼。以下代碼就能很方便的遍歷vector中的元素,並列印出來:

1 2 3 4 5 6 7 8 std::vector<int> int_vec; int_vec.push_back(1); int_vec.push_back(2); //如果要修改int_vec中的元素,將變數x聲明為 int& 即可 for (int x: int_vec) {     std::cout << x << endl; }

可以遍歷的對象包括:

  • 數組。(不包括指針)
  • 定義了begin()和end()方法,且返回該方法返回迭代器的類對象。(STL 中所有容器都可以)

(對於動態生成的數組的遍歷,用下麵介紹的Range類也能省不少代碼)

參考http://en.cppreference.com/w/cpp/language/range-for 可知,

       語句 for ( range_declaration : range_expression) loop_statement

與以下語句作用等價:

1 2 3 4 5 6 7 8 9 {     auto && __range = range_expression ;     for (auto __begin = begin_expr,             __end = end_expr;             __begin != __end; ++__begin) {         range_declaration = *__begin;     loop_statement     } }

對於可遍歷的類對象,__begin和__end分別由類的begin()和end()方法產生。且由於__range變數是右值引用,如果range_expression的結果是右值,其將會在迴圈結束後析構。


這樣,C++11終於支持了這種現代編程語言都支持的遍歷方式了。但是,無論是語法還是標準庫都不支持對具體數字的遍歷,比如python中的 for i in xrange(1,5)語句中,x將連續取[1,4]中的值。(Boost庫有irange類可以滿足這個需求,但是下麵會討論下我的實現)

最直接的方法,就是寫一個函數,返回一個vector<int>對象,其元素為從begin到end的值。但這樣每次迴圈時都得構造一個這樣的對象,略慢。

從標準來看,如果一個類要支持這樣遍歷,至少得有begin()和end()方法。在for迴圈的初始化部分,調用了這兩個方法之後,就沒這個類啥事了——都是迭代器的事。所以很自然,從迭代器上下手。這個迭代器必須支持三種操作:!=,首碼++,解引用。有沒發現,如果這個“迭代器”是個int數值的話,上面迴圈中的__begin!=__end;++__begin語句就是一個非常自然的實現。現在的目標很簡單了:這個“迭代器”不遍歷容器中的每個元素,而就是一個簡單的int數值的封裝。對其解引用將返回這個數,而比較和自加操作均對這個數進行操作。


有想法之後,實現起來就很容易了。首先定義一個仿迭代器 FakeIter ,其對一個數值進行封裝,並重載必須的操作符。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class FakeIter {     typedef long _VType;  //數值的類型為long。當然了,也可以寫個模板出來 public:       explicit FakeIter(_VType val)         :value_(val){}       bool operator != (const FakeIter& other) const     {         return (this->GetValue()) != (other.GetValue());     }     _VType operator* () const     {         return GetValue();     }     const FakeIter& operator++ ()     {         ++value_;         return *this;     } private:     _VType GetValue() const     {         return value_;     }     _VType value_; };

至於“容器”類的實現,就更簡單了:實現begin()和end()方法,並返回上面的FakeIter就好了。類中的方法加了一些cout語句,可以更清楚的瞭解迴圈執行時具體方法的調用過程,實際用時可以刪掉。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Range {     typedef long _VType;    //同樣,也可以弄個模板出來,但是就不方便用了 public:     Range (_VType begin_v, _VType end_v)         :begin_value_(begin_v), end_value_(end_v)     {         cout<<"Range::Range()"<<endl;     }     ~Range()     {         cout<<"Range::~Range()"<<endl;     }       FakeIter begin () const     {         cout<<"Range::begin()"<<endl;         return FakeIter(begin_value_);     }     FakeIter end () const     {         cout<<"Range::end()"<<endl;         return FakeIter(end_value_ );     } private:     _VType begin_value_;     _VType end_value_; };

好了,試試看這貨有沒用:

1 2 3 4 5 for (auto x: Range(1,5)) {     std::cout<<x<<endl; } std::cout<<"Loop end"<<endl;

在vs2012及clang下輸出如下:

Range::Range() 
Range::begin() 
Range::end() 




Range::~Range() 
Loop end

嗯,如果把那些影響視線的輸出語句的註釋掉的話貌似能用了。現在要遍歷一個new生成的數組的話,只需用這東西Range個下標,世界就清靜了。

但是,步長呢!好吧,貌似我暫時還沒這方面的需求。不過實現起來也很簡單:修改FakeIter類就可以了,可以增加一個表示步長的成員,然後再修改自加操作。更進一步,也可以加一個生成器方法,就能變成一個更通用的生成器了。配合C++11 lambda操作符,用起來也挺方便。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 首先,我們定義一個Student類來測試. 在這樣一個數據中. 我們發現,如果使用自帶的 Distinct ,發現得數據依然是一樣,並沒有想象中的去除重覆。 以下,給出幾個解決方案。 第一種: 繼承EqualityComparer 我們新建一個類。如下。且必須重寫父類中的抽象方法。Equals和Ge ...
  • 一、背景 這幾天在維護公司的一個項目,嗯…到現在七八年沒人動過了(也是老古董了),都說N年前的代碼碰不得 處處是坑 不能挖坑還得一步一步的填坑,恰好今天就填了一坑 此處作為記錄 供以後翻閱,對代碼除了有些看不懂或者說是很凌亂之外,其他都還行(沒註釋、有註釋的地方是自動生成的英文註釋…..、包含 各種 ...
  • 組織者組織組織者組織組織者組織組織者組織組織者組織組織者組織組織者組織組織者組織組織者組織 組織者組織組織者組織組織者組織組織者組織組織者組織組織者組織組織者組織組織者組織組織者組織組織者組織 組織者組織組織者組織組織者組織組織者組織組織者組織組織者組織 組織者組織組織者組織組織者組織組織者組織組織 ...
  • 本文只是基礎代碼片段,直接先寫 結論: C# DateTime 時間相減 —— 和 時區無關,只和時間值有關。 運行結果: 測試代碼: 於是 就有了 這樣的問題 (如下問題 僅作思考): > 最開始, 項目是 國內項目 —— 從不考慮時區, 統一用的 +8時間, 存入資料庫的 也是 +8時間. > ...
  • 在一些比較重要的業務系統中,通常會要求系統跟蹤數據記錄的變動情況。系統要記錄什麼時間,什麼人,對那些信息進行了變動。 比較簡單的實現方式是在每個表中加入兩個欄位CreatedBy和CreatedAt,見圖1。CreatedBy用來存是誰進行了這次更改。CreatedAt用來存什麼時間進行了這次更改。 ...
  • 一、要求 二、思路 1.購物類buy 接收 信用卡類 的信用卡可用可用餘額, 返回消費金額 2.信用卡(ATM)類 接收上次操作後,信用卡可用餘額,總欠款,剩餘欠款,存款 其中: 1.每種交易類型不單獨處理金錢,也不單獨記錄流水賬,每種交易類型調用處理金錢的函數(傳入交易類型,交易金額) 2.處理金 ...
  • 今天用Python提取了Linux內核源代碼的目錄樹結構,沒有怎麼寫過腳本程式,我居然折騰了2個小時,先是如何枚舉出給定目錄下的所有文件和文件夾,os.walk可以實現列舉,但是os.walk是只給出目錄名和文件名,而沒有絕對路徑。使用os.path.listdir可以達到這個目的,然後是創建目錄, ...
  • 上一文只研究了JImage類,今天繼續其他常用的joomla內置類,個人是從常用角度來寫的,如果PHP本身函數比起Joomla內置類用起來更方便的,我就濾過不說,如果你實在想用,自己去查吧,個人覺得,無論多優秀的方法,如果PHP自帶函數也能很好解決,那麼用PHP內置函數是最好的,學習PHP首先就是學 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...