🎯 مقدمة:
STL ليست مجرد “مكتبة”…
STL هي قلب C++ الحقيقي.
هي مجموعة من:
- حاويات Containers (vector, list, set, map…)
- خوارزميات Algorithms (sort, find, binary_search…)
- هياكل بيانات Data Structures
- Iterators للتنقّل
- Templates لتعميم الأنواع
لو فهمت STL، انت فعليًا فهمت 50% من قوة C++.
في هذا الدرس رح نتوسع في أهم 3 حاويات تستخدمها يوميًا:
- vector
- set
- map
ومش بس شرح… بل:
- طريقة عملها داخليًا
- كيفية استخدامها بكفاءة
- الأخطاء الشائعة
- ومتى تستخدم كل واحدة منها.
خلّينا نبدأ.
──────────────────────────────────────────────
🔥 الجزء الأول: vector — المصفوفة الديناميكية الذكية
❗ ما هو vector؟
هو مصفوفة ديناميكية (Dynamic Array) تعمل كالتالي:
- حجمها يتغير تلقائيًا
- تخزن عناصر متتالية في الذاكرة
- الوصول لعناصرها O(1)
- الإضافة في النهاية O(1) amortized
- الإضافة في الوسط O(n)
- الحذف في النهاية O(1)
- الحذف من الوسط O(n)
✔ تملك خصائص أقوى من new[] بكثير
لأنها تدير الذاكرة تلقائيًا.
✨ إنشاء vector
#include <vector>
using namespace std;
vector<int> v;
✨ إضافة العناصر
v.push_back(10);
v.push_back(20);
v.push_back(30);
✨ الوصول للعناصر
cout << v[0];
cout << v.at(1); // آمن
✨ الحجم والسعة
cout << v.size();
cout << v.capacity();
🔥 شو يعني capacity؟
الـ vector يحجز مساحة أكبر من الحاجة
عشان يقدر يكبر بدون إعادة تخصيص مستمر.
مثلاً:
size = 3
capacity = 4
لما تزيد العناصر، يضاعف capacity.
✨ التكرار باستخدام for-each
for (int x : v) {
cout << x << endl;
}
✨ الحذف
v.pop_back();
✨ المسح الكلّي
v.clear();
✨ التحقق إن كان فارغاً
v.empty();
🔥 مثال عملي كبير:
vector<int> nums;
for(int i = 1; i <= 5; i++)
nums.push_back(i);
for (int x : nums)
cout << x << " ";
🧠 كيف يعمل vector داخليًا؟
- يحجز block متواصل في الذاكرة
- عند امتلاء block:
- ينشئ block أكبر (عادة ×2)
- ينقل العناصر له
- يحذف القديم
لذلك:
الإضافة في النهاية سريعة جدًا
الإضافة في الوسط بطيئة جدًا
⚠️ أخطاء شائعة:
❌ push_back داخل loop مع v.size() المتغير
قد يسبب إعادة تخصيص وتغيير الذاكرة.
❌ الاحتفاظ بمؤشرات لعناصر vector
قد تتحرك الذاكرة في عملية إعادة التخصيص.
──────────────────────────────────────────────
──────────────────────────────────────────────
🔥 الجزء الثاني: set — مجموعة مرتّبة بدون تكرار
❗ ما هو set؟
هي بنية بيانات:
- لا تحتوي عناصر مكررة
- العناصر مرتبة تلقائيًا (Balanced BST)
- الوصول/الحذف/البحث: O(log n)
تستخدم أحسن هياكل البيانات من الناحية النظرية:
Red-Black Tree
✨ إنشاء set
#include <set>
set<int> s;
✨ الإضافة
s.insert(10);
s.insert(3);
s.insert(7);
s.insert(3); // يتم تجاهل التكرار
✨ الحذف
s.erase(7);
✨ التحقق
if (s.count(10))
cout << "موجود";
✨ التكرار
for (int x : s)
cout << x << endl;
النتيجة مرتّبة تلقائيًا.
🔥 خصائص مهمّة:
✔ يمنع التكرار
✔ دائمًا مرتّب
✔ أداء ممتاز O(log n)
💡 متى أستخدم set؟
- لو عندك بيانات بدون تكرار
- لو بدك ترتيب تلقائي
- لو بدك حذف/بحث بكفاءة عالية
⚠️ ملاحظة: unordered_set
لو بدك سرعة أعلى (O(1)):
استخدم:
unordered_set<int> us;
لكن بدون ترتيب.
──────────────────────────────────────────────
──────────────────────────────────────────────
🔥 الجزء الثالث: map — قاموس مفاتيح/قيم (Key-Value)
❗ ما هو map؟
خريطة تستعمل شجرة Red-Black:
- كل عنصر عبارة عن (key → value)
- مفاتيح مرتبة تلقائيًا
- لا يسمح بمفتاح مكرر
- الوصول/الإضافة/الحذف O(log n)
✨ إنشاء map
#include <map>
map<string, int> ages;
✨ الإضافة
ages["Mohammad"] = 22;
ages["Ahmad"] = 19;
✨ الوصول
cout << ages["Mohammad"];
✨ التأكد
if (ages.count("Ahmad"))
cout << "موجود";
✨ التكرار
for (auto p : ages) {
cout << p.first << " - " << p.second << endl;
}
✨ الحذف
ages.erase("Ahmad");
🔥 متى تستخدم map؟
عند حاجتك لـ:
- البحث السريع
- المفتاح يربط بقيمة
- المفاتيح مرتّبة
- لا يوجد تكرار
⚡ unordered_map
لو بدك أداء أعلى (O(1)):
unordered_map<string, int> mp;
لكن بدون ترتيب.
خارطة ممتازة للبرامج الثقيلة.
🧠 كيف تعمل map داخليًا؟
داخلها:
- Red-Black Tree
- كل عنصر Node يحتوي على:
key value left child right child parent color - عملياتها log(n)
مقارنةً بـ vector اللي هو O(n).
──────────────────────────────────────────────
──────────────────────────────────────────────
🔥 أقوى مقارنة شاملة بين vector — set — map
| الخاصية | vector | set | map |
|---|---|---|---|
| الترتيب | لا | نعم | نعم |
| التكرار | مسموح | ممنوع | المفتاح ممنوع |
| الوصول لعناصر | O(1) | O(log n) | O(log n) |
| البحث | O(n) | O(log n) | O(log n) |
| الإضافة | سريع في النهاية | متوسط | متوسط |
| حاوية Key/Value | ❌ | ❌ | ✔ |
| أفضل استخدام | بيانات متسلسلة | عناصر فريدة | قاموس lookup |
🔥 أمثلة مشروع واقعية كبيرة
🔸 1. كلمة تظهر أكثر شيء في نص (map)
map<string, int> freq;
for (string w : words)
freq[w]++;
for (auto p : freq)
cout << p.first << ": " << p.second << endl;
🔸 2. إزالة التكرارات من قائمة (set)
set<int> s(nums.begin(), nums.end());
🔸 3. برنامج تسجيل طلاب (map)
map<int, string> students;
students[100] = "Mohammad";
students[101] = "Ahmad";
🔸 4. فرز درجات باستخدام vector + sort
vector<int> grades = {90, 70, 100};
sort(grades.begin(), grades.end());
⚠️ أخطاء قاتلة يقع فيها المبرمجون الجدد:
❌ استخدام vector لما تحتاج map
❌ استخدام set لحفظ بيانات غير مرتبة
❌ الاعتماد على operator[] في map للبحث
❌ استخدام unordered_map للمفاتيح المعقدة بدون hash
❌ محاولة حذف أثناء التكرار بدون عنونة صحيحة
❌ الاحتفاظ بمؤشرات داخل vector بعد إعادة تخصيصه
🎯 الخلاصة النهائية (مُلخّص استخلاص عصارة STL):
في هذا الدرس تعلمت:
✔ vector
- مصفوفة ديناميكية
- الأسرع في الوصول
- الأفضل للبيانات المتسلسلة
- الإضافة في النهاية سريعة جدًا
- لكنه غير مناسب للحذف أو الإضافة في الوسط
✔ set
- مرتب تلقائيًا
- بدون تكرار
- مناسب للبحث السريع
- يعتمد على شجرة Red-Black
✔ map
- key → value
- مرتّب
- بدون تكرار في المفاتيح
- مناسب لتخزين قواعد بيانات صغيرة
- أساس أغلب الأنظمة
ولما تفهم الفرق بينهم،
بتعرف أي حاوية تستخدم ومتى تستخدمها —
وهذا هو سر مبرمج C++ المحترف.
اكتشاف المزيد من كود التطور
اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.


