بسم الله الرحمن الرحيم
اقدم اليوم لكم الدرس الثاني بموضوع polymorphism
______________________________________________________________________________
الربط الديناميكي
dynamic binding
لو قام برنامج باستدعاء تابع(دالة) ظاهري باستخدام مؤشر من نمط صف اساسي إلى غرض تابع إلى صف مشتق ,فإنه سيقوم باختيار التابع المناسب ديناميكيا ً.
ونسمي هذه العملية (اي اختيار التابع المناسب اثناء زمن التنفيذ ) بالربط الديناميكي dynamic binding.
سنعرض مثال نصممه بالاستعانه بتقنية تعدد الاشكال(الاوجه -الوجهيات ) polymorphism هذا المثال يقوم بطلب المستخدم بادخال قيمة ومن ثم ينظر للقيمة اذا كانت اقل من 10 ينشأ غرض A ويعطيه القيمة اما اذا كان خلاف ذلك سيقوم بانشاء غرض B ويعطيه القيمة
الصف(class -الصنف) A يحتوي على دالة طباعة تقوم بعرض القيمة فقط
اما الصف B يحتوي على دالة طباعة تقوم بعرض القيمة بعد خصم 5% من القيمة الاصلية.
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
#include<new>
class A
{
public:
A(int=0);
virtual print();
protected:
int x;
};
A::A(int a)
{
x=a;
}
A::print()
{
cout<<"This Class is A and The value :"<<x<<"it is less than 10"<<endl;
}
class B:public A
{
public:
B(int=0);
print();
private:
int y;
};
B::B(int a)
{
y=a;
}
B::print()
{
cout<<"This Class is B and The value is:"<<y<<" . it is more than 10 and the value aftar the discount is 5% ,Now The value is :"<<y-((5*y)/100)<<endl;
}
main()
{
int value;
A *p=NULL;
cout<<"Enter The value";
cin>>value;
if(value<10)
p=new A(value);
else
p=new B(value);
p->print();
return 0;
}
_____________________________________________________________________________
نأتي الان إلى موضوع نظري وهو Switch
يمكن لنا من خلال تعليمة Switch ان نقوم باستدعاء التوابع من صفوفها على حسب الشرط الذي يتحكم بنوع النمط(الصف-الصنف-الفئة-class) ,وهذه طريقة مجدية , ويمكن ان تكون باستخدام if لكن !
في الانماط المتقدمة والتي تعتمد على مستويات متعدده في البنى الهرمية سيكلف هذا وقتا كثيرا وربما نواجه اخطاء بارسال رسائل غير موجودة داخل الصف , ايضا ربما ينسى المبرمج ان يقوم بعمليات الفحص المطلوبة بشكل صحيح , وكذلك اي تغيير لأي صف من صفوف البنى الهرمية يعتبر تعديلا على عملية switch من جديد وهذا يكلف وقتا كبيرا ويزيد من احتمالية الاخطاء .
لذلك اتت تقنية تعدد الاشكال(الاوجه- الوجهيات) polymorphism لتحل هذه المشكلة وتسهل استخدامها داخل البرامج ذات المستويات المتعدده داخل بنية هرمية.
ملاحظة :مصظلح "رسالة" مرادف لمصطلح "استدعاء تابع" .
*****************************************************************************
سنتكلم اليوم عن موضوع من اهم مواضيع polymorphism تعدد الاشكال.
الصفوف المجردة والصفوف الجسدة و التوابع الظاهرية الصافية (الصوافي) pure virtual function
التوابع الظاهرية الصافيه (الصوافي) pure virtual function:
هذه التوابع هي توابع ظاهرية ولكن تتميز بكلمة صافيه فماذا تعني ؟!
تعني كلمة صافيه انها توابع تتواجد في صفوف خاصة تسمى الصفوف المجردة كنماذج فقط ويتم تعريفها في الصفوف المشتقة.
والفائدة من هذه التوابع انه في بعض البنى الهرمية نقوم بتعريف صف اساسي ويوجد مثلا دالة طباعة print لا نحتاجها في الصف الاساسي , وانما نحتاجها في الصفوف المشتقة فقط . لذلك نقوم بتعريفها على انها توابع صافيه بإعطائها قيمة pure
القيمة pure:
لكي نعطي دالة قيمة pure لنجعلها دالة صافيه , نقوم بماسواتها بالصفر اثناء تعريف النموذج
مثلا:
virtual print()=0
ربما يستبهم الامر على القارء وانا اعلم ذلك لكن يكتفي حاليا في قراءة التعاريف التي ستفهم في مثالنا القادم.
الصفوف المجردة:
وهي الصفوف التي تحتوي على توابع ظاهرية صافيه, وهذه الصفوف تمثل الارضيه للبنى الهرمية اي انها صف اساسي بمثابة القمة في البنية الهرمية , ولكن هذه الصفوف لا يمكن ان يشتق منها اي غرض والسبب في ذلك انها تحتوي على توابع لا يمكن استدعائها من الصف نفسه لانها غير معرفه فيه الا وهي التوابع الظاهرية الصافيه .
الصفوف المجسدة:
هي الصفوف التي مرت معنا سابقا , صفوف يمكن اشتقاق اغراض منها , وهي تشتق من الصف المجرد ان وجد .
سنعرض لكم الان مثال جيد لشرح التوابع الظاهرية والظاهرية الصافيه والصفوف المجردة والصفوف المشتقة من خلال بنية هرمية مكونه من مستويين .
(ملاحظة:في حال استبهام التعاريف ارجوا فهم المثال ومحاولة اعادة تجريبه , ثم العودة مرة اخرى للتعاريف ومقارنتها مع استخدامها داخل المثال .
_____________________________________________________________________________
#ifndef PROJECT_H
#define PROJECT_H
class project
{
public:
virtual char *getname()=0;
virtual getnum();
virtual print()=0;
virtual getSalary();
virtual retired();
};
#endif
الملف الرأسي لتعريف الصف project
#include<iostream>
using namespace std;
#include"project.h"
project::getnum()
{
return 0;
}
project::getSalary()
{
return 0;
}
project::retired()
{
return 0;
}
الصف project
#ifndef STUDENT_H
#define STUDENT_H
#include"project.h"
class student:public project
{
public:
student(char * ="",int=0);
virtual char *getname();
virtual getnum();
virtual print();
protected:
char *name;
int number;
};
#endif
الملف الرأسي لتعريف الصف student
#include<iostream>
using namespace std;
#include"student.h"
student::student(char *nam,int num)
{
name=nam;
number=num;
}
char *student::getname()
{
return name;
}
student::getnum()
{
return number;
}
student::print()
{
cout<<"This Class Is Student Class";
}
الصف لـstudent
#ifndef LABOURER_H
#define LABOURER_H
#include"student.h"
class labourer:public student
{
public:
labourer(char * = "",int =0,int=0,int=0);
virtual char *getname();
virtual getnum();
virtual print();
virtual getSalary();
virtual retired();
private:
int salary;
int old;
};
#endif
الملف الرأسي لتعريف الصف labourer
#include<iostream>
using namespace std;
#include"labourer.h"
#include"student.h"
labourer::labourer(char *nam,int num,int sal,int ore):student(nam,num)
{
salary=sal;
old=ore;
}
char *labourer::getname()
{
return name;
}
labourer::getnum()
{
return number;
}
labourer::getSalary()
{
return salary;
}
labourer::retired()
{
return old+60;
}
labourer::print()
{
cout<<"This Class Is Labourer"<<endl;
}
الصف labourer
#include<iostream>
using namespace std;
#include"project.h"
#include"student.h"
#include"labourer.h"
void doit(project *pointer)
{
cout<<"Name is :"<<pointer->getname()<<endl;
cout<<"Number is :"<<pointer->getnum()<<endl;
cout<<"Salary is :"<<pointer->getSalary()<<endl;
cout<<"Retired :"<<pointer->retired()<<endl;
pointer->print();
cout<<endl;
}
main()
{
project *p=NULL;
student st("Ali",104);
labourer la("Ahmad",219,1520,1988);
p=&st;
doit(p);
p=&la;
doit(p);
return 0;
}
___________________________________________________________________________
البرنامج
يقوم البرنامج على ثلاثة صفوف
الأول هو project
وهو صف مجرد لا يمكن ان نشتق اغراض منه لذلك كما قلنا هو يمثل ارضية العمل , فيحتوي على الدوال الموجودة في الصفوف الموجودة بالمستويات الوراثية.
نلاحظ تعريف الدالة print والدالة getname على انهما دالتان ظاهريتان صافيتان وذلك عن طريق اعطائهم قيمة pure
كما ونلاحظ ظرورة تعريفهم في اول مستويات الوراثة (الصف student )
اما باقي الدوال فهي دوال ظاهرية فقط ونلاحظ انها معرفة في الصف المجرد , وذلك من اجل ارسال اي رسالة (عن طريق مؤشر على الصف الاساسي المجرد ) في صف مشتق ولم تكن موجودة فيه.
الصفوف الاخرى
student & labourer
هذه صفوف مجسدة يمكن اشتقاق اغراض منها كما هو مبين من البرنامج
يأخذ الصف student اسم الطالب وكذلك رقمه (اي رقم كمثال فقط )
يأخذ الصفlabourer اسم الموظف ورقمه وراتبه وسنة ولادته.
الدوال
getname تعيد الاسم
getnum تعيد الرقم
getSalary تعيد الراتب
retired تعيد سنة التقاعد وهي سنة الميلاد زائد 60
دالة print تقوم بطباعة اسم الصف
في داخل البرنامج (fig)
يتم اشتقاق مؤشر من الصف الاساسي المجرد project
ثم اشتقاق اغراض لكلا الصفين student & labourer
تم اعطاء المؤشر عنوان غرض الصف student وتم اعطاء المؤشر لدالة doit ثم تمت نفس العملية مع الصف labourer
الدالة doit تقوم بطباعة الاسم والرقم والراتب وسنة التقاعد واسم الصف عن طريق استدعاء الدوال بواسطة المؤشر pointer
ستكون المخرجات
Name Is:Ali
Number Is : 104
Salary Is:0 R.S
retired:0
This Class Is Student Class
Name Is:Ahmad
Number Is : 104
Salary Is:0 R.S
retired:2048
This Class Is Labourer
_________________________________________________________________________
هكذا اكون قد انهيت الدرس الثاني.
اتمنى من المولى القدير ان اكون قد وفقت في طرحه
مع تمنياتي لكم بالتوفيق
ملاحظة: بالنسبة للمصطلحات قمت بكتابة المصطلح على حسب ماورد بأغلب الكتب وايضا كتبة اسمه باللغة الانجليزية .