تعدد الأشكال الساكن
تجنّب الحمل الزائد لاستدعاءات virtual
تعدد الأشكال الساكن درس مجاني في C++ Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C++ Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C++ Academy 4 دروس في المجموع.
نوعان من تعدد الأشكال
تدعم C++ نوعين من تعدد الأشكال.
- ديناميكي: تُحسم الدوال الافتراضية في وقت التشغيل عبر vtable
- ثابت: تُحسم القوالب وCRTP في وقت الترجمة
يستبدل تعدد الأشكال الثابت المرونة بالسرعة.
تكلفة virtual
يتطلب الاستدعاء الافتراضي قفزة غير مباشرة عبر vtable. وعادةً لا يستطيع المترجم تضمينه، مما يمنع العديد من التحسينات.
#include <iostream>
struct Shape {
virtual double area() const = 0;
virtual ~Shape() = default;
};
struct Square : Shape {
double s;
Square(double x) : s(x) {}
double area() const override { return s * s; }
};
int main() {
Shape* p = new Square(3);
std::cout << p->area() << "\n";
delete p;
return 0;
}التوزيع الثابت باستخدام CRTP
مع CRTP، يستدعي الأساس دالة الفئة المشتقة مباشرةً عبر تحويل في وقت الترجمة، ولذلك يستطيع المترجم تضمين كل شيء.
#include <iostream>
template <typename T>
struct Shape {
double area() const { return static_cast<const T*>(this)->area(); }
};
struct Square : Shape<Square> {
double s;
Square(double x) : s(x) {}
double area() const { return s * s; }
};
int main() {
Square sq(3);
std::cout << sq.area() << "\n";
return 0;
}تسمية الدالة المُمرِّرة
عندما يعرّف كل من الأساس والمشتق area، امنح الدالة المُمرِّرة في الأساس اسمًا مميزًا مثل compute حتى تستدعي تنفيذ الفئة المشتقة بوضوح ومن دون التباس.
#include <iostream>
template <typename T>
struct Shape {
double compute() const { return static_cast<const T*>(this)->area(); }
};
struct Circle : Shape<Circle> {
double r;
Circle(double x) : r(x) {}
double area() const { return 3.14159 * r * r; }
};
int main() {
Circle c(2);
std::cout << c.compute() << "\n";
return 0;
}القوالب كتعدد أشكال ثابت
تُعد قوالب الدوال العادية أيضًا شكلًا من تعدد الأشكال الثابت: إذ تعمل الشيفرة نفسها مع أي نوع يوفر الواجهة المطلوبة، أي الكتابة بأسلوب البطة في وقت الترجمة.
#include <iostream>
struct Dog { void speak() const { std::cout << "Woof\n"; } };
struct Cat { void speak() const { std::cout << "Meow\n"; } };
template <typename Animal>
void make_speak(const Animal& a) { a.speak(); }
int main() {
make_speak(Dog{});
make_speak(Cat{});
return 0;
}عندما يكون النوع معروفًا
ينطبق تعدد الأشكال الثابت فقط عندما يكون النوع الفعلي معروفًا وقت الترجمة. إذا كان عليك تخزين كائنات غير متجانسة في حاوية واحدة واختيار السلوك وقت التشغيل، فستظل بحاجة إلى الدوال الافتراضية.
لا توجد حاويات غير متجانسة
لا يمكنك وضع Shape<Square> وShape<Circle> في std::vector نفسها باعتبارهما فئة أساسية مشتركة، لأنهما نوعان غير مرتبطين. هذا هو القيد الأساسي لتعدد الأشكال الثابت.
فائدة الأداء
من خلال تضمين الاستدعاء الموزَّع، يستطيع المترجم طيّ الثوابت وإزالة الحمل الزائد لاستدعاء الدالة بالكامل. في الحلقات الضيقة التي تتعامل مع نوع معروف واحد، قد يكون تعدد الأشكال الثابت أسرع بكثير.
#include <iostream>
template <typename T>
struct Op {
int apply(int x) const { return static_cast<const T*>(this)->apply(x); }
};
struct Doubler : Op<Doubler> {
int apply(int x) const { return x * 2; }
};
int main() {
Doubler d;
int sum = 0;
for (int i = 0; i < 5; ++i) sum += d.apply(i);
std::cout << sum << "\n";
return 0;
}الجمع بين النوعين
يمكنك استخدام CRTP للمسار الحرج، وغطاء افتراضي رفيع عند الحاجة إلى محو النوع. يوفّر ذلك السرعة حيث تكون مهمة، والمرونة حيث تكون مطلوبة.
فحوصات الواجهة الثابتة
إذا نسيت فئة مشتقة تنفيذ الدالة المطلوبة، فسيظهر الخطأ وقت الترجمة عند إنشاء كائن التمرير في الفئة الأساسية، وليس وقت التشغيل.
#include <iostream>
template <typename T>
struct Greeter {
void greet() const { static_cast<const T*>(this)->hello(); }
};
struct English : Greeter<English> {
void hello() const { std::cout << "Hello\n"; }
};
int main() {
English{}.greet();
return 0;
}اختيار الأداة المناسبة
استخدم تعدد الأشكال الثابت عندما:
- يكون النوع معروفًا وقت الترجمة
- يكون الأداء أمرًا بالغ الأهمية
- لا تحتاج إلى نوع أساسي مشترك وقت التشغيل
وإلا، ففضّل الدوال الافتراضية من أجل الوضوح والمرونة.
اختبار سريع
حدّد المفاضلة الأساسية في تعدد الأشكال الثابت.
مراجعة
قارنت بين تعدد الأشكال الثابت والديناميكي.
- الافتراضية = توزيع وقت التشغيل، وvtable، وحاويات غير متجانسة
- CRTP والقوالب = توزيع وقت الترجمة، وإمكانية التضمين، وسرعة أكبر
- يتطلب النوع الثابت معرفة النوع وقت الترجمة
- اختر بناءً على حاجتك إلى المرونة أو الأداء
الأسئلة الشائعة
هل درس «تعدد الأشكال الساكن» مجاني؟
نعم — نص درس «تعدد الأشكال الساكن» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C++ Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C++ Academy 4 دروس في المجموع.
ماذا ستتعلم في «تعدد الأشكال الساكن»؟
تجنّب الحمل الزائد لاستدعاءات virtual تتمرن على C++ Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ C++ Academy؟
لا تُشترط خبرة سابقة. C++ Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «تعدد الأشكال الساكن»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس C++ Academy هذا؟
نعم. كل درس في C++ Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- أسلوب CRTP
- تعدد الأشكال الساكن
- Mixins باستخدام CRTP
- متى تستخدم CRTP