Generics في C++: لماذا هي ضرورية وكيفية استخدامها
\n\nكل مبرمج عاجلاً أم آجلاً يواجه موقفاً يحتاج فيه إلى كتابة دالة تعمل مع أنواع بيانات مختلفة. على سبيل المثال، دالة إيجاد العنصر الأقصى – يجب أن تعمل بشكل جيد مع int و double و string. بدون أدوات خاصة، سيكون عليك كتابة تطبيق منفصل لكل نوع. هنا تأتي generics (الأنواع العامة) أو كما تسمى في C++، القوالب لمساعدتك.
في C++، آلية القوالب هي أساس البرمجة العامة. تتيح لك كتابة كود يعمل مع أي نوع بيانات دون التضحية بالأداء. في هذه المقالة، سنشرح ما هي generics في C++، ولماذا هي ضرورية وكيفية استخدامها عملياً. المادة موجهة للمبتدئين، لذا سننتقل من البسيط إلى المعقد.
\n\nما هي القوالب (generics) في C++؟
\n\nالقالب (template) هو بناء يسمح بتعريف دالة أو كلاس يعمل مع نوع بيانات عشوائي. بدلاً من نوع محدد، تحدد معامل النوع (مثل T)، ويقوم المترجم عند الترجمة باستبدال النوع المطلوب. وهذا يسمى استنساخ القالب.
تخيل قالباً لصنع البسكويت. القالب واحد، لكن يمكن صنع البسكويت من عجائن مختلفة: رملية، شوكولاتة، شوفان. القالب هو الشكل، والنوع (int, double, string) هو العجين. تصف الخوارزمية مرة واحدة، ويقوم المترجم تلقائياً بإنشاء إصدارات للأنواع المطلوبة.
\n\nهناك نوعان من القوالب:
\n- \n
- قوالب الدوال – لإنشاء دوال عامة. \n
- قوالب الكلاسات – لإنشاء كلاسات عامة (مثل الحاويات). \n
قوالب الدوال: الخطوة الأولى نحو الكود العام
\n\nلنبدأ بأبسط شيء – دالة قالبية. لنفترض أننا بحاجة إلى دالة تعيد القيمة القصوى من قيمتين. إليك كيف تبدو بدون قوالب:
\n\nint max_int(int a, int b) {\n return (a > b) ? a : b;\n}\n\ndouble max_double(double a, double b) {\n return (a > b) ? a : b;\n}\n\nالكود مكرر. مع القالب يتم حل ذلك بأناقة:
\n\n#include <iostream>\n#include <string>\n\ntemplate <typename T>\nT max_value(T a, T b) {\n return (a > b) ? a : b;\n}\n\nint main() {\n std::cout << max_value(10, 20) << std::endl; // 20\n std::cout << max_value(3.14, 2.71) << std::endl; // 3.14\n std::cout << max_value(std::string("apple"), \n std::string("banana")) << std::endl; // banana\n return 0;\n}\n\nالكلمة المفتاحية template تخبر المترجم أن هذا قالب. <typename T> هي قائمة معاملات القالب. T هو اسم نوع افتراضي سيتم استبداله لاحقاً. بدلاً من typename يمكن استخدام class – لا فرق:
template <class T>\nT max_value(T a, T b) {\n return (a > b) ? a : b;\n}\n\nعند استدعاء max_value(10, 20)، يرى المترجم أن الوسائط من نوع int، ويقوم تلقائياً بإنشاء إصدار من الدالة لـ int. هذه العملية تسمى استنتاج الأنواع. إذا لزم الأمر، يمكن تحديد النوع صراحة:
std::cout << max_value<double>(5, 7.5) << std::endl; // 7.5\n\nهام: يجب تعريف الدالة القالبية في ملف الرأس (أو قبل استخدامها)، لأن المترجم يحتاج إلى رؤية التعريف الكامل للاستنساخ.
\n\nقوالب الكلاسات: إنشاء حاويات عالمية
\n\nقوالب الكلاسات هي أداة أكثر قوة. المثال الأبرز هو الحاويات من المكتبة القياسية (STL): std::vector<int>، st