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

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

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

ابدأ الدورة
```html

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



في عالم البرمجة بلغة C++، أحد المفاهيم الأساسية التي تتيح كتابة كود مرن وقابل لإعادة الاستخدام وآمن من حيث النوع هي القوالب (templates). غالبًا ما يطلق عليها "generics" قياسًا على لغات أخرى مثل Java أو C#، على الرغم من أن هذه المفهوم في C++ أقوى وأعمق بكثير.



تخيل أنك بحاجة إلى كتابة دالة للبحث عن العنصر الأقصى في مصفوفة. بالنسبة للأعداد الصحيحة (int) هناك دالة، وللأعداد العشرية (double) دالة أخرى، وللنصوص (std::string) دالة ثالثة. بدون القوالب، سيتعين عليك تكرار الكود، مما يؤدي إلى أخطاء وزيادة تعقيد الصيانة.



Generics (القوالب) تحل هذه المشكلة. فهي تتيح كتابة كود يعمل مع أي نوع بيانات، دون التضحية بالأداء (على عكس الدوال الافتراضية أو void*). القوالب هي أساس المكتبة القياسية لـ C++ (STL): الحاويات (std::vector، std::map)، الخوارزميات (std::sort، std::find) والمؤشرات الذكية (std::shared_ptr) — جميعها تم تنفيذها باستخدام القوالب.



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



الجزء الرئيسي: كيفية استخدام Generics (القوالب) في C++



1. قوالب الدوال (Function Templates)



أبسط طريقة للبدء مع generics هي إنشاء قالب دالة. بدلاً من كتابة عدة إصدارات محملة بشكل زائد، تكتب إصدارًا واحدًا معلمًا بنوع البيانات.



الصيغة:

template <typename T>T max(T a, T b) {    return (a > b) ? a : b;}


الكلمة المفتاحية template تخبر المترجم بأن هذا قالب. <typename T> تعلن عن معامل النوع T. يمكنك استخدام class بدلاً من typename — لا فرق بينهما.



مثال على الاستخدام:

#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: 20 std::cout << max(3.14, 2.71) << std::endl; // double: 3.14 std::cout << max(std::string("C++"), std::string("Python")) << std::endl; // string: Python return 0;}


مهم: يقوم المترجم بإنشاء نسخة منفصلة من الدالة لكل نوع مستخدم. يُسمى هذا استنساخ القالب (template instantiation). بفضل هذا، يتم تحقيق أقصى أداء — لا توجد تكاليف إضافية للاستدعاءات الافتراضية.



عدة معاملات نوع:

template <typename T1, typename T2>void printPair(T1 a, T2 b) {    std::cout << "(" << a << ", " << b << ")" << std::endl;}// printPair(1, "hello"); // سيطبع: (1, hello)


2. قوالب الفئات (Class Templates)



تتيح قوالب الفئات إنشاء هياكل بيانات عامة. المثال الكلاسيكي هو std::vector<T>. يمكنك إنشاء فئة قالب خاصة بك، على سبيل المثال، مكدس بسيط:



template <typename T>class Stack {private:    T* data;    size_t size;    size_t capacity;public:    Stack(size_t cap = 10) : size(0), capacity(cap) {        data = new T[capacity];    }    ~Stack() { delete[] data; }

void push(const T& value) { if (size < capacity) { data[size++] = value; } }

T pop() { if (size > 0) { return data[--size]; } throw std::out_of_range("Stack is empty"); }

bool isEmpty() const { return ```

المدونات

توصيات الكتب