69 / 90

C++ Iterators

C++ iteratorlari

Iterator - elementning konteynerdagi o'rnini ifodalovchi ko'rsatgichga o'xshash ob'ekt. U konteynerdagi elementlarni takrorlash uchun ishlatiladi.

Faraz qilaylik, bizda 4 o'lchamli sonlar nomli vektor bor. Keyin begin() va end() a'zo funksiyalar bo'lib, mos ravishda vektorning boshi va oxiriga ishora qiluvchi iteratorlarni qaytaradi.

-nums.begin() vektordagi birinchi elementga ishora qiladi, ya'ni 0-indeks -nums.begin() + i i indeksdagi elementga ishora qiladi. -nums.end() vektordagi oxirgi elementdan o'tgan bir elementga ishora qiladi

Vector Iterators

begin() and end() iterators identify the beginning and the end of the container.

[!NOTE] C++ STL konteyneri STL algoritmlarini ishlatiladigan konteyner turidan mustaqil ravishda qo'llashi uchun iteratorlarni taqdim etadi.

Iteratorlarni aniqlash

Biz iteratorni quyidagi sintaksis yordamida aniqlashimiz mumkin:

example.cpp
// vektor iteratorini yarating
vector<int>::iterator vec_itr;

// xarita iteratorini yarating
map<char, int>::iterator map_itr;

Bu erda biz vec_itr va map_itr iteratorlarini vektor yoki xaritadagi elementlar bo'ylab harakatlanish uchun yaratdik.

Har doim esda tutingki, biz iteratorlardan ma'lumotlar turlariga mos kelmaydigan konteynerlar bilan foydalana olmaymiz. Masalan,

example.cpp
// butun sonli vektor yaratish
vector<int> num {1, 2, 3};

// Xato: itr faqat butun vektorlar bilan ishlatilishi mumkin
vector<double>::iterator itr = num.begin();

[!NOTE] Initsializatsiya paytida iterator turini aniqlash uchun auto kalit so'zidan (C++ 11 dan keyin) foydalanishimiz mumkin. Masalan, vector<string>::iterator itr = languages.begin(); quyidagicha yozilishi mumkin: auto itr = languages.begin();

1-misol: C++ iteratorlari

example.cpp
#include <iostream>
#include<vector>
using namespace std;

int main() {

    vector <string> languages = {"Python", "C++", "Java"};

    // satr vektoriga iterator yaratish
    vector<string>::iterator itr;

    // barcha elementlarni takrorlash
    for (itr = languages.begin(); itr != languages.end(); itr++) {
        cout << *itr << ", ";
    }

    return 0;
}

Chiqish

kod
Python, C++, Java,

Yuqoridagi misolda tillar nomli vektorni takrorlash uchun itr iteratoridan foydalanganmiz.

Bu yerga,

-itr = languages.begin() - itr ga birinchi vektor elementiga ishora qiluvchi iteratorni tayinlaydi

-itr != languages.end() - itr vektorning oxiriga yetganligini tekshiradi

-itr++ - uni keyingi pozitsiyaga oshiradi

-*itr - vektor elementni itr pozitsiyasiga qaytaradi

Iteratorning asosiy operatsiyalari

Iteratorlarda bajarilishi mumkin bo'lgan ba'zi fundamental operatsiyalar quyidagi jadvalda ko'rsatilgan.

Amaliyotlar tavsifi *itr elementni joriy holatiga qaytaradi itr->m iterator tomonidan ko‘rsatilgan ob’ektning m a’zo qiymatini qaytaradi va (*itr).m ga ekvivalentdir ++itr iteratorni keyingi holatga o‘tkazadi -–itr iteratorni oldingi holatga o‘tkazadi itr + i iteratorni XCODE67X pozitsiyasiga ko‘chiradi, agar i nuqtalari XCO8 pozitsiyasi bo‘yicha qaytarilsa. iteratorlar bir xil itr1 != itr2 , agar iteratorlar tomonidan ko'rsatilgan pozitsiyalar bir xil bo'lmasa, true ni qaytaradi itr = itr1 itr1 tomonidan ko'rsatilgan pozitsiyani itr iteratoriga belgilaydi

[!NOTE] Yuqorida sanab o'tilgan barcha operatsiyalarni iteratorlarning barcha turlarida bajarish mumkin emas. Biz iteratorlarning har xil turlari bilan tanishganimizdan keyin bu haqda batafsilroq gaplashamiz.

Iterator turlari

C++ standart shablon kutubxonasi besh turdagi iteratorlarni taqdim etadi. Ular:

  • Kirish iteratori
  • Chiqish iteratori
  • Oldinga iterator
  • Ikki tomonlama iterator
  • Tasodifiy kirish iteratori

C++ Iterator Types

Input Iterators, Output Iterators, Forward Iterators, Bidirectional Iterators, and Random Access Iterators are the five types of C++ iterators.

Yuqoridagi diagrammada ko'rsatilganidek, tasodifiy kirish iteratori ham qo'shimcha funksiyalarga ega ikki tomonlama iteratordir.

Xuddi shunday, ikki yo'nalishli iteratorlar ham qo'shimcha funksiyalarga ega bo'lgan oldinga iteratorlar va hokazo.

Kirish iteratori

C++ kiritish iteratorlari oldinga iteratsiya paytida ba'zi qiymatlarni o'qish/qayta ishlash imkoniyatiga ega.

Biz ++ yordamida oldinga takrorlashimiz va * yordamida qiymatlarni yoki -> yordamida a'zo qiymatlarini o'qishimiz mumkin.

Kirish oqimidan qiymatlarni o'qiydigan iterator kirish iteratoriga misoldir.

U quyidagicha yaratilishi mumkin:

example.cpp
// cin dan qiymatlarni o'qish uchun kirish iteratorini yarating
istream_iterator<int> input_itr(cin);

Bu yerda biz cin standart kirish oqimidan kirish qiymatlarini o‘qiydigan int tipidagi input_itr nomli kiritish iteratorini yaratdik.

[!NOTE] Kirish va chiqish iteratorlari C++ STL iteratorlari tarkibiga kiradi va <iterator> sarlavha faylida aniqlanadi.

Chiqish iteratori

C++ chiqish iteratorlari oldinga iteratsiya paytida ba'zi qiymatlarni yozishga qodir.

Biz ++ yordamida oldinga takrorlashimiz va * yordamida qiymatlarni yozishimiz mumkin. = operatori qiymatlarni yozish uchun ishlatilishi mumkin

Chiqish oqimiga qiymatlarni yozuvchi iterator chiqish iteratoriga misoldir.

example.cpp
// konsolga butun sonlarni yozish uchun chiqish iteratorini yarating
ostream_iterator<int> output_itr(cout, " ");

Bu yerda biz cout standart kirish oqimiga qiymatlarni yozuvchi int tipidagi output_itr nomli chiqish iteratorini yaratdik.

Oldinga iterator

C++ Forward Iteratorlari oldinga siljish paytida ba'zi qiymatlarni o'qish/yozish imkoniyatiga ega.

forward_list sinfining iteratorlari oldinga iteratorlardir.

Biz ++ yordamida oldinga takrorlashimiz va * yordamida qiymatlarni o'qish va yozishimiz yoki -> yordamida a'zo qiymatlarini o'qish va yozishimiz mumkin.

Masalan,

example.cpp
#include <iostream>
#include <forward_list>
using namespace std;

int main() {

    forward_list<int> nums{1, 2, 3, 4};

    // nuqta uchun iteratorni ishga tushiring
    // oldinga siljish ro'yxatining boshiga
    forward_list<int>::iterator itr = nums.begin();

    while (itr != nums.end()) {
        // bilvosita operator yordamida iterator qiymatiga kirish
        int original_value = *itr;

        // bilvosita operator yordamida yangi qiymat tayinlash
        *itr = original_value * 2;

        // iteratorni keyingi pozitsiyaga yo'naltiring
        itr++;
    }

    // raqamlar tarkibini ko'rsatish
    for (int num: nums) {
        cout << num << ", ";
    }

    return 0;
}

Chiqish

kod
2, 4, 6, 8,

Yuqoridagi misolda biz oldingi ro'yxatning boshiga ishora qilish uchun raqamlar ro'yxatini va oldingi iteratorni yaratdik.

Keyin biz raqamlar tarkibiga kirish va o'zgartirish uchun itr dan foydalandik.

Nihoyat, biz o'zgartirilgan oldinga ro'yxat elementlarini ko'rsatish uchun ranged for loop dan foydalandik.

Ikki tomonlama iterator

C++ Ikki tomonlama iteratorlar ham oldinga, ham orqaga takrorlash imkoniyatiga ega.

Biz ++ yordamida oldinga, -- yordamida orqaga, * yordamida qiymatlarni o'qish va yozishimiz yoki -> yordamida a'zo qiymatlarini o'qish va yozishimiz mumkin.

list , set , multiset , map va multimap konteyner sinflarining iteratorlari ikki tomonlama iteratorlardir.

example.cpp
#include <iostream>
#include <list>
using namespace std;

int main() {

    list<int> nums {1, 2, 3, 4, 5};

    // raqamlarning boshiga ishora qilish uchun iteratorni ishga tushiring
    list<int>::iterator itr = nums.begin();

    cout << "Moving forward: " << endl;

    // elementlarni oldinga qarab ko'rsatish
    while (itr != nums.end()) {
        cout << *itr  << ", ";

        // iteratorni 1 pozitsiyaga oldinga siljiting
        itr++;
    }

    cout << endl << "Moving backward: " << endl;

    // elementlarni orqaga qarab ko'rsatish
    while (itr != nums.begin()) {
        if (itr != nums.end()) {
            cout << *itr << ", ";
        }

        // iteratorni 1 pozitsiyaga orqaga suring
        itr--;
    }

    cout << *itr << endl;

    return 0;    
}

Chiqish

kod
Moving forward: 
1, 2, 3, 4, 5, 
Moving backward: 
5, 4, 3, 2, 1

Yuqoridagi misolda biz ro'yxatning boshiga ishora qilish uchun raqamlar ro'yxatini va ikki yo'nalishli iteratorni yaratdik.

Keyin biz uni oldinga va orqaga qarab raqamlar tarkibiga kirish va ko'rsatish uchun ishlatdik.

Tasodifiy kirish iteratori

C++ tasodifiy kirish iteratorlari tasodifiy kirish bilan birga ikki tomonlama iteratorlarning barcha xususiyatlariga ega.

Tasodifiy kirish iteratorlari uchun ba'zi tez-tez ishlatiladigan operatorlar:

  • ++ - iterate forward
  • -- - iterate backward
  • * or [] - read and write values
  • -> - access member values
  • + - iterate forward by desired numbers of steps
  • - - iterate backward by desired numbers of steps

vector , deque , array konteyner sinflarining iteratorlari va satrlarning iteratorlari tasodifiy kirish iteratorlaridir.

Mana bir misol,

example.cpp
#include <iostream>
#include <vector>
using namespace std;

int main() {

    // vektor yarating
    vector<int> vec{1, 2, 3, 4};

    // birinchi va oxirgi elementlarga ishora qilish uchun iteratorlarni yarating
    vector<int>::iterator itr_first = vec.begin();
    vector<int>::iterator itr_last = vec.end() - 1;

    // vektor elementlarini ko'rsatish
    cout << "First Element: " << *itr_first << endl;
    cout << "Second Element: " << itr_first[1] << endl;

    cout << "Second Last Element: " << *(itr_last - 1) << endl;
    cout << "Last Element: " << *(itr_last) << endl;

    return 0;
}

Chiqish

kod
First Element: 1
Second Element: 2
Second Last Element: 3
Last Element: 4

Yuqoridagi misolda vektor vec va ikkita tasodifiy kirish iteratorlarini mos ravishda vektorning boshi va oxiriga ishora qilish uchun itr_first va itr_last yaratdik.

Keyin biz iteratorlardan vec tarkibiga tasodifiy tartibda kirish va ularni ko'rsatish uchun foydalandik.

Iteratorlar tomonidan qo'llab-quvvatlanadigan operatorlar

Yuqorida aytib o'tilganidek, barcha operatorlar iteratorlarning barcha turlariga mos kelmaydi. Turli iteratorlar uchun ishlatilishi mumkin bo'lgan operatorlar quyidagi jadvalda ko'rsatilgan.

Iterator turi qo'llab-quvvatlanadigan operatorlar Kirish iteratori ++ , * , -> , == , != Chiqish iteratori ++ , * , = oldinga iterator ++ , * , XCODECODEX , XCODECO11 , Iterator ++ , -- , * , -> , == , != Tasodifiy kirish iteratori ++ , -- , * , -> , [] , XCODEX , X6CO2 , XCODEX , X6CO2 , , <= , > , >= , == , !=

Nega iteratorlardan foydalanish kerak?

C++ da iteratorlardan foydalanishni xohlashingiz mumkin bo'lgan ba'zi sabablar:

  • Algoritmlarni qo'llab-quvvatlash: C++ standart kutubxonasi std::find() , std::sort() va std::accumulate() kabi iteratorlar bilan ishlatilishi mumkin bo'lgan keng ko'lamli algoritmlarni taqdim etadi. Bu algoritmlar konteynerlarda umumiy amallarni bajarishga imkon beradi.
  • Xotira samaradorligi: Iteratorlar bizga bir vaqtning o'zida butun ma'lumotlar to'plamini yuklamasdan katta ma'lumotlar to'plamlarini bir vaqtning o'zida bir elementni qayta ishlashga imkon beradi, bu esa xotira samaradorligini ta'minlaydi.
  • Muvofiqlik: Iteratorlar har xil turdagi konteynerlar ma'lumotlariga izchil kirish va manipulyatsiya qilish usulini ta'minlaydi. Biz vektorlar, ro'yxatlar, to'plamlar yoki boshqa konteynerlar uchun bir xil iterator sintaksisidan foydalanishimiz mumkin.
  • Kodni soddalashtirish: Iteratorlar konteyner ustida takrorlash tafsilotlarini yashirish orqali kodni soddalashtirishi mumkin, bu esa kodni yanada o'qilishi mumkin.

Tez-tez so'raladigan savollar Iteratorlar va ko'rsatkichlar bir-biriga o'xshashdir, chunki ularning ikkalasi ham ma'lum xotira joylarida saqlangan qiymatlarga kirishadi. Ammo ular quyidagi jadvalda ko'rsatilganidek, boshqa ko'plab yo'llar bilan farqlanadi: Iteratorlar Ko'rsatkichlar Vektorlar, ro'yxatlar va boshqalar kabi konteynerlarni takrorlash uchun ishlatiladi. Xotira joyiga ishora qilish uchun ishlatiladi. Pastki idish o'zgartirilganda bekor qilinishi mumkin. Asosiy ma'lumotlar strukturasi o'zgartirilsa ham o'z kuchida qoladi. std::find() , std::sort() va boshqalar kabi algoritmlar bilan foydalanish mumkin. std::find() , std::sort() va boshqalar kabi algoritmlar bilan ishlatib bo'lmaydi. for diapazonlari bilan foydalanish mumkin. for diapazonlari bilan ishlatib bo'lmaydi. Imkoniyatlariga ko'ra kirish, chiqish, oldinga, ikki yo'nalishli yoki tasodifiy kirish iteratorlari sifatida tasniflanishi mumkin. Ko'rsatkichlar tabiatan tasodifiy kirishdir. Doimiy iterator - bu doimiy qiymatga ishora qiluvchi iterator. Doimiy iteratorning o'zi oshirilishi yoki kamayishi mumkin, lekin u ko'rsatgan elementning qiymatini o'zgartirib bo'lmaydi. U konteyner sinfi tomonidan taqdim etilgan const_iterator turi yordamida yaratilishi mumkin. Masalan, // int tipidagi vektor <int> vec {1, 2, 3} vektorini yaratish; // vektor vektorining boshiga ishora qiluvchi doimiy iterator yaratish<int>::const_iterator itr = vec.cbegin(); begin() o'rniga cbegin() dan foydalanishga e'tibor bering. Buning sababi, cbegin() doimiy iteratorni qaytaradi, begin() esa oddiy iteratorni qaytaradi. begin() Konteynerning birinchi elementiga ishora qiluvchi iteratorni qaytaradi. rbegin() Konteynerning oxirgi elementiga ishora qiluvchi teskari iteratorni qaytaradi. cbegin() Konteynerning birinchi elementiga ishora qiluvchi doimiy iteratorni qaytaradi. end() Konteynerning oxirgi elementidan keyin darhol nazariy elementga ishora qiluvchi iteratorni qaytaradi. rend() Konteynerning birinchi elementidan oldingi nazariy elementga ishora qiluvchi teskari iteratorni qaytaradi. cend() Konteynerning oxirgi elementidan keyin darhol nazariy elementga ishora qiluvchi doimiy iteratorni qaytaradi. Eslatma: Oddiy iteratorlar va teskari iteratorlar bir-biriga mos kelmaydi. Masalan, vec.rbegin() vec.end()- 1 bilan bir xil emas, garchi ular bir xil elementga ishora qilsalar ham. vec.rbegin() == vec.end() - 1; // not defined *(vec.rbegin()) == *(vec.end() -1) // defined and true C++ da advance() , next() va previous() iterator funksiyalari bo'lib, iteratorni konteynerdagi ma'lum bir joyga ko'chirish uchun ishlatiladi. Quyida har birining qisqacha izohi berilgan: advance() - iteratorni belgilangan pozitsiyalar soni bo'yicha oldinga yoki orqaga siljitadi. advance() sintaksisi quyidagicha: std::advance(itr, n); // moves the iterator n positions forward std::advance(itr, -n); // moves the iterator n positions backward next() - joriy iteratordan n pozitsiya uzoqda joylashgan elementga iteratorni qaytaradi. next() sintaksisi quyidagicha: std::next(itr, n); // returns an iterator n positions forward from itr previous() - joriy iteratordan oldin n ta pozitsiya bo'lgan elementga iteratorni qaytaradi. previous() sintaksisi quyidagicha: std::previous(itr, n); // returns an iterator n positions before itr