التكرارات: غراء المحكمة الخاصة بلبنان
فهم فئات التكرارات، بدءًا من مكررات الإدخال إلى التكرارات المتجاورة. تعلم حساب المكرر، واتفاقية البداية/النهاية، وكيفية ربط المكررات بالحاويات بالخوارزميات.
تجريد التكرار
التكرارات هي الغراء بين حاويات STL والخوارزميات. بدلاً من كتابة خوارزميات N للحاويات M (تطبيقات N*M)، تحدد STL واجهة مكرر مشتركة. توفر كل حاوية مكررات، وتعمل كل خوارزمية على نطاقات مكررة - مما يوفر تطبيقات N+M تعمل معًا.
المكرر هو كائن:
1. يشير إلى عنصر في التسلسل
2. يمكن إلغاء الإشارة إليه ** (*it) للوصول إلى العنصر
3. يمكن زيادة (++it) للانتقال إلى العنصر التالي
4. يمكن المقارنة (it != end) لاكتشاف نهاية التسلسل
تدعم فئات التكرارات المختلفة مجموعات مختلفة من العمليات، وتشكل تسلسلًا هرميًا من الأضعف إلى الأقوى.
فئات التكرار
تشكل فئات مكرر C++ تسلسلاً هرميًا (يتضمن كل مستوى إمكانيات جميع المستويات أدناه):
| الفئة | العمليات | حاويات المثال |
|---|---|---|
| الإدخال | القراءة، تمرير واحد للأمام (++،*،==) |istream_iterator|
| الإخراج | الكتابة، تمرير واحد للأمام (++،*=) |ostream_iterator،back_inserter|
| إلى الأمام | القراءة/الكتابة، التمرير المتعدد للأمام |forward_list،unordered_set|
| ثنائي الاتجاه | للأمام + للخلف (--) |list،set،map|
| الوصول العشوائي | ثنائي الاتجاه + القفزات (+n،-n،[]،<) |deque|
| متجاورة (C++17) | الوصول العشوائي + العناصر متجاورة في الذاكرة |vector،array،string|
تعلن الخوارزميات عن الفئة الحد الأدنى التي تتطلبها. يحتاجstd::sortإلى مكررات وصول عشوائي، لذا فهو يعمل معvectorولكن ليس معlist(الذي يوفر وظيفة عضوsort()الخاصة به).
اتفاقية البداية/النهاية
توفر كل حاوية STLbegin()وend()- نطاق[begin, end)نصف مفتوح حيث يشيرendإلى العنصر الأخير. تعمل الوظائف المجانيةstd::begin()وstd::end()مع الحاويات ومصفوفات C وأي نوع يوفر الأعضاء.begin()و.end().
#include <vector>
#include <list>
#include <algorithm>
#include <iostream>
#include <iterator>
int main() {
std::vector<int> vec = {5, 3, 1, 4, 2};
// Member functions
auto first = vec.begin(); // points to 5
auto last = vec.end(); // points past 2
// Free functions — preferred for generic code
auto first2 = std::begin(vec);
auto last2 = std::end(vec);
// Works with C-arrays too
int arr[] = {10, 20, 30};
std::sort(std::begin(arr), std::end(arr));
// const iterators — read-only access
auto cit = vec.cbegin(); // const_iterator
// *cit = 99; // ERROR: cannot modify through const_iterator
// Reverse iterators — iterate backward
for (auto rit = vec.rbegin(); rit != vec.rend(); ++rit) {
std::cout << *rit << ' '; // 2, 4, 1, 3, 5 (reversed, pre-sort was applied)
}
std::cout << '\n';
}أدوات التكرار: التالي، السابق، المتقدم، المسافة
يوفر رأسوظائف مساعدة تعمل عبر جميع فئات التكرار، مما يؤدي إلى اختيار التنفيذ الأكثر كفاءة تلقائيًا.
#include <iterator>
#include <vector>
#include <list>
#include <iostream>
int main() {
std::vector<int> vec = {10, 20, 30, 40, 50};
// std::next — returns iterator n positions forward (default 1)
auto it = std::next(vec.begin(), 2); // points to 30
std::cout << *it << '\n'; // 30
// std::prev — returns iterator n positions backward
auto it2 = std::prev(vec.end()); // points to 50
std::cout << *it2 << '\n'; // 50
// std::advance — modifies iterator in place (no return)
auto it3 = vec.begin();
std::advance(it3, 3); // now points to 40
std::cout << *it3 << '\n'; // 40
// std::distance — number of increments from first to last
auto d = std::distance(vec.begin(), vec.end()); // 5
std::cout << "Distance: " << d << '\n';
// These work with non-random-access iterators too
std::list<int> lst = {1, 2, 3, 4, 5};
auto lit = std::next(lst.begin(), 2); // O(n) for list, O(1) for vector
std::cout << *lit << '\n'; // 3
auto ld = std::distance(lst.begin(), lst.end()); // O(n) for list
std::cout << "List distance: " << ld << '\n'; // 5
}قم بإدراج التكرارات وتكرارات الدفق
تسمح مُكرِّرات الإدخال للخوارزميات بـ الإدراج في الحاويات بدلاً من الكتابة فوق العناصر الموجودة. تقوم مكررات الدفق بتكييف تدفقات الإدخال / الإخراج مع واجهة التكرار.
#include <iterator>
#include <vector>
#include <algorithm>
#include <iostream>
#include <sstream>
int main() {
std::vector<int> src = {1, 2, 3, 4, 5};
std::vector<int> dst;
// back_inserter — calls push_back on each assignment
std::copy(src.begin(), src.end(), std::back_inserter(dst));
// dst: {1, 2, 3, 4, 5}
// front_inserter — calls push_front (works with deque, list)
// inserter — calls insert at a given position
// ostream_iterator — write to an output stream
std::copy(dst.begin(), dst.end(),
std::ostream_iterator<int>(std::cout, ", "));
std::cout << '\n'; // 1, 2, 3, 4, 5,
// istream_iterator — read from an input stream
std::istringstream iss("10 20 30 40");
std::vector<int> from_stream(
std::istream_iterator<int>(iss),
std::istream_iterator<int>() // end-of-stream sentinel
);
// from_stream: {10, 20, 30, 40}
}تعد زيادة المكرر بعدend()أو تقليله بعدbegin()سلوكًا غير محدد. الأمر نفسه ينطبق على إلغاء مرجعيةend()- فهو عنصر حارس وليس عنصرًا صالحًا.
من الأخطاء الشائعة كتابةit + 1دون التحقق مما إذا كانitموجودًا بالفعل في النهاية أو بالقرب منها:
auto it = vec.end();
++it; // UB — past-the-end
*it; // UB — dereferencing end()Always check iterator validity before operations. Use std::next and std::prev مع عمليات التحقق من المسافة، أو استخدم حلقات for المستندة إلى النطاق والتي تتعامل مع الحدود تلقائيًا.
When writing generic functions that operate on sequences:
1. Accept iterator pairs (or ranges in C++20) rather than specific containers
2. Usetypenameorautofor the iterator type
3. Usestd::distanceinstead of subtraction (which requires random access)
4. Usestd::next/std::previnstead of+/-arithmetic
5. In C++20, prefer concepts to constrain iterator requirements:
template<std::forward_iterator Iter>
auto my_algorithm(Iter first, Iter last) { ... }This ensures your code works with the widest range of containers.
- Iterators decouple containers from algorithms — the STL's core design principle
- توجد ست فئات للمكرر: الإدخال، الإخراج، الأمام، ثنائي الاتجاه، الوصول العشوائي، المتجاورة
- Use free functions
std::begin()/std::end()for generic code that works with containers and C-arrays - تعمل
std::nextوstd::prevوstd::advanceوstd::distanceعبر جميع فئات التكرارات - Insert iterators (
back_inserter,front_inserter,inserter) let algorithms grow containers - لا تقم أبدًا بإلغاء الإشارة إلى
end()أو الزيادة بعده - فكلاهما سلوك غير محدد
اختبار — اختبر معرفتك
(15 نقطة خبرة)1. ما هي فئة التكرار التي يتطلبها`std::sort`؟
2. ماذا ينشئ`std::back_inserter(vec)`؟
3. ما هو التعقيد الزمني لـ`std::distance(first, last)`على`std::list`؟