網站首頁 編程語言 正文
1. 迭代器簡介
為了提高C++編程的效率,STL(Standard Template Library)中提供了許多容器,包括vector、list、map、set等。然而有些容器(vector)可以通過下標索引的方式訪問容器里面的數據,但是大部分的容器(list、map、set)不能使用這種方式訪問容器中的元素。為了統一訪問不同容器時的訪問方式,STL為每種容器在實現的時候設計了一個內嵌的iterator類,不同的容器有自己專屬的迭代器(專屬迭代器負責實現對應容器訪問元素的具體細節),使用迭代器來訪問容器中的數據。除此之外,通過迭代器可以將容器和通用算法結合在一起,只要給予算法不同的迭代器,就可以對不同容器執行相同的操作,例如find查找函數(因為迭代器提供了統一的訪問方式,這是使用迭代器帶來的好處)。迭代器對一些基本操作如*、->、++、==、!=、=進行了重載,使其具有了遍歷復雜數據結構的能力,其遍歷機制取決于所遍歷的容器,所有迭代器的使用和指針的使用非常相似。通過begin,end函數獲取容器的頭部和尾部迭代器,end迭代器不包含在容器之內,當begin和end返回的迭代器相同時表示容器為空。
STL主要由?容器、迭代器、算法、函數對象、和內存分配器?五大部分構成。
2. 迭代器的實現原理
首先,看看STL中迭代器的實現思路:
從上圖中可以看出,STL通過類型別名的方式實現了對外統一;在不同的容器中類型別名的真實迭代器類型是不一樣的,而且真實迭代器類型對于++、--、*、->等基本操作的實現方式也是不同的。(PS:迭代器很好地詮釋了接口與實現分離的意義)
既然我們已經知道了迭代器的實現思路,現在如果讓我們自己設計一個list容器的簡單迭代器,應該如何實現呢?
1.list類需要有操作迭代器的方法
1.begin/end
2.insert/erase/emplace
2.list類有一個內部類list_iterator
1.有一個成員變量ptr指向list容器中的某個元素
2.iterator負責重載++、--、*、->等基本操作
3.list類定義內部類list_iterator的類型別名
以上就是實現一個list容器的簡單迭代器需要考慮的具體細節。
3. 迭代器的簡單實現
my_list.h(重要部分有注釋說明)
// // Created by wengle on 2020-03-14. // #ifndef CPP_PRIMER_MY_LIST_H #define CPP_PRIMER_MY_LIST_H #include <iostream> template<typename T> class node { public: T value; node *next; node() : next(nullptr) {} node(T val, node *p = nullptr) : value(val), next(p) {} }; template<typename T> class my_list { private: node<T> *head; node<T> *tail; int size; private: class list_iterator { private: node<T> *ptr; //指向list容器中的某個元素的指針 public: list_iterator(node<T> *p = nullptr) : ptr(p) {} //重載++、--、*、->等基本操作 //返回引用,方便通過*it來修改對象 T &operator*() const { return ptr->value; } node<T> *operator->() const { return ptr; } list_iterator &operator++() { ptr = ptr->next; return *this; } list_iterator operator++(int) { node<T> *tmp = ptr; // this 是指向list_iterator的常量指針,因此*this就是list_iterator對象,前置++已經被重載過 ++(*this); return list_iterator(tmp); } bool operator==(const list_iterator &t) const { return t.ptr == this->ptr; } bool operator!=(const list_iterator &t) const { return t.ptr != this->ptr; } }; public: typedef list_iterator iterator; //類型別名 my_list() { head = nullptr; tail = nullptr; size = 0; } //從鏈表尾部插入元素 void push_back(const T &value) { if (head == nullptr) { head = new node<T>(value); tail = head; } else { tail->next = new node<T>(value); tail = tail->next; } size++; } //打印鏈表元素 void print(std::ostream &os = std::cout) const { for (node<T> *ptr = head; ptr != tail->next; ptr = ptr->next) os << ptr->value << std::endl; } public: //操作迭代器的方法 //返回鏈表頭部指針 iterator begin() const { return list_iterator(head); } //返回鏈表尾部指針 iterator end() const { return list_iterator(tail->next); } //其它成員函數 insert/erase/emplace }; #endif //CPP_PRIMER_MY_LIST_H
test.cpp
//// Created by wengle on 2020-03-14.//#include <string>#include "my_list.h"struct student { std::string name; int age; student(std::string n, int a) : name(n), age(a) {} //重載輸出操作符 friend std::ostream &operator<<(std::ostream &os, const student &stu) { os << stu.name << " " << stu.age; return os; }};int main() { my_list<student> l; l.push_back(student("bob", 1)); //臨時量作為實參傳遞給push_back方法 l.push_back(student("allen", 2)); l.push_back(student("anna", 3)); l.print(); for (my_list<student>::iterator it = l.begin(); it != l.end(); it++) { std::cout << *it << std::endl; *it = student("wengle", 18); } return 0;}// // Created by wengle on 2020-03-14. // #include <string> #include "my_list.h" struct student { std::string name; int age; student(std::string n, int a) : name(n), age(a) {} //重載輸出操作符 friend std::ostream &operator<<(std::ostream &os, const student &stu) { os << stu.name << " " << stu.age; return os; } }; int main() { my_list<student> l; l.push_back(student("bob", 1)); //臨時量作為實參傳遞給push_back方法 l.push_back(student("allen", 2)); l.push_back(student("anna", 3)); l.print(); for (my_list<student>::iterator it = l.begin(); it != l.end(); it++) { std::cout << *it << std::endl; *it = student("wengle", 18); } return 0; }
4. 迭代器失效
// inserting into a vector #include <iostream> #include <vector> int main () { std::vector<int> myvector (3,100); std::vector<int>::iterator it; it = myvector.begin(); it = myvector.insert ( it , 200 ); myvector.insert (it,200,300); //it = myvector.insert (it,200,300); myvector.insert (it,5,500); //當程序執行到這里時,大概率會crash for (std::vector<int>::iterator it2=myvector.begin(); it2<myvector.end(); it2++) std::cout << ' ' << *it2; std::cout << '\n'; return 0; }
上面的代碼很好地展示了什么是迭代器失效?迭代器失效會導致什么樣的問題?
當執行完myvector.insert (it,200,300);
這條語句后,實際上myvector已經申請了一塊新的內存空間來存放之前已保存的數據和本次要插入的數據,由于it
迭代器內部的指針還是指向舊內存空間的元素,一旦舊內存空間被釋放,當執行myvector.insert (it,5,500);
時就會crash(PS:因為你通過iterator的指針正在操作一塊已經被釋放的內存,大多數情況下都會crash)。迭代器失效就是指:迭代器內部的指針沒有及時更新,依然指向舊內存空間的元素。
上圖展示了STL源碼中vector容器insert
方法的實現方式。當插入的元素個數超過當前容器的剩余容量時,就會導致迭代器失效。這也是測試代碼中myvector.insert (it,200,300);
插入200個元素的原因,為了模擬超過當前容器剩余容量的場景,如果你的測試環境沒有crash,可以將插入元素設置的更多一些。
5. 參考資料
原文鏈接:https://blog.csdn.net/jinchengzhou/article/details/122474498
- 上一篇:C++中的拷貝構造詳解_C 語言
- 下一篇:C++初階學習之模板進階_C 語言
相關推薦
- 2024-03-18 為什么SpringBoot的jar可以直接運行?
- 2022-01-27 laravel JWTAuth對api接口權限進行鑒權
- 2022-09-16 C++如何實現BitMap數據結構_C 語言
- 2022-03-23 C語言?scanf的工作原理詳解_C 語言
- 2022-07-21 Linux上源碼包安裝nginx及yum 安裝nginx
- 2022-10-10 python正則表達式之re.match()與re.search()的用法及區別_python
- 2022-08-04 Python+NumPy繪制常見曲線的方法詳解_python
- 2023-01-18 解決CentOS下ImportError:?No?module?named?'_sqlite3'的問
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支