بسم الله الرحمن الرحيم اهلا اخواني واخواتي اعضاء الفريق العربي للبرمجة .. اليوم لدينا موضوع مهم من مواضيع البرمجة الكائنية .. وهو الVirtual Function .. لنتخيل اننا نبرمج اشكال صورية (graphices) .. تحتوي على اشكال ؟(مربعات او دوائر ) وخطوط او اي شكل .. وهذه الاشكال نريد طباعتها على الشاشه . وبالطبع كل هذه الاشكال تعتمد على X,Y احداثيات الشاشه للطباعه .. فكيف سيتم هذا ؟؟ في لغة السي ++ .. نستطيع فعل ذلك بطريقة واحدة في البرمجة الكائنية وهو مايسمى بال virtual function . اذا .. ماهي الvirtual function .. ؟؟؟ هي عبارة عن دالة عضو في صنف (class ) ... تعرف باستخدام الكلمة المحجوزة virtual .. تمكننا هذه الدوال من تغيرها والتعديل عليها بالاصناف المورثة من الصنف العام الذي يحتويها .. ويمكن ايضا ان يعرف مؤشر من نوع هذا الصنف الذي يحتوي على الدالة الظاهرية او virtual function . وبعدها يتم استدعاءها عن طريق هذا المؤشر باستخدام ( <- ) . تعريف ويكيبيديا لها ..
سانقل لكم الكود الموجود في ويكيبيديا لانه سهل وواضح واشرحه .. وبعدها اكتب لكم مثال اخر لتوضيحها ..
#include <iostream>using namespace std; class Animal{public: virtual void eat() { cout << "I eat like a generic Animal." << endl; }}; class Wolf : public Animal{public: void eat() { cout << "I eat like a wolf!" << endl; }}; class Fish : public Animal{public: void eat() { cout << "I eat like a fish!" << endl; }}; class OtherAnimal : public Animal{}; int main(){ Animal* anAnimal[4]; anAnimal[0] = new Animal(); anAnimal[1] = new Wolf(); anAnimal[2] = new Fish(); anAnimal[3] = new OtherAnimal(); for (int i = 0; i < 4; i++) { anAnimal[i]->eat(); } for (int i = 0; i < 4; i++) { delete anAnimal[i]; } return 0;}
في البداية بدأنا بانشاء صنف جديد وهو صنف (animal) . وتم التعريف في الجزء العام من هذا الصنف الدالة الظاهرية وهي دالة الاكل الخاصة بكل الحيوانات . ( السطر التالي ) ..
public: virtual void eat() { cout << "I eat like a generic Animal." << endl; }
بعدها تمت برمجة اصناف اخرى مورثة من هذا الصنف العام ... والاصناف الجديدة هي صنف الذئب والسمكه وحيوانات اخرى .. (ظهرت في السطور التالية ) ,,,
class Wolf : public Animalclass Fish : public Animalclass OtherAnimal : public Animal
نشاهد في السطور الثلاثة اعلاه كيف تمت عملية الوراثة من الصنف العام animal .. عن طريق الوراثة ..ورثت الاصناف الجديده دالة الدالة الظاهرية من الصنف العام animal .. لنلاحظ ما هي فائدة الدوال الظاهرية في المثال اعلاه .. عند وراثة الصنف الجديد wolf لدوال الصنف العام animal فقد اخذ منه الدالة الظاهرية eat() والمعرفة بالشكل التالي (داخل animal)
virtual void eat() { cout << "I eat like a generic Animal." << endl; }
حيث تم استخدام الكلمة المحجوزة virtual قبلها .. ولكن نفس الدالة الان موجوده في wolf وايضا void .. ولكنها تطبع جملة اخرى حيث انها معرفه بالشكل التالي ..
void eat() { cout << "I eat like a wolf!" << endl; }
اما في داخل الصنف fish او السمكة .. فهي ايضا ورثت الدالة الظاهرية من الصنف العام animal ولكن تطبع جملة اخرى (كما حدث في الصنف السابق wolf ) فدالة eat في صنف السمكة معرفة بالشكل التالي ..
void eat() { cout << "I eat like a fish!" << endl; }
وبالنسبه للصنف (بقية الحيوانات other animal) فانه ايضا يرث دالة eat من الصنف العام ولكنه لا يحتوي على تعريف جديد خاص بهذه الدالة .. لذلك فهو سيطبع نفس ما موجود في الصنف العام animal .. لذلك فان اخراج الكود السابق على الشاشة سيكون بالشكل التالي ..
I eat like a generic Animal.I eat like a wolf!I eat like a fish!I eat like a generic Animal.
نلاحظ ان او سطر طبع ماموجود في الصنف العام .. اما السطر الثاني فانه طبع ماموجود في الصنف الثاني wolf .. على الرغم من وراثته للدالة من الصنف العام .. الا انه طبع شئ اخر خاص به . وسبب حدوث ذلك هو استخدام الكلمة المحجوزة virtual في الصنف العام والذي جعل الدالة eat ظاهرة لكل الاصناف التي ترثها ويمكن التعديل بها او تركها كما هي .. لكن .. لو كان كود البرنامج السابق بنفس الشكل كله .. ولكن لم نستخدم الكلمة المحجوزة virtual قبل الدالة eat في الصنف العام ... فماذا سيكون الاخراج ... ؟؟ بكل بساطه تطبع جملة الصنف العام لاربع مرات كما هي من دون اي تغيير يطرا عليها من الصنفين الاخرين wolf و fish والسبب في ذلك انها ورثت دالة لايمكن التغيير بها او تعديلها .. فيكون ناتج الطبع على الشاشه ..
I eat like a generic Animal.I eat like a generic Animal.I eat like a generic Animal.I eat like a generic Animal.
وهذه الجملة هي الموجوده في الصنف العام .. ++==++==++==++==++==++==++== لنأخذ مثال اخر .. وبه سنرى المؤشر مع الدوال الظاهرية ..
#include <iostream.h>class base{public:virtual void display(){ cout<<”\nBase”;}};class derived : public base{ public: void display() { cout<<”\nDerived”; }};void main(){ base *ptr = new derived(); ptr->display();}
تم تعريف صنف جديد تحت اسم base الجزء العام منه يحتوي على الدالة الظاهريةdisply والمعرفة بالشكل التالي ..
virtual void display(){ cout<<”\nBase”;}
بعدها برمجنا صنف جديد باسم derived وهو الصنف الابن الذي سيرث الصنف العام base . فقد ورث منه الدالة الظاهرية disply ولكنه عرفها بشكل اخر .
void display() { cout<<”\nDerived”; }
حيث انه سيطبع كلمة تختلف عما سيطبعه بالصنف العام base . نأتي الان الى الدالة main وما سيحدث في داخلها ..
void main(){ base *ptr = new derived(); ptr->display();}
تم تعريف مؤشر من نوع الصنف العام base وهو يشير الى جزء جديد من الذاكرة من نوع derived الذي ورث صفاته من base . الاخراج الذي سيكون على الشاشة من استخدام الجملة .
ptr->display();
هو ما تحتوي دالة disply في الصنف الابن derived . والسبب في ذلك هو ان هذه الدالة هي دالة virtual اي انه يسمح بتغيرها في الدوال الابناء . وايضا ان المؤشر هو من نوع base ولكنه يشير الى بيانات من نوع derived فلذلك الدالة التي ستنفذ هي التي في الصنف الابن derived . ولكن .. لو لم نستخدم الكلمة المحجوزة virtual مع هذه الدالة (اي اننا لم نجعلها ظاهريه ) مع نفس الكود السابق .. فما هو الاخراج ؟؟ الاخراج سيكون طباعة واستخدام الدالة الموجودة في الصنف العام base لانه ورث دالته الى الصنف الابن derived وجعله مجبرا على تنفيذ ما بداخلها من دون تغيير . لدينا الان جزء مهم جدا يجب عليكم معرفته وهو .. دالة ال Constructor او الدالة البانية للصنف (كما تترجم ) لا يمكن ان تكون دالة ظاهرية ابدا ابدا .. والسبب في ذلك هو انه عند تنفيذ او بداية عمل دالة constructor الخاصه بالصنف فان الvirtual function تكون غير مسموحة في تلك الاثناء . لذلك فانه لايمكن ان نجعل الconstructor دالة virtual function . اي ان الكود التالي خاطئ خاطئ ..
class base {virtual base ();};
مثال ..
#include <iostream.h>class base{public:virtual ~base(){}};class derived : public base{ public: ~derived() { }};void main(){ base *ptr = new derived(); // some code delete ptr;}
كود قريب من الكود السابق مع تغيرات بسيطة به يوضح الفقرة الاخيرة .. ارجو ان يكون الموضوع مفيد لكم .. واي اضافات او ملاحظات او تعقيبات على الموضوع ارجو ان تذكورها . واذا كانت هناك فقرة غير واضحه انا في الخدمة .. تحياتي العطرة للجميع ..