【OOP】C++自定義類型的迭代器

来源:https://www.cnblogs.com/bljw-02/archive/2022/05/12/16264952.html
-Advertisement-
Play Games

C++自定義迭代器:介紹了【什麼時候需要用到自定義迭代器】和【如何實現自定義迭代器】。 ...


目錄


1. 什麼時候需要使用自定義的迭代器?

常見、基本的數組類型

  • 對於常見、基本的數組類型,如:intchar ,我們可以簡單地使用下標來進行遍歷:

    int array[5] = {1,2,3,4,5};
    
    // 方法1:使用下標遍歷
    for (int ind = 0; ind < 5; ++ind) {
        cout << array[ind] << " ";
    }
    

    也可以使用範圍來進行遍歷,達到和使用下標同樣的遍歷效果:

    // 方法2:使用範圍遍歷
    for (int &n: array) {
        cout << n << " ";
    }
    

回到頂部

STL 容器

  • 對於 STL 容器來說,如:vectorlist ,我們同樣也可以使用下標和範圍來進行遍歷:

    vector<int> vec = {1,2,3,4,5};
    
    // 方法1:使用下標遍歷
    for (int ind = 0; ind < vec.size(); ++ind) {
        cout << vec[ind] << " ";
    }
    
    // 方法2:使用範圍遍歷
    for (int &n: vec) {
        cout << n << " ";
    }
    

    除了以上兩種方法,STL 容器還可以使用迭代器(iterator)進行遍歷:

    vector<int>::iterator iter;
    
    // 方法3:使用迭代器遍歷
    // .begin() 是指向 vector 首元素的位置的迭代器
    // .end() 是指向 vector 最後一個元素的位置的下一個位置的迭代器
    for (iter = vec.begin(); iter != vec.end(); ++iter) {
        cout << *iter << " ";
    }
    

回到頂部

自定義數據類型

  • 下麵給出一個簡單的自定義類:

    class Group {
    private:
    	vector<vector<int>> students_marks;
        ......
    }
    

    其中,students_marks 是一個用來記錄學生若幹次的分數的 vector,students_marks[0] 也是一個 vector,用來記錄第一個學生的分數,其中 students_marks[0][0] 表示第一個學生的第一個分數。

  • 如果要對 students_marks 進行遍歷,由於不存在 vector<int> 類型的迭代器,因此方法2和方法3無法使用,只能使用下標遍曆法:

    // 定義一個函數,用來輸出 vector 中所有元素
    void printvec(vector<int> &vec) {
        for(int ind = 0; ind < vec.size(); ++ind) {
            cout << vec[ind] << " ";
        }
    }
    
    // 使用下標對 vector<int> 類型的元素進行遍歷
    for (int ind = 0; ind < students_marks.size(); ++ind) {
        printvec(students_marks[ind]);
    }
    
  • 如果要使用以下方法對 students_marks 進行遍歷:

    // 定義 Group 類型的 G
    Group G;
    
    // 使用範圍遍歷
    for(auto v: G) {
        printvec(v);
    }
    

    則需要自己手動編寫指向 vector<int> 數據類型的迭代器。

回到頂部


2. 開始編寫自定義迭代器之前需要思考的問題

  1. 迭代器進行遍歷的對象是什麼?
  2. 迭代器遍歷的範圍是什麼?
  3. 迭代器指向的數據是什麼類型?

只要想明白這三個問題,就不難實現自定義類型的迭代器。

以上面的類 Group 為例,

我們想遍歷的是 students_marks ,將每個學生的分數輸出。

對應問題1,迭代器遍歷的對象是 vector<vector<int>> students_marks

對應問題2,假設總共有 k 個學生,則 students_marks.size() = k ,遍歷的範圍是從 students_marks[0]students_marks[k-1]

對應問題3,迭代器指向的數據是 students_marks[x] ,類型是 vector<int>

回到頂部


3. 如何編寫及實現自定義類型的迭代器?

在後面的步驟中,一些常用的名詞將會以以下標識符代稱:

代替的標識符 對應上面的例子是
遍歷的對象 OBJECT students_marks
遍歷對象的數據類型 OBJECT_type vector<vector<int>>
遍歷對象所處類 OBJECT_class Group
迭代器指向的數據 VALUE students_marks[x]
迭代器指向的數據類型 VALUE_type vector<int>

回到頂部

  1. object 所處的 class 中定義一個迭代器(iterator)的結構體(struct):

    class OBJECT_class {
    private:
    	OBJECT_type OBJECT;
        
    public:
        struct Iterator {
            
        }
        
        ......
    }
    

  2. 設定迭代器的屬性:

    struct Iterator {
        using iterator_category = std::forward_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = VALUE_type;
        using reference = const VALUE_type&;
        using pointer = VALUE_type*;
    }
    
    • iterator_category 是迭代器的類型,如果是正常、基礎的情況下,選擇 forward_iterator_tag 就可以了
    • difference_type 選擇 ptrdiff_t
    • 其餘的 value_typereferencepointer 都是自定義的,根據實際情況填寫

  3. 定義迭代器的成員變數、構造函數、基礎函數

    struct Iterator {
        using iterator_category = std::forward_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = VALUE_type;
        using reference = const VALUE_type&;
        using pointer = VALUE_type*;
        
        // 構造函數
        Iterator(pointer p) :ptr(p) {}
        
        // 拷貝賦值函數
        Iterator& operator=(const Iterator& it) {
            ptr = it.ptr;
        }
        
        // 等於運算符
        bool operator==(const Iterator& it) const {
            return ptr == it.ptr;
        }
        
        // 不等於運算符
        bool operator!=(const Iterator& it) const {
            return ptr != it.ptr;
        }
        
        // 首碼自加
        Iterator& operator++() {
            ptr++;
            return *this;
        }
        
        // 尾碼自加
        Iterator operator ++(int) {
            Iterator tmp = *this;
            ++(*this);
            return tmp;
        }
        
        // 首碼自減
        Iterator& operator--() {
            ptr--;
            return *this;
        }
        
        // 尾碼自減
        Iterator operator --(int) {
            Iterator tmp = *this;
            --(*this);
            return tmp;
        }
        
        // 取值運算
        VALUE_type& operator*() {
            return *ptr;
        }
        
    private:
        // 定義一個指針
        pointer ptr; 
    }
    
    • 以上的函數不是必要也不是完整的,如果不需要用到的可以不寫,額外需要用到的函數可以自行編寫。
    • 但是在大多數情況下,上面這些函數基本上足矣。

  4. 設定遍歷的範圍:

    class OBJECT_class {
    private:
    	OBJECT_type OBJECT;
        
    public:
        struct Iterator {
            
            ......
            ......
                
        private:
            pointer ptr;
            
        }
        
        // 遍歷的第一個元素的位置
        Iterator begin() {
            VALUE_type* head = &OBJECT[0];
            return Iterator(head);
        }
        
        // 遍歷的最後一個元素的下一個位置
        Iterator end() {
            VALUE_type* head = &OBJECT[0];
            return Iterator(head + OBJECT.size());
        }
        
        ......
    }
    
    • head 是指向第一個 VALUE 的指針

回到頂部


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

-Advertisement-
Play Games
更多相關文章
  • 一、引言 SQL Server有一些很好用的功能,只不過由於個人原因沒用過或者不記得怎麼用,導致需要花點時間用其它方式來實現。 二、好用小知識 2.1、FORMAT函數 1)時間格式化,如將當前日期格式化為2022-05-12: SELECT FORMAT(GETDATE(),'yyyy-MM-dd ...
  • 一、引言 A表數據同步至B表的場景很常見,比如一個公司有總部及分廠,它們使用相同的系統,只是賬套不同。此時,一些基礎數據如物料信息,只需要總部錄入即可,然後間隔一定時間同步至分廠,避免了重覆工作。 二、測試數據 CREATE TABLE StudentA ( ID VARCHAR(32), Name ...
  • 本文介紹什麼是 SQL 的聚集函數,如何利用它們彙總表的數據。這些函數很高效,它們返回結果一般比你在自己的客戶端應用程式中計算要快得多。 一、聚集函數 我們經常需要彙總數據而不用把它們實際檢索出來,為此 SQL 提供了專門的函數。使用這些函數,SQL 查詢可用於檢索數據,以便分析和報表生成。這種類型 ...
  • 本文介紹什麼是函數,DBMS 支持何種函數,以及如何使用這些函數;還將講解為什麼 SQL 函數的使用可能會帶來問題。 一、函數 與大多數其他電腦語言一樣,SQL 也可以用函數來處理數據。函數一般是在數據上執行的,為數據的轉換和處理提供了方便。 SQL 如何創建計算欄位 中用來去掉字元串尾的空格的 ...
  • 本文介紹什麼是計算欄位,如何創建計算欄位,我們用例子說明瞭計算欄位在字元串拼接和算術計算中的用途。以及如何從應用程式中使用別名引用它們。 一、計算欄位 存儲在資料庫表中的數據一般不是應用程式所需要的格式,下麵舉幾個例子。 需要顯示公司名,同時還需要顯示公司的地址,但這兩個信息存儲在不同的表列中。 城 ...
  • 轉載請標明出處,維權必究: https://www.cnblogs.com/tangZH/p/12543154.html Glide作為一個強大的圖片載入框架,已經被android官方使用,所以,明白Glide的載入流程以及原理對加深我們對glide的理解是很重要的。 本文基於glide 4.11 ...
  • 在 OpenHarmony 生態發展過程中,涌現了大批優秀的代碼貢獻者,本專題旨在表彰貢獻、分享經驗,文中內容來自嘉賓訪談,不代表 OpenHarmony 工作委員會觀點。 ...
  • Vue3使用插槽時的父子組件傳值 用法見官方文檔深入組件章節,插槽部分: 參考文檔:插槽-作用域插槽-插槽prop 作用域插槽 有時讓插槽內容能夠訪問子組件中才有的數據是很有用的。 需求:插槽內容能夠訪問子組件中才有的數據 實現 子組件 TodoList.vue <template> <div v- ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...