مقدمة: لماذا نحتاج إلى قوائم المهام في لغة C++؟
\nتخيل أنك تقف في طابور عند صندوق السوبر ماركت. أول شخص يأتي يتم خدمته أولاً. في البرمجة، يُسمى هذا الهيكل قائمة انتظار (queue). وهو أحد أنواع البيانات الأساسية التي تعمل وفق مبدأ FIFO (First In, First Out) - "من يأتي أولاً يغادر أولاً".
\nفي لغة C++، تُستخدم قوائم المهام في كل مكان: من معالجة طلبات الشبكة إلى إدارة الخيوط في التطبيقات متعددة الخيوط. بالنسبة للمبرمج المبتدئ، فهم قوائم الانتظار هو خطوة نحو إتقان مواضيع أكثر تعقيدًا مثل البرمجة غير المتزامنة وأنماط التصميم.
\nفي هذه المقالة، سنشرح كيفية إنشاء واستخدام قوائم الانتظار في لغة C++، وسننظر في الحاوية القياسية std::queue، وسنكتب عدة أمثلة عملية، ونناقش الأخطاء الشائعة للمبتدئين.
أساسيات قائمة الانتظار: النظرية والمثال الأول
\nما هي قائمة الانتظار؟
\nقائمة الانتظار هي نوع بيانات مجرد يدعم عمليتين أساسيتين:
\n- \n
- إضافة عنصر إلى نهاية قائمة الانتظار (enqueue / push); \n
- استخراج عنصر من بداية قائمة الانتظار (dequeue / pop). \n
في لغة C++، يتم تنفيذ قائمة الانتظار القياسية في ملف الرأس <queue> كفئة std::queue. بشكل افتراضي، تستخدم std::deque كحاوية أساسية، ولكن يمكنك استبدالها بـ std::list.
المثال الأول: قائمة انتظار الأرقام
\nلنكتب برنامجًا بسيطًا ينشئ قائمة انتظار من الأعداد الصحيحة، ويضيف إليها عدة قيم، ويعرضها على الشاشة.
\n#include <iostream>\n#include <queue>\n\nint main() {\n std::queue<int> my_queue;\n\n // إضافة عناصر إلى قائمة الانتظار\n my_queue.push(10);\n my_queue.push(20);\n my_queue.push(30);\n\n std::cout << "العنصر الأول (front): " << my_queue.front() << std::endl;\n std::cout << "العنصر الأخير (back): " << my_queue.back() << std::endl;\n std::cout << "حجم قائمة الانتظار: " << my_queue.size() << std::endl;\n\n // استخراج العناصر\n while (!my_queue.empty()) {\n std::cout << "تم الاستخراج: " << my_queue.front() << std::endl;\n my_queue.pop();\n }\n\n return 0;\n}\nنتيجة تشغيل البرنامج:
\nالعنصر الأول (front): 10\nالعنصر الأخير (back): 30\nحجم قائمة الانتظار: 3\nتم الاستخراج: 10\nتم الاستخراج: 20\nتم الاستخراج: 30\nلاحظ: front() تُرجع العنصر الأول (الذي سيتم استخراجه بعد ذلك)، بينما back() تُرجع آخر عنصر تمت إضافته. الطريقة pop() تحذف العنصر ولكنها لا تُرجعه - من المهم تذكر ذلك!
الطرق الأساسية لـ std::queue
\nللعمل بكفاءة مع قائمة الانتظار، تحتاج إلى معرفة جميع طرقها. فيما يلي القائمة الكاملة:
\n- \n
push(const T& value)- يضيف عنصرًا إلى نهاية قائمة الانتظار; \n pop()- يحذف العنصر الأول (لا يُرجعه); \n front()- يُرجع مرجعًا إلى العنصر الأول; \n back()- يُرجع مرجعًا إلى العنصر الأخير; \n empty()- يتحقق مما إذا كانت قائمة الانتظار فارغة (يُرجع true/false); \n size()- يُرجع عدد العناصر. \n
هام: قبل استدعاء front() أو pop()، تحقق دائمًا مما إذا كانت قائمة الانتظار فارغة باستخدام empty(). محاولة الوصول إلى عناصر قائمة انتظار فارغة ستؤدي إلى سلوك غير محدد (على الأرجح، تعطل البرنامج).
مثال عملي: قائمة مهام
\nالآن دعنا نحاكي موقفًا حقيقيًا. لنفترض أن لدينا نظامًا يعالج الطلبات. كل طلب هو هيكل يحتوي على رقم ووصف. نضع الطلبات في قائمة الانتظار.