الأصناف المجردة (Abstract Classes)
جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 60 من 104.
تُعد الفئة المجردة أي فئة تحتوي على دالة افتراضية بحتة واحدة على الأقل. وبسبب امتلاكها لواجهة غير مكتملة، لا يمكنك إنشاء كائنات من فئة مجردة بشكل مباشر - فهي موجودة فقط ليتم الوراثة منها.
class Shape {
public:
virtual double area() = 0; // دالة افتراضية بحتة - تجعل Shape مجرداً
virtual double perimeter() = 0; // دالة افتراضية بحتة أخرى
void printInfo() { // طريقة عادية - لها تنفيذ
std::cout << "Area: " << area() << std::endl;
}
virtual ~Shape() = default;
};
Shape s; // خطأ: لا يمكن إنشاء كائن من فئة مجردةيمكن أن تحتوي الأصناف المجردة (Abstract classes) على مزيج من الدوال الافتراضية البحتة (بدون تنفيذ)، والدوال الافتراضية العادية (مع تنفيذ افتراضي)، والدوال غير الافتراضية. يتيح لك ذلك تحديد سلوك مشترك مع مطالبة الأصناف المشتقة بتنفيذ وظائف محددة.
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() override { return 3.14159 * radius * radius; }
double perimeter() override { return 2 * 3.14159 * radius; }
};
Circle c(5.0); // حسناً: Circle تنفذ جميع الدوال الافتراضية البحتة
c.printInfo(); // تستخدم printInfo() الموروثة، وتستدعي area() الخاصة بـ Circleيجب على الفئة المشتقة (derived class) تنفيذ جميع الدوال الافتراضية البحتة (pure virtual functions) لتصبح فئة ملموسة (concrete) (قابلة للإنشاء). إذا تركت أيًا منها دون تنفيذ، فإنها تظل فئة مجردة (abstract) في حد ذاتها. تضمن هذه الآلية أن كل فئة ملموسة في التسلسل الهرمي الخاص بك توفر الوظائف المطلوبة.
التحدي
سهللنقم ببناء نظام مشغل وسائط يوضح كيف تحدد الفئات المجردة (abstract classes) العقود لأنواع الوسائط المختلفة. ستقوم بإنشاء فئة أساسية مجردة تحدد ما يجب على كل مشغل وسائط القيام به، ثم تنفيذ مشغلات ملموسة (concrete players) لمحتوى الصوت والفيديو.
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
MediaPlayer.h: قم بتعريف فئة مجردةMediaPlayerتعمل كمخطط لجميع أنواع الوسائط. يجب أن تحتوي الفئة المجردة على:- عضو محمي (protected) باسم
std::string title - عضو محمي (protected) باسم
int duration(بالثواني) - منشئ (constructor) يقوم بتهيئة كلتا القيمتين
- دالة افتراضية بحتة (pure virtual method) باسم
play()— حيث سيتم تشغيل كل نوع وسائط بشكل مختلف - دالة افتراضية بحتة باسم
getMediaType()تعيدstd::string - دالة عادية (غير افتراضية) باسم
showInfo()تطبع:<title> (<duration>s) - Type: <mediaType>— يجب أن تستدعي هذه الدالةgetMediaType()للحصول على النوع - مدمر افتراضي (virtual destructor)
- عضو محمي (protected) باسم
Players.h: قم بتنفيذ مشغلين ملموسين للوسائط يرثان منMediaPlayer:AudioPlayer:- عضو خاص (private) باسم
std::string artist - منشئ يأخذ العنوان، والمدة، والفنان
- تنفيذ
play()لطباعة:Playing audio: <title> by <artist> - تنفيذ
getMediaType()لتعيد"Audio"
VideoPlayer:- عضو خاص (private) باسم
std::string resolution - منشئ يأخذ العنوان، والمدة، والدقة
- تنفيذ
play()لطباعة:Playing video: <title> in <resolution> - تنفيذ
getMediaType()لتعيد"Video"
- عضو خاص (private) باسم
main.cpp: اقرأ أربعة مدخلات (كل منها في سطر منفصل):- عنوان المقطع الصوتي
- مدة الصوت (عدد صحيح)
- عنوان الفيديو
- مدة الفيديو (عدد صحيح)
قم بإنشاء
AudioPlayerمع الفنان"Unknown Artist"وVideoPlayerمع الدقة"1080p". قم بتخزين كليهما في مصفوفة من مؤشراتMediaPlayer*.قم بالمرور عبر المصفوفة واستدعاء
showInfo()أولاً لكل مشغل، ثم استدعاءplay(). اطبع سطراً فارغاً بين كل عنصر وسائط. قم بتنظيف الكائنات المخصصة ديناميكياً عند الانتهاء.
على سبيل المثال، مع المدخلات Summer Vibes، و 180، و Nature Documentary، و 3600:
Summer Vibes (180s) - Type: Audio
Playing audio: Summer Vibes by Unknown Artist
Nature Documentary (3600s) - Type: Video
Playing video: Nature Documentary in 1080pلاحظ كيف تم تعريف الدالة showInfo() مرة واحدة في الفئة الأساسية المجردة ولكنها تستدعي الدالة الافتراضية البحتة getMediaType() — وهذا يوضح كيف يمكن للفئات المجردة مزج التنفيذات الملموسة مع الدوال التي يجب أن توفرها الفئات المشتقة. تذكر استخدام الكلمة المفتاحية override في جميع الدوال التي يتم تجاوزها.
ورقة مرجعية
يحتوي الفصل المجرد (abstract class) على دالة افتراضية بحتة (pure virtual function) واحدة على الأقل ولا يمكن إنشاء كائن منه مباشرة. الغرض من وجوده هو أن يتم توريثه وتحديد واجهة للفصول المشتقة.
تعريف فصل مجرد (Abstract Class)
استخدم = 0 للتصريح عن دالة افتراضية بحتة:
class Shape {
public:
virtual double area() = 0; // دالة افتراضية بحتة
virtual double perimeter() = 0; // تجعل Shape مجرداً
void printInfo() { // طريقة عادية مع تنفيذ
std::cout << "Area: " << area() << std::endl;
}
virtual ~Shape() = default; // مدمر افتراضي
};يمكن أن تحتوي الفصول المجردة على:
- دوال افتراضية بحتة (بدون تنفيذ)
- دوال افتراضية عادية (مع تنفيذ افتراضي)
- دوال غير افتراضية
تنفيذ الفصول الملموسة (Concrete Classes)
يجب على الفصل المشتق تنفيذ جميع الدوال الافتراضية البحتة ليصبح قابلاً لإنشاء كائنات منه:
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() override { return 3.14159 * radius * radius; }
double perimeter() override { return 2 * 3.14159 * radius; }
};
Circle c(5.0); // صحيح: تم تنفيذ جميع الدوال الافتراضية البحتة
c.printInfo(); // الطريقة الموروثة تستدعي area() الخاصة بـ Circleإذا لم يقم الفصل المشتق بتنفيذ جميع الدوال الافتراضية البحتة، فإنه يظل مجرداً ولا يمكن إنشاء كائن منه.
جرّب بنفسك
#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "Players.h"
int main() {
// قراءة المدخلات
std::string audioTitle;
int audioDuration;
std::string videoTitle;
int videoDuration;
std::getline(std::cin, audioTitle);
std::cin >> audioDuration;
std::cin.ignore();
std::getline(std::cin, videoTitle);
std::cin >> videoDuration;
// TODO: إنشاء AudioPlayer مع الفنان "Unknown Artist"
// TODO: إنشاء VideoPlayer بدقة "1080p"
// TODO: تخزين كليهما في مصفوفة من مؤشرات MediaPlayer*
// TODO: التكرار عبر المصفوفة ولكل مشغل:
// 1. استدعاء showInfo()
// 2. استدعاء play()
// 3. طباعة سطر فارغ بين كل عنصر وسائط
// TODO: تنظيف الكائنات المخصصة ديناميكياً (dynamically allocated objects)
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةبناء وتجميع (Compilation) لغة C++ملفات الـ Header وملفات الـ Sourceالـ Namespaces والـ Scopeمقدمة في الـ OOP في C++الـ Classes مقابل الـ Objectsالمؤشر 'this'الـ Methods (الدوال الأعضاء)الـ Attributes (بيانات الأعضاء)أساسيات الـ Ctors والـ Dtorsمراجعة - آلة حاسبة بسيطة4خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticدوال الـ Getters والـ Settersدوال الأعضاء الثابتة (Const)الكلمة المفتاحية Mutableالدوال والمتغيرات الساكنة (Static)الدوال والفئات الصديقة (Friend)مراجعة - مدير الحساب البنكي7الوراثة
الوراثة الأساسيةمستويات الوصول في الوراثةترتيب استدعاء الـ Ctor والـ Dtorإعادة تعريف الدوال (Method Overriding)الدوال الافتراضية والـ VTableالوراثة المتعددةالوراثة الافتراضيةمراجعة - هيكلية الموظفين10نظرة عامة على STL
نظرة عامة وفلسفة STLحاويات STLالـ Iteratorsخوارزميات STLالـ Functors وتعبيرات Lambdaمراجعة - تكرار الكلمات13أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factory و Abstract Factoryنمط Builderنمط Observerنمط Strategy2إدارة الذاكرة
ذاكرة Stack مقابل Heapالمؤشرات والمراجعالذاكرة الديناميكية (new/delete)المؤشرات الذكية في C++RAII في C++مراجعة - مدير المصفوفات الديناميكية5التغليف (Encapsulation)
محددات الوصول في C++محددات الوصول بشكل متعمقإخفاء المعلوماتStruct مقابل Classالأصناف المتداخلة والداخليةمراجعة - نظام سجلات الطلاب8تعدد الأشكال (Polymorphism)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالتحميل الزائد للدوال (Function Overloading)مراجعة الدوال الافتراضية (Virtual Functions)الدوال الافتراضية البحتة (Pure Virtual Functions)الأصناف المجردة (Abstract Classes)تصميم الواجهات (Interface) في C++التحويل الديناميكي (Dynamic Casting) و RTTIملخص - حاسبة الأشكال11مفاهيم OOP المتقدمة
التركيب مقابل الوراثةالـ Mixins عبر CRTPنمط Pimplمحو النوع (Type Erasure)أصناف Enum والأنواع القويةمعالجة الاستثناءات في OOPتسلسلات الاستثناءات المخصصة14أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط قالب الطريقةنمط الحالةنمط التركيبRAII كنمط3المنشئات والموادم
المنشئ الافتراضيالمنشئ ذو المعاملاتمنشئ النسخمنشئ النقلقوائم تهيئة المنشئالمنشئات المفوضةتعمق في الموادمقاعدة الثلاثة / الخمسة / الصفرمراجعة - فئة String6تحميل العوامل (Operator Overloading)
مقدمة في تحميل العواملتحميل العوامل الحسابيةتحميل عوامل المقارنةعوامل Streamتحميل عامل التعيينتحميل العوامل [] و ()عوامل تحويل النوعمراجعة - Matrix Class9القوالب
قوالب الدوالقوالب الأصنافتخصيص القوالبالقوالب المتغيرةأساسيات SFINAE و Type Traitsمراجعة - الحاويات العامة