Generics في C++: لماذا هي ضرورية وكيفية استخدام القوالب

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: ما هي Generics في C++ ولماذا نحتاج إليها؟



في عالم البرمجة، غالبًا ما تنشأ الحاجة لكتابة كود يعمل بشكل جيد مع أنواع بيانات مختلفة. على سبيل المثال، يجب أن تكون دالة فرز مصفوفة قادرة على فرز الأعداد الصحيحة والسلاسل النصية أو الكائنات المخصصة. بدون آلية البرمجة العامة (generics)، سيكون من الضروري كتابة دوال منفصلة لكل نوع، مما يؤدي إلى تكرار الكود والأخطاء وتعقيد الصيانة.



في C++، يتم حل هذه المشكلة باستخدام القوالب (templates) — وهي أداة قوية تسمح بإنشاء دوال وفئات عامة. يتم تنفيذ مفهوم generics في C++ من خلال القوالب. فهي تسمح للمبرمج بتعريف خوارزمية أو هيكل بيانات دون الارتباط بنوع معين، بل يتم تحديد النوع في مرحلة الترجمة.



المزايا الرئيسية لاستخدام القوالب:

  • تقليل تكرار الكود — قالب واحد يحل محل العديد من الدوال المحملة بشكل زائد.
  • أمان الأنواع — يتم إجراء جميع فحوصات الأنواع في مرحلة الترجمة.
  • الأداء — لا تضيف القوالب أعباء إضافية أثناء وقت التنفيذ، حيث يتم إنشاء الكود لكل نوع محدد بشكل منفصل.
  • المرونة — يمكن إنشاء تركيبات عامة معقدة، بما في ذلك الحاويات (STL) والخوارزميات.


في هذه المقالة، سنشرح بالتفصيل كيفية استخدام القوالب في C++، بدءًا من الأمثلة البسيطة وصولاً إلى التقنيات المتقدمة.



أساسيات العمل مع قوالب الدوال



أبسط طريقة للتعرف على generics هي كتابة دالة قالبية. لنفترض أننا بحاجة إلى دالة تجد القيمة القصوى بين قيمتين. بدون القوالب، سيكون من الضروري كتابة تحميلات زائدة لـ int و double و std::string وهكذا. باستخدام القوالب، يتم ذلك مرة واحدة:



#include <iostream>#include <string>

// قالب دالةtemplate <typename T>T max(T a, T b) { return (a > b) ? a : b;}

int main() { std::cout << max(10, 20) << std::endl; // int std::cout << max(3.14, 2.71) << std::endl; // double std::cout << max("apple", "orange") << std::endl; // const char* (مقارنة سلاسل نصية) return 0;}


هنا، الكلمة المفتاحية template تعلن عن قالب، و typename T (أو class T) تحدد معامل النوع. يقوم المترجم تلقائيًا بإنشاء نسخة من الدالة لكل نوع مستخدم. من المهم ملاحظة أن الأنواع يجب أن تدعم عامل التشغيل > — وإلا سيحدث خطأ في الترجمة.



يمكن أيضًا تحديد عدة معاملات للنوع:



template <typename T1, typename T2>void printPair(T1 a, T2 b) {    std::cout << "(" << a << ", " << b << ")" << std::endl;}

// الاستخدامprintPair(42, "Hello"); // (42, Hello)


قوالب الفئات: إنشاء حاويات عالمية



تسمح قوالب الفئات بإنشاء هياكل بيانات عامة — وهذا هو أساس مكتبة القوالب القياسية (STL). لنلقِ نظرة على مثال بسيط لفئة قالبية لمصفوفة ديناميكية:



#include <iostream>#include <stdexcept>

template <typename T>class Array {private: T* data; size_t size;public: Array(size_t sz) : size(sz) { data = new T[size]; } ~Array() { delete[] data; } T& operator[](size_t index) { if (index >= size) { throw std::out_of_range("Index out of range"); } return data[index]; } size_t getSize() const { return size; }};

int main() { Array<int> intArray(5); // مصفوفة أعداد صحيحة Array<double> doubleArray(3); // مصفوفة أعداد عشرية

المدونات

توصيات الكتب