مكتبة STL (Standard Template Library)

🎯 مقدمة:

STL ليست مجرد “مكتبة”…
STL هي قلب C++ الحقيقي.

هي مجموعة من:

  • حاويات Containers (vector, list, set, map…)
  • خوارزميات Algorithms (sort, find, binary_search…)
  • هياكل بيانات Data Structures
  • Iterators للتنقّل
  • Templates لتعميم الأنواع

لو فهمت STL، انت فعليًا فهمت 50% من قوة C++.

في هذا الدرس رح نتوسع في أهم 3 حاويات تستخدمها يوميًا:

  1. vector
  2. set
  3. 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:
    1. ينشئ block أكبر (عادة ×2)
    2. ينقل العناصر له
    3. يحذف القديم

لذلك:

الإضافة في النهاية سريعة جدًا
الإضافة في الوسط بطيئة جدًا


⚠️ أخطاء شائعة:

❌ 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

الخاصيةvectorsetmap
الترتيبلانعمنعم
التكرارمسموحممنوعالمفتاح ممنوع
الوصول لعناصر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++ المحترف.


اكتشاف المزيد من كود التطور

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

اترك رد

Scroll to Top