C++: أساسيات القوالب

آخر تحديث: 2026-08-26

في الدروس السابقة، كانت الدوال والفئات التي كتبناها تتعامل مع نوع واحد فقط.

لكن ماذا لو أردت كتابة دالة max تعمل مع int و double و string؟

القوالب تحل هذه المشكلة - تسمح للدوال والفئات بدعم أي نوع.


1. ما هي القوالب؟

(1) 1.1 القوالب في الحياة الواقعية*

تشبيه من الحياة الواقعية المكافئ البرمجي
قالب كيك (يمكن صب نكهات عجين مختلفة فيه) قالب
دمج المراسلات في Word (قالب واحد، ملء مستلمين مختلفين) قالب

جوهر القوالب: دع المترجم يُنشئ تلقائياً الإصدار المناسب من دالة أو فئة بناءً على الأنواع المستخدمة.

(2) 1.2 لماذا نحتاج القوالب؟

بدون القوالب (كود مكرر):

TEXT 📖 للعرض فقط
#include <iostream>

// max لـ int
int max(int a, int b) {
 return a > b ? a : b;
}

// max لـ double
double max(double a, double b) {
 return a > b ? a : b;
}
// ... ستحتاج إلى كتابة N إصدار!

مع القوالب (إعادة استخدام الكود):

CPP
#include <iostream>

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

int main() {
 std::cout << max(3, 5) << std::endl; // المترجم يُنشئ إصدار int تلقائياً
 std::cout << max(3.14, 2.72) << std::endl; // المترجم يُنشئ إصدار double تلقائياً
 return 0;
}


2. قوالب الدوال*

(1) 2.1 الصياغة الأساسية*

TEXT 📖 للعرض فقط
template <typename TypeName>
ReturnType functionName(parameterList) {
 // جسم الدالة
}

💡 نقطة مهمة: يمكن كتابة typename أيضاً كـ class (متكافئان)، لكن typename موصى به (أكثر بديهية).

▶ مثال 1: قالب دالة (الصعوبة ⭐)

CPP
#include <iostream>

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

int main() {
 std::cout << "max(3, 5) = " << max(3, 5) << std::endl;
 std::cout << "max(3.14, 2.72) = " << max(3.14, 2.72) << std::endl;
 
 // يمكنك أيضاً تحديد النوع صراحةً
 std::cout << "max<int>(3, 5) = " << max<int>(3, 5) << std::endl;
 
 return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
max(3, 5) = 5
max(3.14, 2.72) = 3.14
max<int>(3, 5) = 5


3. قوالب الفئات*

(1) 3.1 الصياغة الأساسية*

TEXT 📖 للعرض فقط
template <typename TypeName>
class ClassName {
 // الأعضاء
};

▶ مثال 2: قالب فئة Pair (الصعوبة ⭐⭐)

CPP
#include <iostream>
#include <string>

// قالب فئة: تخزين زوج من القيم
template <typename T1, typename T2>
class Pair {
private:
 T1 first;
 T2 second;
 
public:
 Pair(const T1& a, const T2& b) : first(a), second(b) {}
 
 void print() {
 std::cout << "(" << first << ", " << second << ")" << std::endl;
 }
};

int main() {
 Pair<int, double> p1(3, 3.14);
 Pair<std::string, int> p2("Alice", 20);
 
 p1.print(); // (3, 3.14)
 p2.print(); // (Alice, 20)
 
 return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
(3, 3.14)
(Alice, 20)

💡 نقطة مهمة: يجب تحديد أنواع قوالب الفئات صراحةً عند الاستخدام (ما لم يتمكن الباني من استنتاجها).



4. إعلان وتعريف القالب*

(1) 4.1 المشكلة: أين توضع القوالب؟*

إذا وضعت إعلان القالب في .h والتعريف في .cpp، ستحصل على خطأ رابط!

السبب: القالب ليس دالة/فئة حقيقية - إنه "قالب" يُنشئ كوداً عند وقت الترجمة.

(2) 4.2 الحل: ضع الإعلان والتعريف معاً*

النهج الموصى به: ضع كل من إعلان القالب وتعريفه في ملف .h.

TEXT 📖 للعرض فقط
// pair.h (ملف الرأس)
#ifndef PAIR_H
#define PAIR_H

template <typename T1, typename T2>
class Pair {
private:
 T1 first;
 T2 second;
 
public:
 Pair(const T1& a, const T2& b);
 void print();
};

// التعريف يوضع هنا أيضاً!
template <typename T1, typename T2>
Pair<T1, T2>::Pair(const T1& a, const T2& b) : first(a), second(b) {}

template <typename T1, typename T2>
void Pair<T1, T2>::print() {
 std::cout << "(" << first << ", " << second << ")" << std::endl;
}

#endif

💡 نصيحة: هذا هو أكبر اختلاف بين القوالب والدوال/الفئات العادية - القوالب عادة يكون إعلانها وتعريفها معاً.



5. تطبيق: قالب مصفوفة بسيط*

▶ مثال 3: تطبيق قالب فئة Array (الصعوبة ⭐⭐⭐)

CPP
#include <iostream>
#include <cassert>

template <typename T>
class Array {
private:
 T* data;
 int size;
 
public:
 Array(int sz) : size(sz) {
 data = new T[size];
 }
 
 ~Array() {
 delete data;
 }
 
 // باني نسخ (نسخة عميقة)
 Array(const Array& other) : size(other.size) {
 data = new T[size];
 for (int i = 0; i < size; i++) {
 data[i] = other.data[i];
 }
 }
 
 // تحميل العامل زائد التحميل
 T& operator(int index) {
 assert(index >= 0 && index < size);
 return data[index];
 }
 
 int getSize() const {
 return size;
 }
};

int main() {
 Array<int> arr(5);
 for (int i = 0; i < arr.getSize(); i++) {
 arr[i] = (i + 1) * 10;
 }
 
 for (int i = 0; i < arr.getSize(); i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
10 20 30 40 50 

▶ مثال 3: قالب دالة (الصعوبة ⭐)

CPP
#include <iostream>

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

int main() {
    std::cout << "int max: " << getMax(3, 5) << std::endl;
    std::cout << "double max: " << getMax(3.14, 2.71) << std::endl;
    std::cout << "string max: " << getMax("apple", "banana") << std::endl;

    return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
int max: 5
double max: 3.14
string max: banana
💡 نصيحة: تسمح القوالب للدوال بالعمل مع أنواع متعددة؛ يستنتج المترجم تلقائياً النوع الفعلي لـ T.


س: ما الفرق بين القوالب وتحميل الدوال زائد التحميل؟ ج:> - تحميل الدوال زائد التحميل: عدة دوال بنفس الاسم لكن بمعاملات أنواع مختلفة (يحدد المترجم أيها يُستدعى عند وقت الترجمة) > - قوالب الدوال: "قالب" واحد يُنشئ المترجم منه تلقائياً الإصدار المناسب بناءً على الأنواع المستخدمة > > التوصية: إذا كان المنطق متطابقاً تماماً (يختلف النوع فقط)، استخدم القوالب؛ إذا اختلف المنطق، استخدم التحميل الزائد. س: هل يمكن للقوالب دعم جميع الأنواع؟ ج: لا! تتطلب القوالب أن تدعم الأنواع عمليات معينة. > > template <typename T> > T add(T a, T b) { > return a + b; // يتطلب أن يدعم T العامل operator+ > } > > // إذا كان T فئة مخصصة بدون تحميل operator+ زائد التحميل، فسيسبب خطأ ترجمة. > س: متى أستخدم القوالب؟ ج: عندما يكون منطق دالتك/فئتك مستقلاً عن النوع. > > سيناريوهات نموذجية: > - فئات الحاويات (std::vector، std::array) > - الخوارزميات (std::sort، std::find) > - دوال مساعدة (std::max، std::min، std::swap)


س: ما أهم شيء في أساسيات القوالب؟ ج: افهم المفاهيم الأساسية أولاً، ثم عززها من خلال الأمثلة العملية.


❓ أسئلة شائعة

س هل يجب أن يكون إعلان وتعريف القوالب في ملفات الرأس؟
ج نعم، تتطلب القوالب تعريفات كاملة عند وقت الترجمة. تُوضع عادةً في ملفات .h، أو يمكنك استخدام الكلمة المفتاحية export (C++20).
س ما الفرق بين قوالب الفئات وقوالب الدوال؟
ج قوالب الدوال يمكنها استنتاج معاملات النوع تلقائياً، بينما قوالب الفئات تتطلب تحديد معاملات النوع صراحةً.

📖 ملخص


📝 تمارين

  1. أساسي (الصعوبة ⭐): اكتب قالب دالة T min(T a, T b) يُرجع الأصغر من قيمتين.

  2. متوسط (الصعوبة ⭐⭐): اكتب قالب فئة BoxT some عضو من نوع T، ودالتي عضو void set(T val) و T get().

  3. متقدم (الصعوبة ⭐⭐⭐): وسّع قالب فئة Array أعلاه بإضافة:

  4. void push_back(const T& val) (توسيع ديناميكي)

  5. void pop_back()

  6. طبّقه بقوالب بحيث يمكنه تخزين أي نوع

6. 🚀 الخطوات التالية*

الآن بعد أن تعلمت أساسيات القوالب، سندرس بعد ذلك حاويات STL (الدرس 34) - استخدام الحاويات الجاهزة التي توفرها مكتبة C++ القياسية، حتى لا تضطر لإعادة اختراع العجلة!

Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%