C++: أساسيات القوالب
آخر تحديث: 2026-08-26
في الدروس السابقة، كانت الدوال والفئات التي كتبناها تتعامل مع نوع واحد فقط.
لكن ماذا لو أردت كتابة دالة
maxتعمل معintوdoubleوstring؟القوالب تحل هذه المشكلة - تسمح للدوال والفئات بدعم أي نوع.
1. ما هي القوالب؟
(1) 1.1 القوالب في الحياة الواقعية*
| تشبيه من الحياة الواقعية | المكافئ البرمجي |
|---|---|
| قالب كيك (يمكن صب نكهات عجين مختلفة فيه) | قالب |
| دمج المراسلات في Word (قالب واحد، ملء مستلمين مختلفين) | قالب |
جوهر القوالب: دع المترجم يُنشئ تلقائياً الإصدار المناسب من دالة أو فئة بناءً على الأنواع المستخدمة.
(2) 1.2 لماذا نحتاج القوالب؟
بدون القوالب (كود مكرر):
#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 إصدار!
مع القوالب (إعادة استخدام الكود):
#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 الصياغة الأساسية*
template <typename TypeName>
ReturnType functionName(parameterList) {
// جسم الدالة
}
💡 نقطة مهمة: يمكن كتابة typename أيضاً كـ class (متكافئان)، لكن typename موصى به (أكثر بديهية).
▶ مثال 1: قالب دالة (الصعوبة ⭐)
#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;
}
الخرج:
max(3, 5) = 5
max(3.14, 2.72) = 3.14
max<int>(3, 5) = 5
3. قوالب الفئات*
(1) 3.1 الصياغة الأساسية*
template <typename TypeName>
class ClassName {
// الأعضاء
};
▶ مثال 2: قالب فئة Pair (الصعوبة ⭐⭐)
#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;
}
الخرج:
(3, 3.14)
(Alice, 20)
💡 نقطة مهمة: يجب تحديد أنواع قوالب الفئات صراحةً عند الاستخدام (ما لم يتمكن الباني من استنتاجها).
4. إعلان وتعريف القالب*
(1) 4.1 المشكلة: أين توضع القوالب؟*
إذا وضعت إعلان القالب في .h والتعريف في .cpp، ستحصل على خطأ رابط!
السبب: القالب ليس دالة/فئة حقيقية - إنه "قالب" يُنشئ كوداً عند وقت الترجمة.
- إذا كان الإعلان في
.hوالتعريف في.cpp، فلن يعرف المترجم أي إصدارات أنواع يُنشئ عند ترجمة.cpp. - فقط عند نقطة الاستخدام يعرف المترجم الإصدارات التي يجب إنشاؤها.
(2) 4.2 الحل: ضع الإعلان والتعريف معاً*
النهج الموصى به: ضع كل من إعلان القالب وتعريفه في ملف .h.
// 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 (الصعوبة ⭐⭐⭐)
#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;
}
الخرج:
10 20 30 40 50
▶ مثال 3: قالب دالة (الصعوبة ⭐)
#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;
}
الخرج:
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)
س: ما أهم شيء في أساسيات القوالب؟ ج: افهم المفاهيم الأساسية أولاً، ثم عززها من خلال الأمثلة العملية.
❓ أسئلة شائعة
📖 ملخص
- قوالب الدوال: البرمجة العامة، يتم إنشاء نسخ منها عند وقت الترجمة
- معاملات القالب تُعلن باستخدام typename/class
- قوالب الفئات: تجريدات عامة مثل الحاويات والمؤشرات الذكية
- تخصيص القوالب: تطبيقات مخصصة لأنواع معينة
- مزايا القوالب: إعادة استخدام الكود، أمان الأنواع، بدون تكلفة
📝 تمارين
-
أساسي (الصعوبة ⭐): اكتب قالب دالة
T min(T a, T b)يُرجع الأصغر من قيمتين. -
متوسط (الصعوبة ⭐⭐): اكتب قالب فئة
BoxTsome عضو من نوعT، ودالتي عضوvoid set(T val)وT get(). -
متقدم (الصعوبة ⭐⭐⭐): وسّع قالب فئة
Arrayأعلاه بإضافة: -
void push_back(const T& val)(توسيع ديناميكي) -
void pop_back() -
طبّقه بقوالب بحيث يمكنه تخزين أي نوع
6. 🚀 الخطوات التالية*
الآن بعد أن تعلمت أساسيات القوالب، سندرس بعد ذلك حاويات STL (الدرس 34) - استخدام الحاويات الجاهزة التي توفرها مكتبة C++ القياسية، حتى لا تضطر لإعادة اختراع العجلة!