اسرة منتديات الفيلسوف تتمني لمصرنا الحبيبة الامن والامان , مع أطيب الاماني بقضاء وقت ممتع معنا .. أ/ محمد الجندي .. موجه علم النفس بالمنصورة .. 01001637295 كلمة الإدارة



إضافة رد
 
أدوات الموضوع إبحث في الموضوع انواع عرض الموضوع
قديم 02-12-2012, 08:29 PM   #1
معلومات العضو
مدير عام
الصورة الرمزية محمد الجندي
رقم العضوية : 1
تاريخ التسجيل: Sep 2002
مجموع المشاركات : 886
محمد الجندي تم تعطيل التقييم
افتراضي برنامج قاعدة بيانات لتسجيل بيانات الطلاب على شكل سجلات


هذا المشروع عبارة عن برنامج قاعدة بيانات لتسجيل بيانات الطلاب على شكل سجلات كل سجل يحوي بيانات الطالب مثل الاسم و الرقم و الدرجات و التقدير الخ ، و قد قمت بكتابته بلغة سي بلس بلس لسببين :
الأول : التمرين و التدريب الذاتي على كتابة المشاريع بهذه اللغة القوية و المتينة لتحقيق مفاهيم البرمجة كائنية التوجه OOP لحل المشكلات البرمجية راجع هذا الموضوع بخصوص الـOOP .
الثاني : أن هذه اللغة و برغم انتشارها الواسع عالميا فإن استخدامها بين المبرمجين العرب بصورة احترافية قليل – نوعا ما – و ذلك لأسباب ليس هذا مجال بيانها ، لهذا أحببت المساهمة في عملية نشر استخدام هذه اللغة و محاولة تبسيطها و تقريبها للمبرمج العربي خاصة المبتدئين من أمثالي .

الدخول إلى التفاصيل :
لتنفيذ هذا المشروع نحتاج إلى 5 كلاسات (طبقات) كالتالي – مع ملاحظة أن استخراج الطبقات مسألة نسبية تختلف من مبرمج إلى آخر - :
· كلاس Student : و فيه عناصر <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات كل طالب كالرقم و الاسم و الدرجات ...
· كلاس dBase : تخزين <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب في قاعدة <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات مبينة على السلاسل المتصلة Linked List و القيام بعمليات الحذف و الاضافة و التعديل الخ.
· كلاس RW2File : مهمته تسجيل البيانات على القرص الصلب و قراءتها منه.
· كلاس UserInterFace : و هو واجهة المستخدم الذي يقوم بإدارة كل العمليات التي يقوم بها البرنامج.
· كلاس StudentCount : و هو كلاس بسيط مهمته تنحصر في عدّ <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب بحيث يكون لدينا عدد <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب جميعا و عدد الناجحين فقط و عدد الراسبين فقط و ذلك باستخدام الدوال الساكنة static function بدون إنشاء كائنات منه كما سيأتي التفصيل لاحقا...

نأتي الآن إلى تصميم مبسط بواسطة لغة UML (لغة النمذجة الموحدة ) مستخدمين في ذلك تخطيط الطبقة Class Diagram :





شرح مختصر للمخطط أعلاه :
· مساحة الأسماء الجديدة التي سنستخدمها في المشروع بالاسم : studentDB وضعناها بالأعلى.
· الكلاس UserInterFace يستخدم نسخة واحدة من الكلاس RW2File .
· الكلاس RW2File يرث الكلاس dBase

· الكلاس dBase يتضمن نسخ من الكلاس Student عددها مابين 0 – إلى n .
· الكلاس Student يرث الكلاس StudentCount .

الطبقة الأولى Student :
بدأنا بها لأنها أهم طبقة ، و كما ترى فهي ترث من الطبقة StudentCount و لكن دعنا لا نستعجل الآن في ذكر هذا و لنقم بإنشاء الطبقة و كبسلتها بصورة محترفة ! و نترك الكلام عن StudentCount لاحقا :
هذه الطبقة (الكلاس) مطلوب منها تخزين البيانات الخاصة بالطالب : الاسم و الرقم و الدرجات لعدد 6 مواد و درجة نصف السنة و درجة آخر السنة و المتوسط و المعدل (التقدير) كل ذلك في عناصر <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات (متغيرات) بالاضافة إلى دوال تقوم بإدخال هذه البيانات في المتغيرات الأعضاء (Setter) و دوال تقوم بقراءة هذه القيم (Getter) ... دعنا لا نطيل الكلام و حيّ على العمل :
افتح <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">مشروع جديد على الـ Dev-C++ - حيث سيكون هو المترجم (و بالأصح IDE) الذي سنعمل عليه المشروع ( راجع هذا الموضوع لتحميل المترجم المميز Dev-C++ و معرفة أساسيات استخدامه : هنـــــــــا ) و ليكن مشروعنا على الكونسول – و يمكن تطويره لاحقا ليعمل على واجهة رسومية GUI - و اكتب اسم المشروع و ليكن Student_Database_System أو أي اسم مناسب ثم اختر النوع Console Application ثم كما في الصورة بالأسفل ثم حدد مكان حفظ المشروع و من الأفضل عمل مجلد جديد لحفظ ملفات المشروع به :




سيقوم البرنامج بفتح ملف الـmainتلقائيا و لن نحتاجه الآن و سنعدّله لاحقا المهم ، قم بفتح ملف جديد تابع للمشروع (Ctrl+N) ثم اضغط yes لتأكيد إضافته للمشروع و احفظه بالاسم StudentClass.h ثم اكتب الكود التالي و احفظه : (ملاحظة كل كلاس سيكون له ملفان منفصلان :
ملف رأس و ملف تنفيذي.. )



حمل الملف من هنا

تحليل :
السطر 5:
أعلنا عن مساحة أسماء جديدة بالاسم studentDB بحيث تتضمن تعريف الكلاس بالاضافة للمتغير الثابت QuizeSizeو الذي يساوي القيمة 6 في سطر 7 ، هذا الثابت سنستخدمه لتحديد حجم المصفوفة في السطر 33 لدرجات 6 مواد و سنستخدمه أيضا في أماكن أخرى من المشروع .
السطران 11 و 13 :
دالة البناء و دالة الهدم للكلاس.
الأسطر 15 – 21 :
هذه دوال الـ Setter و واضح من اسمها أن وظيفتها هي تعيين قيم المتغيرات الأعضاء في القسم الخاص private و بما أنها في القسم الخاص فهذا يعني أنه لا يمكن الوصول إليها من خارج الكلاس لذلك نستعمل دوال الـSetter لتعيين قيم هذه المتغيرات بحيث اننا نمرر القيمة التي نريد وضعها للدالة و تقوم هي بإسنادها للمتغير – بمعرفتها J - كما سنرى عند عمل الملف التنفيذي implementation file للكلاس.
الأسطر 23 – 29 :
دوال الـGetter و وظيفتها بكل بساطة هي ارجاع القيم المحفوظة بالمتغيرات الخاصة ، فمثلا لو أردنا الاسم نستعمل الدالة GetName() التي ترجع الاسم المحفوظ بالمتغير الخاص Name ، و هكذا... و أعلنا عن هذه الدوال أنها ثابته const حتى لا يسمح لها المترجم بأن تقوم بتغيير قيمة المتغير و إنما فقط تُرجع قيمته و ذلك تجنبا لحدوث أي خطأ غير مقصود ..
كلا النوعين – دوال الـSetter و الـGetter في القسم العام publicمن الكلاس ، يسمى هذا النوع من الدوال بالواجهة الخارجية للكلاس بحيث يتم الوصول للأعضاء في القسم الخاص عن طريق هذه الواجهة فقط و لايمكن الوصول مباشرة لأعضاء القسم الخاص و ذلك لحماية الكائن من أن يتم تعديل حالته الداخلية – أي المتغيرات الخاصة – من خارجه .
الأسطر 31 – 35 :
هذه هي متغيراتنا الأعضاء في القسم الخاص و هي التي ستحفط لي <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب : الرقم stID و الاسم Name و مصفوفة حجمها 6 خانات لحفظ درجات 6 مواد Quize[QuizeSize] و درجة نصف السنة Mid و درجة نهاية السنة Final و متوسط كل الدرجات Average و أخيرا تقدير الطالب Grade، لاحظ أننا استعملنا أسماء واضحة تدل على الغرض من استخدام المتغير – أو الدالة – و هذا له أهمية كبرى في تسهيل فهم البرنامج و تعديله أو صيانته لاحقا و خاصة في البرامج الكبيرة . طبعا هذه المتغيرات ليست ثابتة أي يتم تحديدها على حسب رؤية المبرمج الذي يقوم ببرمجة المشروع حسب رغبة العميل ...
بهذا نكون قد صممنا الكلاس Student بصورة جيدة يمكن قراءته أي الكود و فهمه و بالتالي تعديله بسهولة و في زمن وجيز J ، نأتي الآن إلى الملف التنفيذي للكلاس كما يلي:

(قم أيضا بعمل ملف جديد و اضافته للمشروع بالاسم StudentClass.cpp )



حمل ملف الكود من هنا

تحليل :
السطر 2:
قمنا بتضمين ملف رأس الكلاس StudentClass.h و قد وضعناه بين علامتي تنصيص " " حتى يعرف المترجم أنه موجود في نفس المجلد الخاص بالمشروع .
السطر 4:
أعلمنا المترجم باستخدامنا لمساحة الأسماء التي سبق و أعلنا عنها في رأس الكلاس بالاسم studentDB .
الأسطر 6 – 13 :
تنفيذ (متن) دالة البناء و فيها نقوم بتصفير (تهيئة) المتغيرات الخاصة و يتم ذلك في كل كائن يتم انشاؤه من هذا الكلاس تمهيدا للإستخدام.
السطر 15 :
دالة الهدم .
السطر17 :
أولى دوال الـSetter هذه الدالة نمرر لها وسيط من النوع long – عدد صحيح طويل – عبارة عن رقم الطالب يتم اسناد قيمته للمتغير الخاص stID بعبارة أخرى تقوم الدالة يتخزين القيمة الممررة في المتغير الخاص stID .
السطر 19 :
هذه الدالة مثل الدالة السابقة و لكن نمرر لها وسيط من النوع string هو اسم الطالب .
السطران 21 – 22 :
مثل الدالتان بالأعلى إلا أننا استخدمنا تكرار for لنسخ القيم من المصفوفة quize[] التي قمنا بتمريرها للدالة إلى المصفوفة العضو بالكلاس Quize[] و استفدنا هنا من الثابت QuizeSize الذي سبق الإعلان عنه في رأس الكلاس – بالأعلى - سطر 7 و هو يحدد عدد المواد بـ6 مواد
السطر 24 و 26 :
هذان السطران هما دالتان Setter كما الدوال السابقة و وظيفتهما : الأولى تعيين قيمة المتغير الخاص Mid و الثانية تعيين قيمة Final عن طريق القيمة التي يتم تمريرها لكل منهما من النوع int .
الأسطر 28 – 34 :
الدالة SetAverage()و وظيفتها تعيين قيمة المتوسط لكل الدرجات و هي كما ترى لاتحمل أي وسيط و بالتالي لن نمرر لها قيمة المتوسط بل تقوم هي بحسابه ثم تخزين قيمته في المتغير العضو Average من النوع floatأي عدد ذو فاصلة عشرية ، طبعا المتوسط يعني مجموع الأعداد على عددها أي مجموع درجات المواد (6 مواد + نصف السنة + نهاية السنة ) على عددها أي 8 .
الأسطر 36 – 44 :
الدالة SetGrade() و هي كالدالة السابقة تقوم بتعيين تقدير الطالب حسب متوسطه باستخدام if – else كما هو واضح و لا نمرر لها أي قيمة .
كل دوال الـSetter أعلاه تعيد قيمة من النوع void أي لا شيء و ذلك لطبيعة وظيفتها ...
الأسطر 46 – 58 :
دوال الـGetter و هي 7 دوال وظيفة كل واحدة ارجاع قيمة المتغير الخاص الذي تتبع له كما سترى عند اختبار الكلاس. قم الآن بترجمة هذا الملف فقط (Ctrl+F9) بحيث أننا كلما نقوم بعمل ملف تنفيذي نقوم بترجمته و في النهاية سنحتاج فقط لترجمة الملف main ثم تنفيذ البرنامج .

اختبار الكلاس Student :
من الأفكار الجيدة في البرمجة هي تقسيم المشروع إلى أجزاء أو مراحل و عند نهاية كل جزء القيام بعمل اختبار لهذا الجزء للتأكد من عمله بكفاءة و في الحقيقة فللإختبار أنواع كثيرة لن نخوض في تفاصيلها و يكفينا الآن عمل برنامج مبسط نستعمل فيه الكلاس Student لإدخال <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات طالب ثم عرضها فقط لنطمئن أن كل شيء على ما يرام حتى هذه اللحظة JJ :

قم بكتابة الكود التالي في ملف main – و الذي سيختلف شكله من مرحلة إلى أخرى وصولا إلى نهاية البرنامج - :


حمل ملف الكود من هنا

التحليل :
السطر 3:
ضمنّا المكتبة stdlib.h و هذه هي الطريقة القياسية المتفق عليها – مؤخرا ! – لتضمين المكتبات التقليدية – و المستخدمة أساسا في لغة سي C و قامت لغة سي بلس بلس بوراثتها - بحيث تكتب على الشكل <cstdlib> يتم اضافة الحرف c و حذف الامتداد .h ، ملاحظة يقوم المترجم Dev-C++ بتضمينها تلقائيا حيث أنها تعرِّف الدالة system("PAUSE") المستخدمة لتثبيت الشاشة.
الأسطر 4 و 5 :
تضمين المكتبة iostream لعمليات الادخال و الاخراج و تضمين ملف رأس الكلاس StudentClass.h .
الأسطر 7 و 8 :
و فيهما أخبرنا المترجم باستخدامنا لمساحة الأسماء القياسية std بالإضافة لمساحة الأسماء الخاصة بنا studentDB .
السطر 12 :
أعلنا عن الكائن st من الكلاس Student .
الأسطر 15 – 22 :
نقوم فيها باستدعاء دوال الـSetter الخاصة بالكائن – باستعمال معامل النقطة . – أي اسم الكلاس نقطة ثم اسم الدالة مثلا st.SetID(111);و مررنا لهذه الدالة قيمة عبارة عن رقم صحيح هي رقم الطالب سيقوم المترجم باستدعاء الدالة و تنفيذها و بالتالي تخزين الرقم 111 في المتغير الخاص stID و لو حاولت الوصول مباشرة لهذا المتغير مثلا : st.stID = 111; فسيعطيك المترجم رسالة خطأ بأنه لايمكن الوصول إلى المتغير الخاص stID . و هكذا في بقية دوال الـSetter نقوم باستعمالها لإدخال <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب إلى المتغيرات الأعضاء ما عدا الدالتان st.SetAverage() و st.SetGrade() حيث لا نمرر لهما أي قيمة و إنما تقومان بحساب المتوسط و التقدير من القيم التي سبق إدخالها ثم تخزنهما في المتغيران Average و Grade كما سبق الكلام عنهما .
الأسطر 25 – 32 :
فيها نقوم بعرض البيانات التي سبق إدخالها و نقوم بذلك باستخدام دوال الـGetter ...
السطر 34:
ايقاف الشاشة مؤقتا للإطلاع على النتائج .

الآن قم بترجمة و تنفيذ البرنامج لتحصل على الآتي :



واو!! كما ترى تمت عملية الاختبار المبسطة بنجاح ! و كلاسنا يعمل بكفاءة و الحمد لله فقد قام بعرض القيم التي قمنا بتمريرها له كما قام بحساب المتوسط Average و التقدير Grade و عرضهما على الشاشة ...

الطبقة الثانية : dBase
هذه الطبقة تقوم بانشاء قاعدة البيانات و تقوم بادارتها مثل الاضافة و الحذف و التعديل ... الخ .
قاعدة البيانات هذه تقوم على القوائم المتصلة Linked Listو هي كما نعلم : هيكلة البيانات في شكل سلسلة متصلة من السجلات (العقد) كل سجل (عقدة) يحتوي على الأقل على حقلين الأول لتخزين البيانات الهدف و الثاني عبارة عن مؤشر يُشير لموقع العقدة التالية.
هنا سيكون الحقل الأول كائن st من الكلاس Student أما الحقل الثاني فهو مؤشر للعقدة التالية كما سبق.
في الحقيقة كان يمكننا استخدام المصفوفات arrayو لكن القوائم المتصلة تتميز عنها بعدة مميزات لعل من أهمها شيئان الأول: استعمال أفضل للذاكرة و الثاني : إدخال عدد غير محدد من <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب ، و ليس هذا موضع تفصيل ذلك ! .

الآن لتقم بعمل ملف جديد و اضافته للمشروع و هو ملف رأس الكلاس dBase بالاسم dBaseClass.h كما يلي :




حمل ملف الكود من هنا
تحليل :
السطر 5 :
تضمين لملف رأس الكلاس Student حيث أننا سنحتاج لاستخدام هذا الكلاس في كلاسنا الجديد هذا لذلك لابد من تضمين ملف الرأس الخاص به.
الأسطر 11 – 14:
هنا و في القسم العام أعلنا عن سجل structure باستخدام الكلمة المحجوزة struct اسمه – أي السجل : student – انتبه فهذا مختلف عن اسم الكلاس Student - ، هذا السجل يحتوي على حقلين – كما سبق – أولهما هو عبارة عن كائن st من الكلاس Student و الثاني هو مؤشر اسمه next من نفس نوع السجل حيث سنستخدمه لاحقا للإشارة إلى عنوان السجل (العقدة) التاليـ(ة)
الأسطر 16 و 19 :
دالتي البناء و الهدم .
السطر 22 :
دالة تعيد قيمة من النوع int و اسمها AddStudent نمرر لها أربع متغيرات أولها من النوع string و البقية أعداد صحيحة intاحداها مصفوفة [] . هذه الدالة كما هو واضح من اسمها مهمتها إضافة طالب جديد إلى قاعدة البيانات كما سترى عند الكلام عن متنها ( ملفها التنفيذي ) .
السطر 23:
دالة أخرى من نفس النوع اسمها EditStudent و نمرر لها نفس متغيرات الدالة السابقة مع اضافة متغير آخر من النوع long ، عمل الدالة هو تعديل (تحرير) <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات طالب موجود مسبقا بقاعدة البيانات .
السطر 24 :
دالة ثالثة اسمها DeleteStudent أي حذف طالب موجود بقاعدة البيانات ، هذه الدالة تاخذ وسيطا واحدا من النوع long .
السطر 25:
دالة رابعة اسمها ChecID سنستخدمها عند الحذف أو التعديل للتأكد من صحة رقم الطالب المدخل بواسطة المستخدم قبل القيام بحذف أو تعديل كما سترى يعد قليل.
هذه الدوال الأربع يمكن اعتبارها دوال عمليات قاعدة البيانات Database Operationكما عنونّاها باستخدام الملاحظة بالسطر 21 و الغرض هو تسهيل و تبسيط الفهم لقاريء الكود ، و الدول التالية هي لعرض البيانات لذلك وضعنا ملاحظة : Get information في السطر 27 .
السطر 28 :
الدالة DisplayAll لعرض <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات جميع الطلاب.
السطر 29:
الدالة DisplayPass لعرض <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات جميع <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب الناجحين فقط .
السطر 30 :
الدالة DisplayFail لعرض <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات جميع <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب الراسبين .
الأسطر 31 و 32 :
دالتان للبحث عن الطالب اولاهما للبحث عن طريق رقم الطالب لذلك نمرر لها وسيطا من النوع long و الأخرى و بنفس الاسم للبحث عن طريق اسم الطالب لذلك نمرر لها وسيطا من النوع string ، لاحظ استخدامنا لمفهوم زيادة التحميل Overloading لهاتين الدالتين ...

كل الدوال أعلاه في القسم العام من الكلاس و نأتي الآن إلى القسم الخاص :
السطر 34:
متغير fID من النوع longعن طريقه نحدد أول رقم للطالب حيث أن البرنامج يجب أن يقوم بتوليد رقم لكل طالب بصورة آلية (وهذا أسهل للمستخدم ) ، لذلك و كما سترى لاحقا سنستخدم هذا المتغير لتحديد رقم البداية ثم نقوم بزيادته بمقدار 1 عند اضافه طالب و العكس – أي انقاصه بمقدار 1 – عند الحذف .
السطر 35 :

مؤشر اسمه Head من نفس نوع السجل student هذا المؤشر في الواقع هو أهم متغير في كلاسنا هذا حيث أنه يُشير إلى أول عقدة في السلسلة المتصلة التي تحوي بياناتنا و بالتالي إذا أردنا الوصول إلى أي سجل (عقدة) في هذه السلسلة فلا يمكن ذلك إلا عن طريق هذا المؤشر !! لذلك هو مهم جدا جدا ، و إليك هذا المخطط المبسط لشكل السلسلة المتصلة و موقع المؤشر Head منها :




نأتي الآن أعزائي إلى الملف التنفيذي للكلاس dBase و الذي سنكتب فيه متون دواله كالتالي :



حمل الكود من هنا

تحليل الكود :
السطر 4:
تضمين ملف الرأس dBaseClass.h .
السطر 8 :
دالة البناء الخاصة بالكلاس و التي سيتم استدعاءها عند إنشاء كائن من الكلاس ، و كما ترى تم تهيئة المتغيرات الخاصة : فالمؤشر Head سنعطيه القيمة NULLأي خالي و ذلك للدلالة على أن قاعدة البيانات (السلسلة المتصلة ) خالية و سيتم تغيير هذه القيمة عند اضافة <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب لاحقا ، المتغير الخاص الآخر fID تم إعطاءه القيمة 1000 (و هي اختيارية حسب مزاجك ) و ذلك لاخبار الكائن أن الأرقام – أي أرقام <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب stID – سيبدأ عدها من هذا الرقم بحيث يتم زيادته بمقدار 1 عند إضافة طالب جديد و بالعكس : إنقاصه بمقدار 1 عند حذف طالب ...
السطر 10:
دالة الهدم التي سيتم استدعاءها عند تدمير الكائن أو عند خروجه من مداه ، هذه الدالة ستقوم بحذف المؤشر Headمن الذاكرة – و بالتالي حذف كامل السلسلة - و في الحقيقة فهذه مسألة مهمة جدا جدا لتجنب الوقوع في فخ تسرب الذاكرة ، و في لغة سي بلس بلس فهذه مسئولية المبرمج بخلاف لغة جافا و ليس هذا موضع تفصيل ..
الأسطر 12 – 45 :
هذه أول دالة عضو بالكلاس و اسمها AddStudent() و كما هو واضح فهي تقوم بإضافة طالب جديد لقاعدة البيانات كالتالي :
14 : في هذا السطر أعلنا عن مؤشرين current و linker و هما من نفس نوع السجل student .. فائدة المؤشرين ستتضح بعد قليل حيث سنستخدمهما لإنشاء عقدة جديدة و ربطها بالعقدة التالية في كل مرة يتم استدعاء الدالة لإضافة سجل جديد و هما مؤشران مؤقتان في الواقع (على الرصة stack) بحيث سيقوم المترجم بتدميرهما آليا عند الوصول لقوس نهاية الدالة و بالتالي فلا حاجة لحذفهما باستخدام المعامل delete.
15 : زيادة المتغير الخاص fID بمقدار 1 ، لاحقا سنقوم بتمرير هذا المتغير – بعد زيادته – إلى الدالة SetID() و هي عضو بالكلاس Student و ذلك لوضع رقم الطالب .
16 : سنختبر هنا هل Head = NULL باستعمال عبارة الشرط if، فإذا كان كذلك (أي قيمة الشرط صواب ) فهذا يعني أن هذه أول عقدة في السلسلة و عند ذلك ينفذ الكود الخاص بإنشائها و وضع البيانات بها في الأسطر 18 – 26 .
18 : نقوم بإنشاء العقدة الأولى عن طريق المعامل new (طبعا هذه العقدة تحتوي على حقلين الأول هو كائن st من الكلاس Student و الثاني هو مؤشر لموقع العقدة التالية ) ثم يخزن عنوان (موقع ) هذه العقدة – بالذاكرة – في المؤشر Head و قد ميزنا العقدة الأولى هنا لأنها الأهم ، و في الأسطر التالية – حتى سطر 26 يتم وضع <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب عن طريق دوال الـSetter التي سبق الكلام عنها في الكلاس Student .
26 : في هذا السطر نعطي المؤشر next في هذه العقدة قيمة أولية NULLو هي أولية لأنه سيتم تغييرها عند إضافة عقدة أخرى و لكن الآن لابد من التأكد من جعلها خالية حتى لايكون بها عنوان قديم و غير مقصود بالذاكرة مما قد يسبب أخطاءً كبيرة عند التعامل مع السلسلة بالحذف أو الاضافة أو غير ذلك.

30 : هذا السطر و الأسطر اللاحقة له في هذه الدالة هي قسم else التابعة للشرط if السابق و سيتم تنفيذه إذا لم يكن المؤشر Head خالي (أي كانت قيمة الشرط بالأعلى خطأ ) أي أن السلسلة ليست خالية ( لاحظ هنا فائدة تهيئة المؤشر Head في دالة البناء ) و في هذا السطر يتم اسناد قيمة المؤشر Head إلى المؤشر المؤقت linker و الغرض من ذلك أننا لا نريد تغيير قيمة المؤشر Headلأنه مفتاح للوصول لكامل السلسلة كما سبق و إذا غيرنا قيمته فهذا يعني ضياع كامل السلسلة و عدم تمكننا من الوصول لها بعد ذلك ، لذلك نستعمل هذه الخدعة البسيطة : نحن بحاجة لعنوان أول عقدة بالسلسلة ، نقوم بتخزين هذا العنوان في مؤشر مؤقت هو linker بحيث نغير قيمته هو لاحقا و نحافظ بذلك على قيمة المؤشر الأهم Head .
31 – 32 : هنا نستعمل التكرار للوصول إلى عنوان آخر عقدة بالسلسلة – باستخدام المؤشر linker - و ذلك تمهيدا لإضافة عقدة جديدة و إلحاقها بذيل السلسلة المتصلة كما سترى بعد قليل.
33 : نقوم بإنشاء سجل (عقدة ) جديد(ة) و تخزين عنوانه في المؤشر current و في الأسطر اللاحقة نقوم بوضع <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب الجديد باستخدام دوال الـSettre كما بالأعلى .
42 : هذا السطر هو الذي يقوم بعملية ربط العقدة الجديدة بالعقدة الأخيرة بالسلسلة و عنوانها موجود بالمؤشر linker ( حسب الأسطر 31 – 32 ) حيث سنضع بالمؤشر next في هذه العقدة عنوان عقدتنا الجديدة و التي عنوانها محفوظ في المؤشر current .
44 : هنا و بعد الانتهاء بنجاح ستعيد الدالة القيمة 0 للدلالة على نجاح العملية ( إضافة طالب جديد ) .

الأسطر 47 – 69 :
متن دالة أخرى هي EditStudent() و وظيفتها هي تحرير – تعديل – <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات طالب سبق إدخاله و هي تسمح بتعديل كل البيانات ما عدا رقم الطالب .
49 : لابد أولا من اختبار قيمة المؤشر Headللتأكد من أنه ليس خاليا – أي أن السلسلة ليست فارغة – فإذا كان خاليا فإن الدالة لن تفعل شيئا ، و إذا لم يكن خاليا يقوم بتنفيذ بقية كود الدالة .
51 : نعلن هنا عن مؤشر مؤقت ptr من نفس نوع السجل student و نعطيه قيمة المؤشر Head حيث سنستعمل هذا المؤشر للتنقل خلال العقد ( السجلات) في السلسلة المتصلة .
53 – 57 : هذا التكرار وظيفته هي البحث عن الطالب المراد تعديله بدلالة رقم الطالب و الغرض من ذلك هو معرفة عنوان عقدة هذا الطالب بحيث نتمكن من تعديل البيانات بدلالة المؤشر ptr .
58 – 67 : عند إيجاد الطالب فإن المؤشر ptrلن يكون خاليا بل سيحتوي عنوان سجل الطالب المراد تعديل بياناته و بالتالي فسيقوم بالتعديل باستخدام القيم الجديدة التي تم تمريرها للدالة – و لن يتم تعديل رقم الطالب كما سبق- .

71 – 93 : هذه دالة الحذف DeleteStudent()و وظيفتها بكل بساطة هي حذف سجل الطالب و لكن لابد من معالجة ذلك بحيث لا يحصل انقطاع في السلسلة بسبب حذف عقدة بها كما سترى ، و سنمرر لهذه الدالة وسيطا واحدا هو رقم الطالب المراد حذفه .
73 : التأكد من عدم خلو المؤشر Head و قد سبق الكلام عن ذلك .
75 : نعلن هنا عن مؤشران جديدان أولهما parent و سنخزن به عنوان (والد) العقدة المراد حذفها (أي العقدة السابقة لها) و المؤشر الثاني ptr و سنعطيه قيمة المؤشر Head – عنوان أول عقدة – ثم سنستخدمه لاحقا للتنقل داخل السلسلة .
76 – 77 : في هذا الشرط سيختبر –باستخدام if - إن كانت العقدة المراد حذفها هي العقدة الأولى – و التي يشير إليها المؤشر Head فإذا كان كذلك ( أي صواب ) فسيقوم بكل بساطة بتغيير المؤشر Head بحيث يضع به عنوان العقدة التالية . و إلا فسينفذ مقطع else كما يلي:
78 – 89 : هذا الجزء من الكود سيتم تنفيذه في حالة لم تكن العقدة المراد حذفها هي العقدة الأولى – و التي دائما نميزها بالذكر كما لاحظت لأهميتها - .
80 – 86 : هذا التكرار وظيفته هي البحث عن العقدة السابقة للعقدة المراد حذفها و تخزين عنوانها في المؤشر parentبحيث نستفيد من ذلك بعد قليل في ربط العقدة السابقة بالعقدة التي تلي العقدة المراد حذفها (العقدة الهدف ) ! و ذلك حتى لا تنقطع السلسلة عند حذف عقدة منها .
87 : نعم هذا السطر هو الذي يقوم بعملية الربط المشار إليها بالأعلى ( حيث أن العقدة السابقة يتم ربطها بالعقدة اللاحقة تمهيدا لحذف العقدة الهدف في السطر 88 ) .
90 : يتم انقاص المتغير fID – رقم الطالب - بمقدار 1 و ذلك بعد نجاح عملية الحذف .
و هنا سؤال ذكي : افترض أن رقم الطالب – و الذي مررناه لدالة الحذف هذه – غير صحيح ، مما سيسبب مشكلة أو عدة مشاكل أقلها إنقاص رقم الطالب فما الحل ؟ الجواب : لن نستدعي دالة الحذف – أو دالة التعديل – إلا بعد التأكد من أن رقم الطالب صحيح و ذلك باستخدام دالة عضو مخصصة لذلك ChecID() :

95 – 114 : هنا متن الدالة ChecID() و وظيفتها هي التأكد من أن رقم الطالب الذي أدخله المستخدم هو رقم صحيح ( أي موجود بقاعدة البيانات ) .
97: أعلنا عن متغير صحيح اسمه ReturnCode و كما هو واضح من اسمه سيحمل القيمة التي سترجعها الدالة و أعطيناه قيمة أولية -1 ( و التي ستعني أن قاعدة البيانات خالية ) .
101 – 109 : هذا التكرار سيقوم بالبحث عن رقم الطالب فإذا وجده ستتغير قيمة المتغير ReturnCode إلى 1 ( سطر 105 ) .
110 – 111 : إذا لم يجده (ptr = NULL) ستتغير قيمة ReturnCode إلى 0 .


113 هنا تقوم الدالة بارجاع قيمة المتغير ReturnCode، إذن و باختصار فهذه الدالة سترجع إحدى ثلاث قيم : -1: للدلالة على أن قاعدة البيانات خالية ، 1: للدلالة على أن رقم الطالب صحيح – أي موجود بقاعدة البيانات - ، 0: للدلالة على أن رقم الطالب غير صحيح – أي غير موجود - ، و سنستفيد بشكل كبير من هذه القيم لاحقا عند حذف أو تعديل سجل طالب كما سترى بحول الله عند اختبار الكلاس dBase .

116 – 141 : هذه دالة بالاسم DisplayAll() أي عرض جميع <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب و هي بكل بساطة تقوم بالمرور على كل عقدة في السلسلة بدلالة المؤشر ptr حيث تعرض <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات كل عقدة (كل طالب ) و لا أعتقد أنها تحتاج إلى مزيد شرح!.

143 – 171 : الدالة DisplayPass() و هي كالسابقة إلا أنها تقوم بعرض <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب الناجحين فقط حيث نختبر تقدير كل طالب فإذا لم يكن "F" – أي راسبا يتم عرضه ( الاختبار في سطر 151 ) .

173 – 201 : و هذه الدالة DisplayFail() و هي كسابقتها إلا أنها تعرض <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب الفاشلين ! ، قصدي: الراسبين J
.

203 – 264 : هنا دالتان أولاهما GetStudent() نمرر لها وسيطا واحدا StudentID من النوع longو وظيفتها هي البحث عن طالب بدلالة رقمه و عند إيجاد الطالب يتم عرض بياناته و إلا فستعرض الدالة رسالة تفيد أنها لم تجد طالبا بالرقم المذكور أما الدالة الأخرى فهي بنفس الاسم حيث تقوم بنفس الوظيفة : البحث عن طالب و لكن بدلالة اسمه هذه المرة و كلا الدالتان تقوم بعملهما عن طريق التفتيش عن الرقم أو الاسم في كل عقدة و عند التطابق تقوم بعرض كل <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب المطلوب .
انتهينا – حتى الآن – من الكلاس الثاني dBase الخاص بإنشاء و إدارة قاعدة <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب دعونا الآن نقوم باختبارها للتأكد من عملها بصورة مقبولة ثم نستمر في عملية بناء المشروع J .

اختبار الكلاس dBase :

قم بتعديل الملف main الذي قمنا باختبار البرنامج عن طريقه - بعد كلامنا عن الكلاس - Student


ليصبح كالتالي:








التحليل :
الأسطر 10 – 22 :
هذه دالة عامة – ليست عضوا في الكلاس – و قد أخذتها من صندوق الأدوات tool boxJ الخاص بي (و الذي يضم مجموعة متميزة من الدوال و الكلاسات) و أضفتها هنا و اسمها HandleNotaNumberError() ، وظيفتها التصدي لخطأ إدخال قيمة غير رقمية في متغير رقمي من خلال الكائن cin و سيتم استدعائها عند كل إدخال إذا حدث فشل في الكائن cin – بسبب إدخال قيمة غير رقمية في متغير رقمي – حيث تتم معالجة الفشل عن طريق الدالة cin.clear()و يتم تنبيه المستخدم حتى يحاول الادخال مرة أخرى ، و سيتكرر الأمر إذا أدخل قيمة خاطئة مرة أخرى و هكذا .. سنقوم بتطوير هذه الدالة لتعمل من خلال الاستثناءات عند تصميمنا لكلاس يمثل واجهة المستخدم لاحقا بحول الله ...

السطر 26 :
هنا أعلنا عن الكائن db من الكلاس dBase و هذا الكائن هو الذي سيقوم بكل العمل و ما علينا إلا إرشاده و توجيهه بالأوامر J .

السطر 27 :
استعملنا التكرار for(;و هو يجعل البرنامج يكرر الكود التالي – بين القوسين سطري 28 و 179 – دائما بحيث لا يخرج من التكرار إلا بجملة شرطية ( بطريقة مشابهة للـ while ) الغرض من ذلك أن البرنامج سيعرض قائمة خيارات للمستخدم ليختار منها ، عند الاختيار يذهب البرنامج لتنفيذ ما تم اختياره ثم يرجع إلى نفس قائمة الخيارات مستعدا لتلقي أوامر أخرى حتى ينفذها و يرجع مرة أخرى للقائمة الرئيسية و هكذا دائما لحين الخروج من البرنامج .

السطر 29 :
مسح الشاشة و الغرض من ذلك هو أن يكون البرنامج منسقا بصورة جميلة تعجب المستخدم مما يرفع أسهم البرنامج لديه.

الأسطر 31 – 40 :
هذه هي قائمة البرنامج الرئيسية بها 9 خيارات : 1. لإضافة طالب جديد ، 2. تعديل <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات طالب 3. حذف سجل طالب 4. البحث عن <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات طالب بدلالة اسمه 5. البحث عن <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات طالب بدلالة رقمه 6. عرض <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات جميع <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب 7. عرض <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الناجحين فقط 8. عرض <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الراسبين 9. الخروج من قاعدة البيانات ، و يتم توجيه المستخدم للإختيار .

الأسطر 43 – 44 :
هنا ستم استدعاء دالة HandleNotaNumberError() في حالة حدوث فشل في الكائن cin بسبب إدخال قيمة غير رقمية في المتغير الرقمي choice و الذي يفترض أن يأخذ القيمة بين 1 – 9 التي تمثل اختيار العميل تمهيدا لتنفيذ الإختيار عن طريق أمر الـ switch .

الأسطر 45 – 46 :
الإعلان عن دوال مؤقته تستخدم لإدخال <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب تمهيدا لتمريرها للكائن db من خلال الدوال الأعضاء ..

السطر 48 :
عبارة switch و تعمل على اختبار المتغير choice و من ثم يتم تنفيذ الجزء الخاص بقيمة المتغير من خلال case ..

الأسطر 50 – 74 :
في حالة اختار المستخدم الخيار 1 – إضافة طالب جديد - و بالتالي كانت قيمة المتغير choice هي 1 فسيتم تنفيذ هذا المقطع من الكود و فيه يقوم المستخدم بإدخال <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب الجديد ثم يقوم الكائن بإضافته عن طريق الأمر (db.AddStudent()) و بعد ذلك يتم الرجوع مرة أخرى للقائمة الرئيسية .

الأسطر 75 – 111 :
و هذا الجزء سيتم تنفيذه في حالة اختيار الخيار رقم 2 – تعديل <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات طالب – حيث سيتم أولا التأكد من رقم الطالب ( التأكد من وجود الطالب بقاعدة البيانات ) عن طريق الدالة العضو بالكائن (db.ChecID()) ثم يتم التعديل بإدخال البيانات الجديدة و استدعاء دالة التعديل العضو في الكائن (db.EditStudent()) لتقوم بالتعديل المطلوب.

الأسطر 112 – 133 :
هنا ستم تنفيذ الجزء الخاص برقم 3 و هو حذف سجل الطالب و بنفس طريقة الجزء السابق سيتم أولا التأكد من رقم الطالب الذي أدخله المستخدم و في حالة كان الرقم صحيحا يتم حذف سجل الطالب عن طريق الدالة (db.DeleteStudent()) . و هكذا بقية الأجزاء حتى النهاية و في الحقيقة لا أريد الاطالة أكثر و إنما قم بترجمة و تنفيذ كامل المشروع و اختبار النتائج للتأكد من عمل البرنامج بصورة سليمة قدر المستطاع .

الطبقة الثالثة : RW2File

هذا الكلاس وظيفته هي تسجيل <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب على القرص و ذلك تجنبا لضياع هذه البيانات عند إغلاق البرنامج ، ثم و عند تشغيل البرنامج مرة أخرى يقوم بقراءة هذه البيانات التي سبق تسجيلها على القرص و بالتالي يُدخلها في قاعدة البيانات ( السلسلة المتصلة ) مرة أخرى حتى نستطيع التعامل معها ( عرض ، تعديل ، حذف ...الخ ) لذلك سيقوم هذا الكلاس بوراثة الكلاس السابق dBaseليؤدي مهمته بنجاح ، و هي مهام الكلاس dBase بالإضافة إلى مهام أخرى جديدة ( الوراثة من المفاهيم المهمة و المستخدمة في تطوير البرمجيات في الـoop ) و سنحتاج لتجهيز الكلاس dBase حتى يصبح هو الكلاس الأساس Base class الذي سيرثه كلاسنا الجديد RW2File بحيث يصبح ملف الرأس كالتالي :











حمل الملف من هنا


ستلاحظ تغييرات في بعض الأسطر كالتالي :

سطر :20
تمت إضافة الكلمة المحجوزة virtual (ظاهري أو خيالي ) قبل دالة الهدم ~dBase() – لإعلام المترجم أننا نريد تجاوزها (عدم استخدامها) - و ذلك للتأكد من أن عملية تدمير الكلاس RW2File ستتم بصورة آمنة باستخدام دالة الهدم الخاصة به – و متنها هو نفسه متن الدالة ~dBase() حيث تقوم أيضا بحذف المؤشر Head ، و إذا لم نفعل هذا – أي اضافة virtual – فإن دالة البناء في الكلاس الأساس dBase قد يتم استدعاؤها عندما يتم تدمير الكلاس المشتق RW2File باستخدام المؤشر Head ( و الذي هو أصلا عضو في الكلاس dBase ) عن طريق الأمر delete و قد لايتم تحرير أي مساحة تم حجزها من الذاكرة ، لذلك كان لابد من هذا التعديل .

سطر 23 :
تم الإعلان عن دالة صديقة – و الدالة الصديقة يتم الإعلان عن رأسها فقط داخل الكلاس باستعمال الكلمة المحجوزة friend ، فهي ليست عضوا فيه ! و يتاح لها الوصول لكل عناصر الكلاس ! – و هذه الدالة و اسمها operator<< (المعامل >> ) و ستقوم بزيادة تحميل المعامل >> بحيث يقوم بعرض كل <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب بدلالة مؤشر يشير لسجل الطالب و سترى ذلك بالتفصيل في الملف التنفيذي ( و هذا التعديل في الواقع لا دخل له بعملية تجهيز الكلاس dBase لعملية الوراثة و إنما هو فكرة لتحسين الكود باستخدام زيادة تحميل المعاملات Operator Overloading سترى نتائجها بعد قليل ، و قد وضعتها هنا حتى تتمكن من المقارنة بين الكلاس قبل التعديل و بعد التعديل ) .

سطر 26 :
في الدالة العضو AddStudent() قمنا بعمل تغييرين أو لهما أننا جعلنا القيمة التي تعيدها الدالة هي إشارة من نفس نوع السجل dBase::student (و قد كانت في السابق تعيد عددا صحيحا int !) ما الغرض من ذلك ؟ الجواب أن الكلاس المشتق RW2Fileيقوم - كما سبق ذكره - بكتابة سجلات <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب إلى القرص الصلب و أيضا قراءتها منه ، و لتنفيذ عملية الكتابة يجب أولا أن يقوم المستخدم بإدخال <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب إلى قاعدة البيانات ثم و مباشرة يتم كتابة السجل الجديد على القرص ، سنستخدم – في الكلاس المشتق - دالة لكتابة السجل على القرص – كما سترى لاحقا - و لكن هذه الدالة ستحتاج لعنوان كل سجل على حدة حتى تستطيع الوصول لبياناته لتسجيلها لذلك تم تعديل الدالة AddStudent() بحيث ستعيد عنوان كل سجل يتم إدخاله في كل مرة .
التغيير الثاني في الدالة AddStudent() هو إضافة الكلمة المحجوزة virtual لإعلام المترجم أننا نريد تجاوز هذه الدالة بحيث أننا لن نستطيع استدعاءها إلا عن طريق دالة عضو في الكلاس RW2Fileلماذا ؟ لأن الوراثة تتيح لك إضافة تحسينات لدوال الكلاس الأساس عن طريق تجاوز الدالة المراد تحسينها و إنشاء دالة أخرى بنفس الاسم في الكلاس المشتق و تكون بنفس اسم الدالة التي تم تجاوزها في الكلاس الأساس (حيث أن الكلاس المشتق لا يستطيع تعديل الكلاس الأساس). قد يبدو لك هذا الكلام غامضا الآن و لكن ستتضح الرؤية أكثر عند الكلام بتفصيل عن الكلاس المشتق لاحقا بحول الله.

سطر 30 :
أضفنا دالة جديدة ChecName() و مشابهة للدالة الأخرى ChecID()، هذه الدالة الجديدة ستعيد القيمة 1 إذا كان الاسم الذي مررناه لها موجودا بقاعدة البيانات – كما سترى في المتن – هذه الدالة سنستخدمها حتى لا نسمح للمستخدم بإدخال اسم لطالب جديد و هذا الاسم سبق إدخاله بحيث أن أسماء <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب – كأرقامهم – لا بد أن تكون فريد – مختلفة – ( هذا التعديل للكلاس dBase هو أيضا تعديل لتحسين عمل الكلاس و لا علاقة له بعملية تجهيزه للوراثة ..).

سطر 38 :
وضعنا المتغيرات الأعضاء (fID و Head) – و التي كانت في القسم الخاص private - وضعناها في القسم المحمي protected و ذلك حتى يتمكن الكلاس المشتق من الوصول لها ...


و الآن قم بتعديل الملف التنفيذي للكلاس الأساس


ليصبح كالتالي :









حمل ملف الكود من هنا


الأسطر 13 – 27 بها الدالة الصديقة operator<<()– و هي ليست عضوا في الكلاس لذلك لم نضع اسم الكلاس قبلها – متبوعا بالمعامل :: - كما فعلنا مع الدوال الأخرى ، هذه الدالة و بكل بساطة تقوم بزيادة تحميل المعامل >> باستخدام كائن os من الكلاس ostream (مكتبة iostream) بحيث يعرض لي <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب بدلالة مؤشر ptr من نوع السجل dBase::studentبعبارة أخرى إذا استعملت العبارة cout << ptr; ( ptr مؤشر يُشير إلى سجل ) فسيتم عرض <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب المخزنة في السجل الذي يشير إليه المؤشرptr و سنقوم باستخدام ذلك في أربع دوال ! : DisplayAll() و DisplayPass() و DisplayFail() و GetStudent() و GetStudent()– النسخة الأخرى منها – (انظر الكود ) فائدة هذا أن زيادة تحميل هذا المعامل مكنتني من تقليل حجم الكود بأكثر من 30 سطرا و جعلته أسهل الآن أو مستقبلا ...

ستلاحظ السطر 43 – في متن الدالة AddStudent() أصبح يعيد قيمة المؤشر Head (في حالة كانت هذه العقدةالأولى في السلسلة ) أما السطر 61 فيعيد قيمة المؤشر current حيث أننا عدلنا نوع البيانات الذي ستعيده الدالة من عدد صحيح إلى إشارة إلى السجل الذي تمت إضافته كما سبقت الإشارة لذلك بالأعلى...

الأسطر 238 – 257 : بها متن الدالة الجديدة ChecName() و هي مشابهة لعمل الدالة ChecID() .


قم بإجراء التعديلات أعلاه على الكلاس الأساس dBase ثم قم بإضافة ملف رأس للكلاس المشتق RW2File للمشروع و اكتب به الكود التالي :










حمل ملف الكود من هنا


تحليل الكود :

السطر 6 :
تضمين المكتبة fstream و التي تضم تعاريف تيارات الإدخال و الإخراج من و إلى الملف تمهيدا لاستخدامها...

السطر 11 :
أعلنا عن الكلاس الجديد RW2File و في نفس السطر أعلمنا المترجم بأن هذا الكلاس سيرث الكلاس الأساس dBase عن طريق كتابة اسمه بعد الرمز : مسبوقا بالكلمة public .
السطر 13 :
أول المتغيرات الخاصة و هو عبارة عن المتغير buffer من النوع الحرفي char بسعة 256 حرفا و سنستخدمه كمخزن مؤقت عند قراءة البيانات من الملف على القرص.

السطر 14 :
الإعلان عن المتغير DataFileWrite من النوع ofstream (ضمن المكتبة fstream) أي تيار إخراج حيث سنستخدم هذا المتغير لفتح الملف المحدد و كتابة البيانات به ثم إغلاقه .

السطر 15 :
الإعلان عن المتغير DataFileRead من النوع ifstream (أيضا ضمن المكتبة fstream) أي تيار إدخال حيث سنستخدمه لفتح الملف و قراءة البيانات ثم إغلاقه .

السطران 19 و 22 :
دالتي البناء و الهدم .

السطر 25 :
دالة ReadData() لقراءة تيار البيانات من الملف و هي من النوع int و تأخذ وسيطا عبارة عن إشارة إلى كائن من نفس الكلاس.

السطر 26 و 27 :
دالة WriteData() لكتابة تيار البيانات إلى الملف و هي من النوع intو تأخذ أربع وسائط و هذه الدالة تقوم بكتابة <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات طالب (سجل) واحد فقط إلى الملف في كل مرة يتم استدعاءها أما الدالة الأخرى ( بنفس الاسم ) و بدون وسائط فتقوم بكتابة كامل السلسلة (كل الطلاب) إلى الملف عند استدعائها ، هذه الدالة الأخرى سيتم استدعاؤها عند نهاية البرنامج بغرض تسجيل كامل البيانات – بعد إجراء أي تعديل عليها - .

السطر 28 :
الدالة AddStudent() و هي أصلا عضو في الكلاس الأساس dBase– بتوقيع مختلف – و قد أعدنا تعريفها هنا حيث سيتم تحسينها حتى تستقبل رقم الطالب أيضا مع بقية بياناته لإضافتها لقاعدة البيانات . التوارث لا يتيح للكلاس المشتق إزالة أي شيء من الكلاس الأساس لذلك يمكننا تجاوز الدالة التي لا نريدها في الكلاس الأساس عن طريق وضع الكلمة المحجوزة virtual قبلها – كما سبق عند الكلام عن تجهيز الكلاس dBase– ثم إعادة تعريفها في الكلاس المشتق و هذا بالضبط هو ما عملناه هنا ! و هكذا أصبح لهذه الدالة تنفيذان أحدهما بالكلاس الأساس و الآخر بالكلاس المشتق و كل تنفيذ سيقوم بعمل مختلف ، تعدد التنفيذات – بنفس الاسم - هذا هو ما يسمى بتعدد الأشكال (تعدد الأوجه ) polymorphism . جميع أعضاء الكلاس dBase (متغيرات و دوال) أصبحت – عن طريق التوارث – أعضاء في الكلاس الجديد RW2Fileيمكن استخدامها مباشرة و عند استدعاءها يستطيع المترجم معرفة مكانها و الوصول لها بدون أي كود إضافي ، و هكذا استفدنا من الكلاس الأساس عن طريق توريثه لكلاس جديد يضمه مع تحسينات و إضافات من دون حاجة لإعادة كتابته من جديد و لعل هذا من أهم فوائد التوريث Inheritance و كما ترى فهو مفيد جدا في تطوير البرامج خاصة البرامج الكبيرة و المعقدة.



نأتي الآن لملف تنفيذ الكلاس كالتالي :










حمل الملف من هنا


تحليل الكود :

الأسطر 8 – 11 :
متن دالة الهدم و تقوم بحذف المؤشر Head و الذي هو في الأصل عضو في الكلاس الأساس ، لاحظ أنها بنفس متن دالة هدم الكلاس الأساس dBase لذا كان لابد من تجاوز دالة الهدم في الكلاس الأساس عن طريق وضع الكلمة virtual قبلها كما سبق ...

الأسطر 13 – 44 :
متن الدالة ReadData() و نمرر لها إشارة لكائن rd من الكلاس RW2File ، و تعمل كالتالي كالتالي :
15 : نقوم باستخدام المتغير DataFileRead لفتح الملف أولا باستخدام الأمر open() حيث نضع بين القوسين اسم الملف بين علامتي تنصيص "DataRecord.dat " ( يمكن وضع مسار الملف كاملا لإعلام المترجم عن مكان وجوده أو ذكر اسم الملف فقط مما يُعلم المترجم أن الملف موجود في نفس مجلد المشروع و إذا لم يكن موجودا فسيقوم بإنشائه!) ، أما الأمر ios::in فهو يُعلم المترجم أن فتح الملف بغرض القراءة منه .
16 : هنا – و بعد فتح الملف – سيتأكد أولا من نجاح فتح الملف عن طريق الأمر is_open() و الذي يعيد القيمة صواب true إذا تمت عملية الفتح و بالتالي سينفذ الكود التالي بين قوسين :
18 – 20 : هنا متغيرات مؤقتة ستستخدم لتخزين البيانات التي ستتم قراءتها من الملف ثم تمريرها لدالة AddStudent() لإضافتها للسلسلة .
21 : تكرار forو سيستمر حتى تتم قراءة جميع البيانات من الملف . طبعا سنكون قد كتبنا البيانات بالملف مسبقا بطريقة محددة بحيث نفصل بين كل حقل و الآخر (كالرقم و الاسم .. ) بالرمز | ثم الرمز * للدلالة على نهاية السجل .

23 : نستخدم المتغير DataFileRead لقراءة أول جزء من البيانات باستخدام الأمر getline() حيث سنضع بين القوسين اسم المتغير الذي سنضع به البيانات التي ستتم قراءتها –و هو المتغير العضو buffer – ثم فاصلة , ثم عدد الأحرف المطلوب قراءتها – و هو عدد تقريبي – ثم و بين العلامتين ' 'نضع مُحدِّد – أيّ رمز تختاره – ، هذا المحدد سيخبر الأمر بحدود الجزء الذي تريد قراءته من البيانات ، بعبارة أخرى : هذا الأمر يقوم بالقراءة من السطر – بالملف – حتى يصل إلى الرمز المحدد أو حتى يصل إلى الرقم المحدد أيهما كان أولا . الغرض من ذلك هو الفصل بين البيانات – الرقم و الاسم و الدرجات .. الخ – بحيث أن كل نوع سيتم تخزينه في متغير مختلف :
24 : استخدمنا المتغير المؤقت studentID لنحفظ فيه قيمة الجزء الأول من سطر و البيانات و الذي هو رقم الطالب و لكن و لأن هذا الجزء من البيانات المخزن في المتغير buffer هو حرفي لذلك لابد من تحويله إلى عدد صحيح int باستخدام الأمر atoi() و الذي يقوم بالتحويل من char إلى int ثم نقوم باسناد الرقم الناتج إلى المتغير المؤقت studentID الخاص بحفظ رقم الطالب .
و ستتكرر هذه العملية ( قراءة البيانات جزءا بعد جزء و تخزينها في متغير مؤقت ) حتى الوصول للسطر :
36 و 37 : سيختبر إن كانت هذه نهاية الملف بحيث يخرج من التكرار for إن كان كذلك .
38: هنا يتم استدعاء الدالة المحسنة AddStudent() عن طريق الكائن rd و الذي مررنا إشارة له إلى دالة القراءة من الملف ، (سيتجاوز المترجم نسخة الدالة AddStudentبالكلاس الأساس و يستدعي النسخة الموجودة بالكلاس المشتق و هو – أي المترجم - واثق من ذلك لأننا عند تعريف هذه الدالة بالكلاس الأساس سبقناها بالكلمة المحجوزة virtual ! و بالرغم من ذلك فيمكننا استدعاء نفس الدالة و لكن من الكلاس الأساس و ذلك في الدالة WriteData()العضو في الكلاس المشتق كما سترى بعد قليل !) ، هذه الدالة سنمرر لها <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب – و المخزنة في خمس متغيرات مؤقته – و وظيفة الدالة هي إضافة هذه البيانات إلى قاعدة بياناتنا (السلسلة المتصلة) و سيتم تكرار ذلك لكل <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب الذين تم تخزين بياناتهم في الملف . سيأتي الكلام عن متن هذه الدالة بعد قليل.
40 : إغلاق الملف بعد إنتهاء القراءة منه .
41 : ستعيد الدالة الرقم 0 للدلالة على نجاح العملية .
43 : أما في حالة الفشل في فتح الملف فستعيد الدالة الرقم -1 ، هذه القيم سنتعامل معها لاحقا عند استعمال الدالة في ملف main .

الأسطر 46 – 66 :
هنا الدالة WriteData() و التي تقوم بكتابة سجل طالب واحد للملف حيث نمرر لها <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات هذا الطالب لإضافته أولا لقاعدة البيانات ثم كتابته للملف.
48 : نعلن هنا عن المؤشر ptrو الغرض منه تخزين عنوان سجل الطالب بالذاكرة – بعد إضافته لقاعدة البيانات – بغرض استخدام هذا المؤشر للوصول لبيانات الطالب (خاصة رقم الطالب) كما سيتضح بعد قليل .
49 : هنا يتم استدعاء الدالة AddStudent() العضو في الكلاس الأساس dBase عن طريق وضع معامل تحديد المدى ::بين اسم الدالة و اسم الكلاس الذي تنتمي له و ذلك حتى يعرف المترجم أننا نريد هذه الدالة و ليست الأخرى بالكلاس المشتق ، ( لاحظ هنا أن هذه الدالة ستقوم بعمل مختلف – نوعا ما – عن عمل الدالة AddStudent() بالكلاس المشتق RW2File – برغم أنهما يحملان نفس الاسم - و هذا ما سبق الاشارة إليه بأنه تعدد الأشكال لنفس الدالة polymorphism . ) بعد إضافة <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب لقاعدة البيانات تعيد الدالة عنوان سجل الطالب بالسلسلة و يتم تخزين هذا العنوان في المؤشر ptr.
50 – 52 : هنا ستقوم الدالة بعرض رقم الطالب و المتوسط و التقدير حيث أن البرنامج هو الذي يقوم بتوليدها ثم يُخبر المستخدم فورا بها ...
53 : هنا سنقوم بفتح الملف تمهيدا للكتابة فيه باستخدام الأمر open() كما سبق عند الكلام عن القراءة من الملف ، و هنا نستخدم الأوامر : ios:ut لإعلام المترجم أننا نريد الكتابة في الملف هذه المرة ، ثم الأمر ios::appلإعلام المترجم أيضا أننا و عند الكتابة لا نريد حذف البيانات الموجودة مسبقا بالملف ( و نستخدم هذا لأننا نستخدم هذه الدالة لكتابة <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات طالب جديد في كل مرة يتم استدعاءها لذلك لا نريد حذف <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب الذين سبق إدخالهم ) ، أما الأمر ios::binaryفهو يُفيد في تسجيل البيانات في شكل متسلسل أي بدون فراغات و الغرض من ذلك هو تقليل حجم الملف – قدر الإمكان - (خاصة إذا احتوى على عدد كبير جدا من الطلاب) بعدم وضع أي فراغات – أو أسطر جديدة - .
56 : هنا سنستخدم المتغير DataFileWrite لتسجيل رقم الطالب في الملف باستخدام معامل الادراج << (بصورة شبيهة لاستخدام هذا المعامل مع الكائن cout ) و بدلالة المؤشر ptrو الذي يُشير إلى عنوان سجل الطالب – الذي قمنا بإضافته للسلسلة – في الذاكرة . و سيتم تسجيل بقية البيانات في الملف في الأسطر التالية لذلك .
62 : إغلاق الملف بعد إنتهاء عملية الكتابة .

الأسطر 68 – 93 :
نفس الدالة السابقة WriteData() إلا أنه تم إعادة توصيفها هنا (overloaded) بحيث تقوم بكتابة <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات جميع <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب إلى الملف بدلا عن النسخة الأولى التي تقوم بكتابة <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات طالب واحد في كل مرة .
70 : أعلنا عن مؤشر ptr و أعطيناه قيمة المؤشر Head بحيث سنصل لكل سجلات (عقد) السلسلة عن طريقه.
71 : هنا سيتم فتح الملف تمهيدا للكتابة فيه و هو كما في النسخة السابقة من الدالة إلا أنك تلاحظ أننا لم نستخدم الأمر ios::appمما يعني أن هذه البيانات ( جميع <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب ) ستتم كتابتها بعد حذف كل البيانات التي سبقت كتابتها !!، لماذا ؟ : افترض أنك قمت بتشغيل قاعدة البيانات و قمت بإضافة طالب جديد فسيقوم البرنامج بكتابة <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات هذا الطالب و هكذا في كل مرة تقوم بإضافة طالب ، و لكن يمكنك أيضا تعديل هذه البيانات أو حذفها لذلك في هذه الحالة سيتم إعادة كتابة جميع هذه البيانات مرة أخرى – مع حذف البيانات القديمة – و هذه الدالة سيتم استدعاءها في الملف main كما سترى لاحقا عند تجربتنا للكلاس.
74 : هنا نختبر المؤشر Head للتأكد من أن قاعدة البيانات ليست خالية و بالتالي نقوم بتسجيل <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب واحدا بعد آخر .
88 : في حالت كانت قاعدة البانات خالية (أي أن المؤشر Headخالي) فلن تتم كتابة أي شي و لكن سيحذف البيانات الموجودة فقط (سترى فائدة ذلك إذا كان بقاعدة البيانات سجل طالب واحد – مثلا – و قام المستخدم للبرنامج بحذفه ثم أغلق البرنامج ).

الأسطر 95 – 128 :
الدالة AddStudent() و هي نفس الدالة AddStudent() في الكلاس dBaseمع وجود 3 إختلافات بينهما لذلك لن نعيد الكلام عنها و إنما نتكلم عن الإختلافات بينهما أولها توقيع الدالة بمعنى الوسائط التي نمررها للدالة ، فهذه الدالة نمرر لها هنا 5 وسائط – أولها رقم الطالب – أما الأخرى فنمرر لها أربع وسائط فقط – بدون رقم الطالب – الإختلاف الثاني :
94 : في هذا السطر نعيّن قيمة المتغير fID العضو بالكلاس الأساس بإعطائه قيمة الوسيط id– و الذي يحمل رقم الطالب الذي تمت قراءته من الملف - . أما في نفس الدالة بالكلاس الأساس فكنا نزيده بمقدار 1 (سطر 14 في الملف التنفيذي للكلاس dBase) .
124 : هنا الإختلاف الأخير بينهما حيث أن هذه الدالة تعيد القيمة 0 للدلالة على نجاحها في تنفيذ مهامها أما الدالة الأخرى فقد كانت تعيد إشارة لعنوان العقدة التي تمت إضافتها (انظر سطري 26 و 44 بالملف التنفيذي للكلاس dBase ) .


و هكذا انتهينا بحمد الله من تصميم و تنفيذ الكلاس RW2File و المشتق من الكلاس dBase نأتي الآن إلى تجربته ، قم بتعديل ملف main ليصبح كالتالي :








التحليل :

السطر 10 :
رأس الدالة HandleNotaNumberError() و قد سبق الكلام عنها عند تجربة الكلاس dBase . و بقية الكود هو نفسه كما في اختبار الكلاس dBase مع بعض التعديلات :

السطر 14 :
الإعلان عن الكائن rd من الكلاس RW2File .

السطر 15 :
المتغير DataChange من النوع البوولي (المنطقي : صواب أو خطأ) و قد أعطيناه قيمة أولية false أي خطأ ، سنستفيد منه لاحقا عند استخدام دالة WriteData()– النسخة التي تقوم بكتابة السلسلة بكاملها للملف – الغرض من ذلك تحسين أدء البرنامج – خاصة عند ضمه لبيانات جمع غفير من <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب – حيث لن يتم مناداة الدالة إلا إذا كانت هناك تعديلات قد أجريت ( بالحذف أو الإضافة ) على قاعدة البيانات كما سترى بالسطر 171.
السطر 17 :
هذه أول دالة عضو نستخدمها و هي ReadData() حيث ستعمل على قراءة البيانات الموجودة بالملف أولا – إن وجدت – تمهيدا للعمل عليها .

الأسطر 18 – 19 :
هنا و في حالة فشل الدالة الآنفة في فتح الملف فستعرض للمستخدم رسالة خطأ تخبره بذلك .

السطر 67 :
هنا استدعاء الدالة العضو WriteData() و التي تقوم بإضافة الطالب لقاعدة البيانات ثم تقوم بكتابة <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب للملف على القرص كما سبق عند شرحها .

الأسطر 81 و 104 :


تلاحظ هنا أننا استدعينا دالتي ChecID() و EditStudent() عن طريق كائن من الكلاس RW2File مع أنهما يتبعان للكلاس dBase في الأصل و لكن قام الكلاس RW2File بوراثة الكلاس dBaseو بالتالي سيرث جمع الدوال و المتغيرات ( العامة و المحمية ) به و يستطيع استعمالها مباشرة كأنها تتبع له هو في الأساس ! و هذا أجمل ما في الوراثة ، كالكائن الحي تماما JJ ، و الأمر كذلك في بقية الدوال ، لاحظ أنه إذا نجحت الدالة EditStudent() في تعديل <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">بيانات الطالب فستتغير قيمة المتغير DataChange إلى صواب true مما سيجعل الدالة WriteData() – سطر 172 تقوم بكتابة كل البيانات مرة أخرى للملف و هكذا الأمر عند حذف سجل أي طالب في case 3 (سطر 120 ).

الأسطر 181 – 185 :
عند اختيار المستخدم لإغلاق قاعدة البيانات سيتم اختبار المتغير DataChange فإذا كانت قيمته true فهذا يعني أنه تم عمل تعديل بالحذف أو الإضافة مما يوجب استدعاء الدالة العضو WriteData()لإعادة كتابة جميع البيانات مرة أخرى إلى الملف و إذا لم تنجح الدالة في ذلك فسيتم عرض رسالة تفيد المستخدم بذلك ، و في كل الحالات يتم الخروج من البرنامج .

الآن قم بالترجمة ثم شغل البرنامج و قم باختبار جميع الخيارات بصورة مكثفة ، و قد قمت بذلك - إلا أنني لم أضع الصور هنا بداعي الاختصار – و قد عمل البرنامج بصورة ممتازة...

من مميزات المترجم Dev-C++أنه يضم أداة لتحليل عمل البرنامج بحيث يخبرك عن عدد مرات و زمن استدعاء كل دالة مما يفيد في اختبار سرعة البرنامج و أدائه و تفاعل دواله ...
اضغط على القائمة Execute و اختر Profile analysis كما بالصورة :


بعد ثوان سيفتح لك نافذة شبيهة بالنافذة التالية :
قم بالاطلاع عليها ففيها معلومات مفيدة عن أداء البرنامج بحيث إذا كانت هناك دالة تستغرق وقتا أطول من اللازم – بسبب بطء أدائها أو كثرة تكرارها – تقوم بتعديلها و تحسينها و ليس هذا موضع تفصيل .

الطبقة الرابعة UserInterFace :
هذه الطبقة – و كما ذكرنا – هي ستمثل الوسيط بين المستخدم و البرنامج فهي واجهة الاستخدام التي ستتفاعل مع طلبات المستخدم و تقوم بتحقيقها و عرض النتائج ، بالتالي فسيتغير شكل الملف main بشكل كامل حيث ستقوم هذه الطبقة بمعظم مهامه ... و لكن دعونا الآن نطلع على ملف الرأس الخاص بها كالتالي :

أما الملف التنفيذي للكلاس فكالتالي :
كما تلاحظ هذا الكلاس هو المسئول عن عرض قائمة الخيارات للمستخدم و التفاعل مع اختيار المستخدم لتنفيذ طلبه ، الآن عدل الملف main حتى يصبح كالتالي :

جميل جدا ، الملف main أصبح أصغر و أجمل بعد أن قام الكلاس UserInterFace بأداء مهامه ، قم بالترجمة و التنفيذ و تجربة البرنامج بصورة مكثفة ..
و هكذا نكون على وشك الانتهاء من المشروع و لكن يتبقى لنا كلاس واحد فلنتكلم عنه :

الطبقة الخامسة : StudentCount
هذا الكلاس مهمته بسيطة جدا ألا و هي عد <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب بحيث نعرف العدد الإجمالي للطلاب في قاعدة البيانات و عدد <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب الناجحون و عدد <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب الراسبون ، و في الواقع الغرض منه هو دراسة المتغيرات الأعضاء الساكنة و الدوال الأعضاء الساكنة static function في الكلاس و كيفية التعامل معها لذلك سنقوم بعمل الكلاس ثم نهيء الكلاس الأول Student حتى يرث من الكلاس الجديد StudentCount و نجري بعض التعديلات على بعض الكلاسات الأخرى حتى نستفيد من الكلاس الجديد..
ملف رأس الكلاس StudentCount:


الأسطر 9 – 11 :
أعلنا عن ثلاث متغيرات ساكنة static من النوع intأولها لحفظ عدد جميع <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب و الثاني لتسجيل عدد <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب الناجحين و الثالث للراسبين، في الواقع كانت المتغيرات الساكنة تستعمل في السابق لحفظ قيم معينة و هذه القيم تحتفظ بها المتغيرات طوال فترة تشغيل البرنامج و بين الاستدعاءات المختلفة للدالات – مع إمكانية تعديل هذه القيم بعكس المتغيرات الثابتة const– و لكن الآن استخدام الكلاسات (الطبقات) أغنانا عن ذلك لأن المتغيرات الأعضاء الخاصة في الكلاس أتاحت لنا حفظ حالتها الداخلية و هي الوظيفة التي كنا نستخدم المتغيرات الساكنة لها .
مع هذا ما زال بإمكان المتغيرات الساكنة لعب دور في الكلاسات ، فالمتغيرات الساكنة في الكلاس تتم تهيئتها عندما يبدأ البرنامج و تصبح مشتركة بين جميع نسخ الكلاس مما يتيح لنا استخدامها لعد نسخ الكلاس Student و الذي - في الواقع - كل نسخة منه تمثل طالبا مختلفا ! (لا تنس أن الكلاس Student يرث الكلاس الجديد) .

الأسطر 22 – 25 :
أربع دوال إثنتان منهما تقومان بالزيادة أو الانقاص بمقدار 1 لقيمة المتغير PassSize و إثنتان تقومان بنفس الشيء للمتغير FailSizeأما المتغير الثالث فسيتم التعامل معه بالزيادو أو النقصان عن طريق دالتي البناء و الهدم ! كما سترى بحول الله عند الكلام عن الملف التنفيذي للكلاس.

الأسطر 28 – 30 :
ثلاث دوال ساكنة كل واحدة منهما تعيد قيمة أحد المتغيرات الأعضاء الساكنة ، فائدة هذه الدوال الساكنة أننا سنتمكن من استدعائها بدون إنشاء كائن من الكلاس ! كما سترى لاحقا .

نأتي الآن للملف التنفيذي :

الأسطر 5 – 7 :
هذه الأسطر تقوم بتصفير المتغيرات الأعضاء الثلاث و سيتم تنفيذها أول بدء البرنامج و قبل إنشاء أي كائنات !! و ذلك لأنها متغيرات ساكنة static و سنغير قيمتها لاحقا فعند إضافة طالب جديد ستتم زيادة المتغير AllSize بمقدار 1 ثم سننظر في نتيجة هذا الطالب فإن كان ناجحا سنزيد المتغير PassSize بمقدار 1 و إلا فسنزيد المتغير FailSize بمقدار 1 ، و عند حذف الطالب سنقوم بالعكس : الإنقاص بمقدار 1 .

السطر 9 :
دالة البناء و كما تلاحظ فهي تقوم بزيادة المتغير AllSize (و الخاص بعدد جميع الطلاب) بمقدار 1 ، معنى هذا أنه في كل مرة يتم فيها استدعاء هذه الدالة فسيزيد المتغير الساكن بمقدار 1 ، لا تنس أن الكلاس Student سيرث هذا الكلاس StudentCount و بالتالي فكل كائن من الكلاس Studentيتم إنشاؤه فسيتم إستدعاء دالة البناء هذه عند إنشائه ! (ثم يتم أيضا – طبعا – استدعاء دالة البناء الخاصة به ) مما يعني زيادة عدد <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب بمقدار 1.

السطر 11 :
دالة الهدم و التي سيتم استدعاؤها عند حذف كائن من الكلاس Student (سجل طالب) و بالتالي سيتم انقاص المتغير AllSize بمقدار 1.

السطر 13 :
الدالة SetPassSize() و التي ستقوم بزيادة المتغير PassSize بمقدار 1 ، هذه الدالة سنستعملها عند إضافة طالب جديد ناجح إلى قاعدة البيانات .

السطر 18 :
الدالة SetFailSize() و هي كسابقتها تقوم بزيادة المتغير FailSize بمقدار 1 ، نستعمل الدالة عند إضافة طالب جديد راسب! .

السطر 23 :
الدالة الساكنة GetAllSize() و التي ستعيد قيمة المتغير الساكن AllSize، هذه الدالة نستطيع استدعاءها في أي مكان نريد من البرنامج لمعرفة العدد الكلي للطلاب من دون حتى أن نكون قد أنشأنا أي كائن منها ! و كذلك الدالتان الساكنتان الأخريتان! .

السطر 28 :
الدالة الساكنة GetPassSize() و هي تُرجع قيمة المتغير الساكن PassSize .

السطر 33 :
الدالة الساكنة الثالثة GetFailSize() و التي تعيد قيمة المتغير FailSize .

السطر 37 :
الدالة SetPassDec() و التي تقوم بإنقاص المتغير PassSize بمقدار 1 ، نستعمل هذه الدالة عند حذف طالب ناجح بحيث ينقص عدد <A href="http://www.alkutnet.com/vb/showthread.php?93105-مشروع-قاعدة-بيانات-الطلاب-بلغة-سي-بلس-بلس-تطبيق-لمفاهيم-الـoop">الطلاب الناجحين بمقدار 1 .

السطر 42 :
الدالة الأخيرة SetFailDec() و التي تقوم بإنقاص المتغير FailSize بمقدار 1 ، و سنستعملها إذا كان الطالب المحذوف راسبا .

الآن أعزائي سنجري بعض التعديلات البسيطة على بقية البرنامج حتى تتم الاستفادة من كلاسنا الجديد و المثير J :
أولا الكلاس Student و الذي سنجعله يرث من الكلاس StudentCount بحيث أن أي نسخة من الكلاس Student (أي سجل طالب ) ستتضمن داخلها تلقائيا نسخة من الكلاس الجديد .
سنقوم بتعديل أول سطر في الكلاس المشتق Student حتى يصبح كالتالي :
Class Student : public StudentCount { //…

أما الكلاس dBase فسنجري عليه عدة تغييرات بحيث سيصبح ملفه التنفيذي كالتالي :

أهم التعديلات هنا الأسطر 167 – 169 حيث ترى أننا قمنا بإستدعاء الدوال الساكنة بدون استعمال أي كائن من الكلاس فقط ذكرنا اسم الدالة مسبوقا باسم الكلاس و بينهما معامل تحديد المدى :: .

أما الكلاس RW2File فسيصبح كالتالي :
تبقى الكلاس UserInterFace و الذي سنعمل به تعديلا بسيطا بحيث يصبح ملفه التنفيذي كالتالي :

بهذا و بحمد الله نكون قد انتهينا من المشروع قم بالترجمة و التنفيذ و جرب البرنامج بصورة مكثفة – و جرب حتى إدخال قيم خاطئة و غير منطقية – لترى كيف يتصرف البرنامج و هل يحتاج إلى أي تعديل ؟ ، مع العلم أنه ليس هناك برنامج كامل ! و بالتالي فإن أي برنامج يمكن تحسينه و تطويره ، و تذكر دائما أن إعادة تصميم البرنامج و تطويره من المسئوليات الحيوية الملقاة على عاتق المبرمج فقط كن صبورا ، فالأمر لا يتطلب سوى بعض الوقت و الخبرة و التفكير .
لتحميل ملفات المشروع كاملة – في صورته النهائية – مضغوط بصيغة RARإضغط هنــــــا . و لتحميل هذا الموضوع بصيغة مستند .docمضغوط RAR من هنا .

بقي أمر أخير : يتيح لنا المترجم أن نضع الأيقونة التي نريدها للبرنامج و ذلك في خيارات المشروع – Project Options(Alt+P) حيث ستظهر لك النافذة التالية :

حيث يمكنك اختيار أيقونة من مكتبة المترجم Library أو تحديد مكان الأيقونة التي تريد على القرص Browseو هناك خيارات أخرى مثل اسم المنتج و الشركة و رقم الاصدار و غير ذلك ... ستجد الملف التنفيذي (الهدف) في نفس مجلد المشروع و بنفس الاسم و لكن بالإمتداد exe و بالأيقونة التي اخترتها ، قم بتجربته و بالتوفيق ...
في مشروعنا التالي – بحول الله – سنعمل على تطوير نفس هذا المشروع بحيث نعمل له واجهة رسومية جميلة J تسر المستخدم و تجعله يحب التعامل مع البرنامج J.
في امان الله

التوقيع : محمد الجندي
النجاح وليـد عمـل جـاد
الأســتاذ / محمــد الجنـدي
موجه علم النفس
ادارة غرب المنصورة التعليمية
( منزل 2218303 ( عمل 2371333
& 01001637295 & 01225588504
محمد الجندي غير متواجد حالياً   رد مع اقتباس

قديم 02-16-2012, 09:14 PM   #2
معلومات العضو
مدير عام
الصورة الرمزية محمد الجندي
رقم العضوية : 1
تاريخ التسجيل: Sep 2002
مجموع المشاركات : 886
محمد الجندي تم تعطيل التقييم
افتراضي

اختبار الكلاس dBase :


قم بتعديل الملف main الذي قمنا باختبار البرنامج عن طريقه - بعد كلامنا عن الكلاس - Student


ليصبح كالتالي:







التحليل :
الأسطر 10 – 22 :
هذه دالة عامة – ليست عضوا في الكلاس – و قد أخذتها من صندوق الأدوات tool boxJ الخاص بي (و الذي يضم مجموعة متميزة من الدوال و الكلاسات) و أضفتها هنا و اسمها HandleNotaNumberError() ، وظيفتها التصدي لخطأ إدخال قيمة غير رقمية في متغير رقمي من خلال الكائن cin و سيتم استدعائها عند كل إدخال إذا حدث فشل في الكائن cin – بسبب إدخال قيمة غير رقمية في متغير رقمي – حيث تتم معالجة الفشل عن طريق الدالة cin.clear() و يتم تنبيه المستخدم حتى يحاول الادخال مرة أخرى ، و سيتكرر الأمر إذا أدخل قيمة خاطئة مرة أخرى و هكذا .. سنقوم بتطوير هذه الدالة لتعمل من خلال الاستثناءات عند تصميمنا لكلاس يمثل واجهة المستخدم لاحقا بحول الله ...

السطر 26 :
هنا أعلنا عن الكائن db من الكلاس dBase و هذا الكائن هو الذي سيقوم بكل العمل و ما علينا إلا إرشاده و توجيهه بالأوامر J .

السطر 27 :
استعملنا التكرار for(; و هو يجعل البرنامج يكرر الكود التالي – بين القوسين سطري 28 و 179 – دائما بحيث لا يخرج من التكرار إلا بجملة شرطية ( بطريقة مشابهة للـ while ) الغرض من ذلك أن البرنامج سيعرض قائمة خيارات للمستخدم ليختار منها ، عند الاختيار يذهب البرنامج لتنفيذ ما تم اختياره ثم يرجع إلى نفس قائمة الخيارات مستعدا لتلقي أوامر أخرى حتى ينفذها و يرجع مرة أخرى للقائمة الرئيسية و هكذا دائما لحين الخروج من البرنامج .

السطر 29 :
مسح الشاشة و الغرض من ذلك هو أن يكون البرنامج منسقا بصورة جميلة تعجب المستخدم مما يرفع أسهم البرنامج لديه.

الأسطر 31 – 40 :
هذه هي قائمة البرنامج الرئيسية بها 9 خيارات : 1. لإضافة طالب جديد ، 2. تعديل بيانات طالب 3. حذف سجل طالب 4. البحث عن بيانات طالب بدلالة اسمه 5. البحث عن بيانات طالب بدلالة رقمه 6. عرض بيانات جميع الطلاب 7. عرض بيانات الناجحين فقط 8. عرض بيانات الراسبين 9. الخروج من قاعدة البيانات ، و يتم توجيه المستخدم للإختيار .

الأسطر 43 – 44 :
هنا ستم استدعاء دالة HandleNotaNumberError() في حالة حدوث فشل في الكائن cin بسبب إدخال قيمة غير رقمية في المتغير الرقمي choice و الذي يفترض أن يأخذ القيمة بين 1 – 9 التي تمثل اختيار العميل تمهيدا لتنفيذ الإختيار عن طريق أمر الـ switch .

الأسطر 45 – 46 :
الإعلان عن دوال مؤقته تستخدم لإدخال بيانات الطالب تمهيدا لتمريرها للكائن db من خلال الدوال الأعضاء ..

السطر 48 :
عبارة switch و تعمل على اختبار المتغير choice و من ثم يتم تنفيذ الجزء الخاص بقيمة المتغير من خلال case ..

الأسطر 50 – 74 :
في حالة اختار المستخدم الخيار 1 – إضافة طالب جديد - و بالتالي كانت قيمة المتغير choice هي 1 فسيتم تنفيذ هذا المقطع من الكود و فيه يقوم المستخدم بإدخال بيانات الطالب الجديد ثم يقوم الكائن بإضافته عن طريق الأمر (db.AddStudent()) و بعد ذلك يتم الرجوع مرة أخرى للقائمة الرئيسية .

الأسطر 75 – 111 :
و هذا الجزء سيتم تنفيذه في حالة اختيار الخيار رقم 2 – تعديل بيانات طالب – حيث سيتم أولا التأكد من رقم الطالب ( التأكد من وجود الطالب بقاعدة البيانات ) عن طريق الدالة العضو بالكائن (db.ChecID()) ثم يتم التعديل بإدخال البيانات الجديدة و استدعاء دالة التعديل العضو في الكائن (db.EditStudent()) لتقوم بالتعديل المطلوب.

الأسطر 112 – 133 :
هنا ستم تنفيذ الجزء الخاص برقم 3 و هو حذف سجل الطالب و بنفس طريقة الجزء السابق سيتم أولا التأكد من رقم الطالب الذي أدخله المستخدم و في حالة كان الرقم صحيحا يتم حذف سجل الطالب عن طريق الدالة (db.DeleteStudent()) . و هكذا بقية الأجزاء حتى النهاية و في الحقيقة لا أريد الاطالة أكثر و إنما قم بترجمة و تنفيذ كامل المشروع و اختبار النتائج للتأكد من عمل البرنامج بصورة سليمة قدر المستطاع .

الطبقة الثالثة : RW2File


هذا الكلاس وظيفته هي تسجيل بيانات الطلاب على القرص و ذلك تجنبا لضياع هذه البيانات عند إغلاق البرنامج ، ثم و عند تشغيل البرنامج مرة أخرى يقوم بقراءة هذه البيانات التي سبق تسجيلها على القرص و بالتالي يُدخلها في قاعدة البيانات ( السلسلة المتصلة ) مرة أخرى حتى نستطيع التعامل معها ( عرض ، تعديل ، حذف ...الخ ) لذلك سيقوم هذا الكلاس بوراثة الكلاس السابق dBase ليؤدي مهمته بنجاح ، و هي مهام الكلاس dBase بالإضافة إلى مهام أخرى جديدة ( الوراثة من المفاهيم المهمة و المستخدمة في تطوير البرمجيات في الـOOP ) و سنحتاج لتجهيز الكلاس dBase حتى يصبح هو الكلاس الأساس Base class الذي سيرثه كلاسنا الجديد RW2File بحيث يصبح ملف الرأس كالتالي :







ستلاحظ تغييرات في بعض الأسطر كالتالي :

سطر :20
تمت إضافة الكلمة المحجوزة virtual (ظاهري أو خيالي ) قبل دالة الهدم ~dBase() – لإعلام المترجم أننا نريد تجاوزها (عدم استخدامها) - و ذلك للتأكد من أن عملية تدمير الكلاس RW2File ستتم بصورة آمنة باستخدام دالة الهدم الخاصة به – و متنها هو نفسه متن الدالة ~dBase() حيث تقوم أيضا بحذف المؤشر Head ، و إذا لم نفعل هذا – أي اضافة virtual – فإن دالة البناء في الكلاس الأساس dBase قد يتم استدعاؤها عندما يتم تدمير الكلاس المشتق RW2File باستخدام المؤشر Head ( و الذي هو أصلا عضو في الكلاس dBase ) عن طريق الأمر delete و قد لايتم تحرير أي مساحة تم حجزها من الذاكرة ، لذلك كان لابد من هذا التعديل .

سطر 23 :
تم الإعلان عن دالة صديقة – و الدالة الصديقة يتم الإعلان عن رأسها فقط داخل الكلاس باستعمال الكلمة المحجوزة friend ، فهي ليست عضوا فيه ! و يتاح لها الوصول لكل عناصر الكلاس ! – و هذه الدالة و اسمها operator<< (المعامل >> ) و ستقوم بزيادة تحميل المعامل >> بحيث يقوم بعرض كل بيانات الطالب بدلالة مؤشر يشير لسجل الطالب و سترى ذلك بالتفصيل في الملف التنفيذي ( و هذا التعديل في الواقع لا دخل له بعملية تجهيز الكلاس dBase لعملية الوراثة و إنما هو فكرة لتحسين الكود باستخدام زيادة تحميل المعاملات Operator Overloading سترى نتائجها بعد قليل ، و قد وضعتها هنا حتى تتمكن من المقارنة بين الكلاس قبل التعديل و بعد التعديل ) .



سطر 26 :
في الدالة العضو AddStudent() قمنا بعمل تغييرين أو لهما أننا جعلنا القيمة التي تعيدها الدالة هي إشارة من نفس نوع السجل dBase::student (و قد كانت في السابق تعيد عددا صحيحا int !) ما الغرض من ذلك ؟ الجواب أن الكلاس المشتق RW2File يقوم - كما سبق ذكره - بكتابة سجلات الطلاب إلى القرص الصلب و أيضا قراءتها منه ، و لتنفيذ عملية الكتابة يجب أولا أن يقوم المستخدم بإدخال بيانات الطالب إلى قاعدة البيانات ثم و مباشرة يتم كتابة السجل الجديد على القرص ، سنستخدم – في الكلاس المشتق - دالة لكتابة السجل على القرص – كما سترى لاحقا - و لكن هذه الدالة ستحتاج لعنوان كل سجل على حدة حتى تستطيع الوصول لبياناته لتسجيلها لذلك تم تعديل الدالة AddStudent() بحيث ستعيد عنوان كل سجل يتم إدخاله في كل مرة .
التغيير الثاني في الدالة AddStudent() هو إضافة الكلمة المحجوزة virtual لإعلام المترجم أننا نريد تجاوز هذه الدالة بحيث أننا لن نستطيع استدعاءها إلا عن طريق دالة عضو في الكلاس RW2File لماذا ؟ لأن الوراثة تتيح لك إضافة تحسينات لدوال الكلاس الأساس عن طريق تجاوز الدالة المراد تحسينها و إنشاء دالة أخرى بنفس الاسم في الكلاس المشتق و تكون بنفس اسم الدالة التي تم تجاوزها في الكلاس الأساس (حيث أن الكلاس المشتق لا يستطيع تعديل الكلاس الأساس). قد يبدو لك هذا الكلام غامضا الآن و لكن ستتضح الرؤية أكثر عند الكلام بتفصيل عن الكلاس المشتق لاحقا بحول الله.

سطر 30 :
أضفنا دالة جديدة ChecName() و مشابهة للدالة الأخرى ChecID() ، هذه الدالة الجديدة ستعيد القيمة 1 إذا كان الاسم الذي مررناه لها موجودا بقاعدة البيانات – كما سترى في المتن – هذه الدالة سنستخدمها حتى لا نسمح للمستخدم بإدخال اسم لطالب جديد و هذا الاسم سبق إدخاله بحيث أن أسماء الطلاب – كأرقامهم – لا بد أن تكون فريد – مختلفة – ( هذا التعديل للكلاس dBase هو أيضا تعديل لتحسين عمل الكلاس و لا علاقة له بعملية تجهيزه للوراثة ..).

سطر 38 :
وضعنا المتغيرات الأعضاء (fID و Head) – و التي كانت في القسم الخاص private - وضعناها في القسم المحمي protected و ذلك حتى يتمكن الكلاس المشتق من الوصول لها ...



و الآن قم بتعديل الملف التنفيذي للكلاس الأساس


ليصبح كالتالي :






الأسطر 13 – 27 بها الدالة الصديقة operator<<()– و هي ليست عضوا في الكلاس لذلك لم نضع اسم الكلاس قبلها – متبوعا بالمعامل :: - كما فعلنا مع الدوال الأخرى ، هذه الدالة و بكل بساطة تقوم بزيادة تحميل المعامل >> باستخدام كائن os من الكلاس ostream (مكتبة iostream) بحيث يعرض لي بيانات الطالب بدلالة مؤشر ptr من نوع السجل dBase::studentبعبارة أخرى إذا استعملت العبارة cout << ptr; ( ptr مؤشر يُشير إلى سجل ) فسيتم عرض بيانات الطالب المخزنة في السجل الذي يشير إليه المؤشرptr و سنقوم باستخدام ذلك في أربع دوال ! : DisplayAll() و DisplayPass() و DisplayFail() و GetStudent() و GetStudent() – النسخة الأخرى منها – (انظر الكود ) فائدة هذا أن زيادة تحميل هذا المعامل مكنتني من تقليل حجم الكود بأكثر من 30 سطرا و جعلته أسهل الآن أو مستقبلا ...

ستلاحظ السطر 43 – في متن الدالة AddStudent() أصبح يعيد قيمة المؤشر Head (في حالة كانت هذه العقدةالأولى في السلسلة ) أما السطر 61 فيعيد قيمة المؤشر current حيث أننا عدلنا نوع البيانات الذي ستعيده الدالة من عدد صحيح إلى إشارة إلى السجل الذي تمت إضافته كما سبقت الإشارة لذلك بالأعلى...

الأسطر 238 – 257 : بها متن الدالة الجديدة ChecName() و هي مشابهة لعمل الدالة ChecID() .



قم بإجراء التعديلات أعلاه على الكلاس الأساس dBase ثم قم بإضافة ملف رأس للكلاس المشتق RW2File للمشروع و اكتب به الكود التالي :







تحليل الكود :

السطر 6 :
تضمين المكتبة fstream و التي تضم تعاريف تيارات الإدخال و الإخراج من و إلى الملف تمهيدا لاستخدامها...

السطر 11 :
أعلنا عن الكلاس الجديد RW2File و في نفس السطر أعلمنا المترجم بأن هذا الكلاس سيرث الكلاس الأساس dBase عن طريق كتابة اسمه بعد الرمز : مسبوقا بالكلمة public .
السطر 13 :
أول المتغيرات الخاصة و هو عبارة عن المتغير buffer من النوع الحرفي char بسعة 256 حرفا و سنستخدمه كمخزن مؤقت عند قراءة البيانات من الملف على القرص.

السطر 14 :
الإعلان عن المتغير DataFileWrite من النوع ofstream (ضمن المكتبة fstream) أي تيار إخراج حيث سنستخدم هذا المتغير لفتح الملف المحدد و كتابة البيانات به ثم إغلاقه .

السطر 15 :
الإعلان عن المتغير DataFileRead من النوع ifstream (أيضا ضمن المكتبة fstream) أي تيار إدخال حيث سنستخدمه لفتح الملف و قراءة البيانات ثم إغلاقه .

السطران 19 و 22 :
دالتي البناء و الهدم .

السطر 25 :
دالة ReadData() لقراءة تيار البيانات من الملف و هي من النوع int و تأخذ وسيطا عبارة عن إشارة إلى كائن من نفس الكلاس.

السطر 26 و 27 :
دالة WriteData() لكتابة تيار البيانات إلى الملف و هي من النوع int و تأخذ أربع وسائط و هذه الدالة تقوم بكتابة بيانات طالب (سجل) واحد فقط إلى الملف في كل مرة يتم استدعاءها أما الدالة الأخرى ( بنفس الاسم ) و بدون وسائط فتقوم بكتابة كامل السلسلة (كل الطلاب) إلى الملف عند استدعائها ، هذه الدالة الأخرى سيتم استدعاؤها عند نهاية البرنامج بغرض تسجيل كامل البيانات – بعد إجراء أي تعديل عليها - .

السطر 28 :
الدالة AddStudent() و هي أصلا عضو في الكلاس الأساس dBase – بتوقيع مختلف – و قد أعدنا تعريفها هنا حيث سيتم تحسينها حتى تستقبل رقم الطالب أيضا مع بقية بياناته لإضافتها لقاعدة البيانات . التوارث لا يتيح للكلاس المشتق إزالة أي شيء من الكلاس الأساس لذلك يمكننا تجاوز الدالة التي لا نريدها في الكلاس الأساس عن طريق وضع الكلمة المحجوزة virtual قبلها – كما سبق عند الكلام عن تجهيز الكلاس dBase – ثم إعادة تعريفها في الكلاس المشتق و هذا بالضبط هو ما عملناه هنا ! و هكذا أصبح لهذه الدالة تنفيذان أحدهما بالكلاس الأساس و الآخر بالكلاس المشتق و كل تنفيذ سيقوم بعمل مختلف ، تعدد التنفيذات – بنفس الاسم - هذا هو ما يسمى بتعدد الأشكال (تعدد الأوجه ) polymorphism . جميع أعضاء الكلاس dBase (متغيرات و دوال) أصبحت – عن طريق التوارث – أعضاء في الكلاس الجديد RW2File يمكن استخدامها مباشرة و عند استدعاءها يستطيع المترجم معرفة مكانها و الوصول لها بدون أي كود إضافي ، و هكذا استفدنا من الكلاس الأساس عن طريق توريثه لكلاس جديد يضمه مع تحسينات و إضافات من دون حاجة لإعادة كتابته من جديد و لعل هذا من أهم فوائد التوريث Inheritance و كما ترى فهو مفيد جدا في تطوير البرامج خاصة البرامج الكبيرة و المعقدة.




نأتي الآن لملف تنفيذ الكلاس كالتالي :







تحليل الكود :

الأسطر 8 – 11 :
متن دالة الهدم و تقوم بحذف المؤشر Head و الذي هو في الأصل عضو في الكلاس الأساس ، لاحظ أنها بنفس متن دالة هدم الكلاس الأساس dBase لذا كان لابد من تجاوز دالة الهدم في الكلاس الأساس عن طريق وضع الكلمة virtual قبلها كما سبق ...

الأسطر 13 – 44 :
متن الدالة ReadData() و نمرر لها إشارة لكائن rd من الكلاس RW2File ، و تعمل كالتالي كالتالي :
15 : نقوم باستخدام المتغير DataFileRead لفتح الملف أولا باستخدام الأمر open() حيث نضع بين القوسين اسم الملف بين علامتي تنصيص "DataRecord.dat " ( يمكن وضع مسار الملف كاملا لإعلام المترجم عن مكان وجوده أو ذكر اسم الملف فقط مما يُعلم المترجم أن الملف موجود في نفس مجلد المشروع و إذا لم يكن موجودا فسيقوم بإنشائه!) ، أما الأمر ios::in فهو يُعلم المترجم أن فتح الملف بغرض القراءة منه .
16 : هنا – و بعد فتح الملف – سيتأكد أولا من نجاح فتح الملف عن طريق الأمر is_open() و الذي يعيد القيمة صواب true إذا تمت عملية الفتح و بالتالي سينفذ الكود التالي بين قوسين :
18 – 20 : هنا متغيرات مؤقتة ستستخدم لتخزين البيانات التي ستتم قراءتها من الملف ثم تمريرها لدالة AddStudent() لإضافتها للسلسلة .
21 : تكرار for و سيستمر حتى تتم قراءة جميع البيانات من الملف . طبعا سنكون قد كتبنا البيانات بالملف مسبقا بطريقة محددة بحيث نفصل بين كل حقل و الآخر (كالرقم و الاسم .. ) بالرمز | ثم الرمز * للدلالة على نهاية السجل .

23 : نستخدم المتغير DataFileRead لقراءة أول جزء من البيانات باستخدام الأمر getline() حيث سنضع بين القوسين اسم المتغير الذي سنضع به البيانات التي ستتم قراءتها –و هو المتغير العضو buffer – ثم فاصلة , ثم عدد الأحرف المطلوب قراءتها – و هو عدد تقريبي – ثم و بين العلامتين ' ' نضع مُحدِّد – أيّ رمز تختاره – ، هذا المحدد سيخبر الأمر بحدود الجزء الذي تريد قراءته من البيانات ، بعبارة أخرى : هذا الأمر يقوم بالقراءة من السطر – بالملف – حتى يصل إلى الرمز المحدد أو حتى يصل إلى الرقم المحدد أيهما كان أولا . الغرض من ذلك هو الفصل بين البيانات – الرقم و الاسم و الدرجات .. الخ – بحيث أن كل نوع سيتم تخزينه في متغير مختلف :
24 : استخدمنا المتغير المؤقت studentID لنحفظ فيه قيمة الجزء الأول من سطر و البيانات و الذي هو رقم الطالب و لكن و لأن هذا الجزء من البيانات المخزن في المتغير buffer هو حرفي لذلك لابد من تحويله إلى عدد صحيح int باستخدام الأمر atoi() و الذي يقوم بالتحويل من char إلى int ثم نقوم باسناد الرقم الناتج إلى المتغير المؤقت studentID الخاص بحفظ رقم الطالب .
و ستتكرر هذه العملية ( قراءة البيانات جزءا بعد جزء و تخزينها في متغير مؤقت ) حتى الوصول للسطر :
36 و 37 : سيختبر إن كانت هذه نهاية الملف بحيث يخرج من التكرار for إن كان كذلك .
38: هنا يتم استدعاء الدالة المحسنة AddStudent() عن طريق الكائن rd و الذي مررنا إشارة له إلى دالة القراءة من الملف ، (سيتجاوز المترجم نسخة الدالة AddStudent بالكلاس الأساس و يستدعي النسخة الموجودة بالكلاس المشتق و هو – أي المترجم - واثق من ذلك لأننا عند تعريف هذه الدالة بالكلاس الأساس سبقناها بالكلمة المحجوزة virtual ! و بالرغم من ذلك فيمكننا استدعاء نفس الدالة و لكن من الكلاس الأساس و ذلك في الدالة WriteData() العضو في الكلاس المشتق كما سترى بعد قليل !) ، هذه الدالة سنمرر لها بيانات الطالب – و المخزنة في خمس متغيرات مؤقته – و وظيفة الدالة هي إضافة هذه البيانات إلى قاعدة بياناتنا (السلسلة المتصلة) و سيتم تكرار ذلك لكل الطلاب الذين تم تخزين بياناتهم في الملف . سيأتي الكلام عن متن هذه الدالة بعد قليل.
40 : إغلاق الملف بعد إنتهاء القراءة منه .
41 : ستعيد الدالة الرقم 0 للدلالة على نجاح العملية .
43 : أما في حالة الفشل في فتح الملف فستعيد الدالة الرقم -1 ، هذه القيم سنتعامل معها لاحقا عند استعمال الدالة في ملف main .

الأسطر 46 – 66 :
هنا الدالة WriteData() و التي تقوم بكتابة سجل طالب واحد للملف حيث نمرر لها بيانات هذا الطالب لإضافته أولا لقاعدة البيانات ثم كتابته للملف.
48 : نعلن هنا عن المؤشر ptr و الغرض منه تخزين عنوان سجل الطالب بالذاكرة – بعد إضافته لقاعدة البيانات – بغرض استخدام هذا المؤشر للوصول لبيانات الطالب (خاصة رقم الطالب) كما سيتضح بعد قليل .
49 : هنا يتم استدعاء الدالة AddStudent() العضو في الكلاس الأساس dBase عن طريق وضع معامل تحديد المدى :: بين اسم الدالة و اسم الكلاس الذي تنتمي له و ذلك حتى يعرف المترجم أننا نريد هذه الدالة و ليست الأخرى بالكلاس المشتق ، ( لاحظ هنا أن هذه الدالة ستقوم بعمل مختلف – نوعا ما – عن عمل الدالة AddStudent() بالكلاس المشتق RW2File – برغم أنهما يحملان نفس الاسم - و هذا ما سبق الاشارة إليه بأنه تعدد الأشكال لنفس الدالة polymorphism . ) بعد إضافة بيانات الطالب لقاعدة البيانات تعيد الدالة عنوان سجل الطالب بالسلسلة و يتم تخزين هذا العنوان في المؤشر ptr.
50 – 52 : هنا ستقوم الدالة بعرض رقم الطالب و المتوسط و التقدير حيث أن البرنامج هو الذي يقوم بتوليدها ثم يُخبر المستخدم فورا بها ...
53 : هنا سنقوم بفتح الملف تمهيدا للكتابة فيه باستخدام الأمر open() كما سبق عند الكلام عن القراءة من الملف ، و هنا نستخدم الأوامر : ios:ut لإعلام المترجم أننا نريد الكتابة في الملف هذه المرة ، ثم الأمر ios::app لإعلام المترجم أيضا أننا و عند الكتابة لا نريد حذف البيانات الموجودة مسبقا بالملف ( و نستخدم هذا لأننا نستخدم هذه الدالة لكتابة بيانات طالب جديد في كل مرة يتم استدعاءها لذلك لا نريد حذف بيانات الطلاب الذين سبق إدخالهم ) ، أما الأمر ios::binary فهو يُفيد في تسجيل البيانات في شكل متسلسل أي بدون فراغات و الغرض من ذلك هو تقليل حجم الملف – قدر الإمكان - (خاصة إذا احتوى على عدد كبير جدا من الطلاب) بعدم وضع أي فراغات – أو أسطر جديدة - .
56 : هنا سنستخدم المتغير DataFileWrite لتسجيل رقم الطالب في الملف باستخدام معامل الادراج << (بصورة شبيهة لاستخدام هذا المعامل مع الكائن cout ) و بدلالة المؤشر ptr و الذي يُشير إلى عنوان سجل الطالب – الذي قمنا بإضافته للسلسلة – في الذاكرة . و سيتم تسجيل بقية البيانات في الملف في الأسطر التالية لذلك .
62 : إغلاق الملف بعد إنتهاء عملية الكتابة .

الأسطر 68 – 93 :
نفس الدالة السابقة WriteData() إلا أنه تم إعادة توصيفها هنا (overloaded) بحيث تقوم بكتابة بيانات جميع الطلاب إلى الملف بدلا عن النسخة الأولى التي تقوم بكتابة بيانات طالب واحد في كل مرة .
70 : أعلنا عن مؤشر ptr و أعطيناه قيمة المؤشر Head بحيث سنصل لكل سجلات (عقد) السلسلة عن طريقه.
71 : هنا سيتم فتح الملف تمهيدا للكتابة فيه و هو كما في النسخة السابقة من الدالة إلا أنك تلاحظ أننا لم نستخدم الأمر ios::app مما يعني أن هذه البيانات ( جميع الطلاب ) ستتم كتابتها بعد حذف كل البيانات التي سبقت كتابتها !!، لماذا ؟ : افترض أنك قمت بتشغيل قاعدة البيانات و قمت بإضافة طالب جديد فسيقوم البرنامج بكتابة بيانات هذا الطالب و هكذا في كل مرة تقوم بإضافة طالب ، و لكن يمكنك أيضا تعديل هذه البيانات أو حذفها لذلك في هذه الحالة سيتم إعادة كتابة جميع هذه البيانات مرة أخرى – مع حذف البيانات القديمة – و هذه الدالة سيتم استدعاءها في الملف main كما سترى لاحقا عند تجربتنا للكلاس.
74 : هنا نختبر المؤشر Head للتأكد من أن قاعدة البيانات ليست خالية و بالتالي نقوم بتسجيل بيانات الطلاب واحدا بعد آخر .
88 : في حالت كانت قاعدة البانات خالية (أي أن المؤشر Head خالي) فلن تتم كتابة أي شي و لكن سيحذف البيانات الموجودة فقط (سترى فائدة ذلك إذا كان بقاعدة البيانات سجل طالب واحد – مثلا – و قام المستخدم للبرنامج بحذفه ثم أغلق البرنامج ).

الأسطر 95 – 128 :
الدالة AddStudent() و هي نفس الدالة AddStudent() في الكلاس dBase مع وجود 3 إختلافات بينهما لذلك لن نعيد الكلام عنها و إنما نتكلم عن الإختلافات بينهما أولها توقيع الدالة بمعنى الوسائط التي نمررها للدالة ، فهذه الدالة نمرر لها هنا 5 وسائط – أولها رقم الطالب – أما الأخرى فنمرر لها أربع وسائط فقط – بدون رقم الطالب – الإختلاف الثاني :
94 : في هذا السطر نعيّن قيمة المتغير fID العضو بالكلاس الأساس بإعطائه قيمة الوسيط id – و الذي يحمل رقم الطالب الذي تمت قراءته من الملف - . أما في نفس الدالة بالكلاس الأساس فكنا نزيده بمقدار 1 (سطر 14 في الملف التنفيذي للكلاس dBase) .
124 : هنا الإختلاف الأخير بينهما حيث أن هذه الدالة تعيد القيمة 0 للدلالة على نجاحها في تنفيذ مهامها أما الدالة الأخرى فقد كانت تعيد إشارة لعنوان العقدة التي تمت إضافتها (انظر سطري 26 و 44 بالملف التنفيذي للكلاس dBase ) .



و هكذا انتهينا بحمد الله من تصميم و تنفيذ الكلاس RW2File و المشتق من الكلاس dBase نأتي الآن إلى تجربته ، قم بتعديل ملف main ليصبح كالتالي :







التحليل :

السطر 10 :
رأس الدالة HandleNotaNumberError() و قد سبق الكلام عنها عند تجربة الكلاس dBase . و بقية الكود هو نفسه كما في اختبار الكلاس dBase مع بعض التعديلات :

السطر 14 :
الإعلان عن الكائن rd من الكلاس RW2File .

السطر 15 :
المتغير DataChange من النوع البوولي (المنطقي : صواب أو خطأ) و قد أعطيناه قيمة أولية false أي خطأ ، سنستفيد منه لاحقا عند استخدام دالة WriteData() – النسخة التي تقوم بكتابة السلسلة بكاملها للملف – الغرض من ذلك تحسين أدء البرنامج – خاصة عند ضمه لبيانات جمع غفير من الطلاب – حيث لن يتم مناداة الدالة إلا إذا كانت هناك تعديلات قد أجريت ( بالحذف أو الإضافة ) على قاعدة البيانات كما سترى بالسطر 171.


السطر 17 :
هذه أول دالة عضو نستخدمها و هي ReadData() حيث ستعمل على قراءة البيانات الموجودة بالملف أولا – إن وجدت – تمهيدا للعمل عليها .

الأسطر 18 – 19 :
هنا و في حالة فشل الدالة الآنفة في فتح الملف فستعرض للمستخدم رسالة خطأ تخبره بذلك .

السطر 67 :
هنا استدعاء الدالة العضو WriteData() و التي تقوم بإضافة الطالب لقاعدة البيانات ثم تقوم بكتابة بيانات الطالب للملف على القرص كما سبق عند شرحها .

الأسطر 81 و 104 :
تلاحظ هنا أننا استدعينا دالتي ChecID() و EditStudent() عن طريق كائن من الكلاس RW2File مع أنهما يتبعان للكلاس dBase في الأصل و لكن قام الكلاس RW2File بوراثة الكلاس dBase و بالتالي سيرث جمع الدوال و المتغيرات ( العامة و المحمية ) به و يستطيع استعمالها مباشرة كأنها تتبع له هو في الأساس ! و هذا أجمل ما في الوراثة ، كالكائن الحي تماما JJ ، و الأمر كذلك في بقية الدوال ، لاحظ أنه إذا نجحت الدالة EditStudent() في تعديل بيانات الطالب فستتغير قيمة المتغير DataChange إلى صواب true مما سيجعل الدالة WriteData() – سطر 172 تقوم بكتابة كل البيانات مرة أخرى للملف و هكذا الأمر عند حذف سجل أي طالب في case 3 (سطر 120 ).

الأسطر 181 – 185 :
عند اختيار المستخدم لإغلاق قاعدة البيانات سيتم اختبار المتغير DataChange فإذا كانت قيمته true فهذا يعني أنه تم عمل تعديل بالحذف أو الإضافة مما يوجب استدعاء الدالة العضو WriteData() لإعادة كتابة جميع البيانات مرة أخرى إلى الملف و إذا لم تنجح الدالة في ذلك فسيتم عرض رسالة تفيد المستخدم بذلك ، و في كل الحالات يتم الخروج من البرنامج .

الآن قم بالترجمة ثم شغل البرنامج و قم باختبار جميع الخيارات بصورة مكثفة ، و قد قمت بذلك - إلا أنني لم أضع الصور هنا بداعي الاختصار – و قد عمل البرنامج بصورة ممتازة...
يتبع ...
محمد الجندي غير متواجد حالياً   رد مع اقتباس
قديم 02-16-2012, 09:16 PM   #3
معلومات العضو
مدير عام
الصورة الرمزية محمد الجندي
رقم العضوية : 1
تاريخ التسجيل: Sep 2002
مجموع المشاركات : 886
محمد الجندي تم تعطيل التقييم
افتراضي

من مميزات المترجم Dev-C++ أنه يضم أداة لتحليل عمل البرنامج بحيث يخبرك عن عدد مرات و زمن استدعاء كل دالة مما يفيد في اختبار سرعة البرنامج و أدائه و تفاعل دواله ...

اضغط على القائمة Execute و اختر Profile analysis كما بالصورة :


بعد ثوان سيفتح لك نافذة شبيهة بالنافذة التالية :

قم بالاطلاع عليها ففيها معلومات مفيدة عن أداء البرنامج بحيث إذا كانت هناك دالة تستغرق وقتا أطول من اللازم – بسبب بطء أدائها أو كثرة تكرارها – تقوم بتعديلها و تحسينها و ليس هذا موضع تفصيل .

الطبقة الرابعة UserInterFace :
هذه الطبقة – و كما ذكرنا – هي ستمثل الوسيط بين المستخدم و البرنامج فهي واجهة الاستخدام التي ستتفاعل مع طلبات المستخدم و تقوم بتحقيقها و عرض النتائج ، بالتالي فسيتغير شكل الملف main بشكل كامل حيث ستقوم هذه الطبقة بمعظم مهامه ... و لكن دعونا الآن نطلع على ملف الرأس الخاص بها كالتالي :

أما الملف التنفيذي للكلاس فكالتالي :
كما تلاحظ هذا الكلاس هو المسئول عن عرض قائمة الخيارات للمستخدم و التفاعل مع اختيار المستخدم لتنفيذ طلبه ، الآن عدل الملف main حتى يصبح كالتالي :
جميل جدا ، الملف main أصبح أصغر و أجمل بعد أن قام الكلاس UserInterFace بأداء مهامه ، قم بالترجمة و التنفيذ و تجربة البرنامج بصورة مكثفة ..
و هكذا نكون على وشك الانتهاء من المشروع و لكن يتبقى لنا كلاس واحد فلنتكلم عنه :

الطبقة الخامسة : StudentCount
هذا الكلاس مهمته بسيطة جدا ألا و هي عد الطلاب بحيث نعرف العدد الإجمالي للطلاب في قاعدة البيانات و عدد الطلاب الناجحون و عدد الطلاب الراسبون ، و في الواقع الغرض منه هو دراسة المتغيرات الأعضاء الساكنة و الدوال الأعضاء الساكنة static function في الكلاس و كيفية التعامل معها لذلك سنقوم بعمل الكلاس ثم نهيء الكلاس الأول Student حتى يرث من الكلاس الجديد StudentCount و نجري بعض التعديلات على بعض الكلاسات الأخرى حتى نستفيد من الكلاس الجديد..

ملف رأس الكلاس StudentCount:
الأسطر 9 – 11 :
أعلنا عن ثلاث متغيرات ساكنة static من النوع int أولها لحفظ عدد جميع الطلاب و الثاني لتسجيل عدد الطلاب الناجحين و الثالث للراسبين، في الواقع كانت المتغيرات الساكنة تستعمل في السابق لحفظ قيم معينة و هذه القيم تحتفظ بها المتغيرات طوال فترة تشغيل البرنامج و بين الاستدعاءات المختلفة للدالات – مع إمكانية تعديل هذه القيم بعكس المتغيرات الثابتة const – و لكن الآن استخدام الكلاسات (الطبقات) أغنانا عن ذلك لأن المتغيرات الأعضاء الخاصة في الكلاس أتاحت لنا حفظ حالتها الداخلية و هي الوظيفة التي كنا نستخدم المتغيرات الساكنة لها .
مع هذا ما زال بإمكان المتغيرات الساكنة لعب دور في الكلاسات ، فالمتغيرات الساكنة في الكلاس تتم تهيئتها عندما يبدأ البرنامج و تصبح مشتركة بين جميع نسخ الكلاس مما يتيح لنا استخدامها لعد نسخ الكلاس Student و الذي - في الواقع - كل نسخة منه تمثل طالبا مختلفا ! (لا تنس أن الكلاس Student يرث الكلاس الجديد) .

الأسطر 22 – 25 :
أربع دوال إثنتان منهما تقومان بالزيادة أو الانقاص بمقدار 1 لقيمة المتغير PassSize و إثنتان تقومان بنفس الشيء للمتغير FailSize أما المتغير الثالث فسيتم التعامل معه بالزيادو أو النقصان عن طريق دالتي البناء و الهدم ! كما سترى بحول الله عند الكلام عن الملف التنفيذي للكلاس.

الأسطر 28 – 30 :
ثلاث دوال ساكنة كل واحدة منهما تعيد قيمة أحد المتغيرات الأعضاء الساكنة ، فائدة هذه الدوال الساكنة أننا سنتمكن من استدعائها بدون إنشاء كائن من الكلاس ! كما سترى لاحقا .


نأتي الآن للملف التنفيذي :
الأسطر 5 – 7 :
هذه الأسطر تقوم بتصفير المتغيرات الأعضاء الثلاث و سيتم تنفيذها أول بدء البرنامج و قبل إنشاء أي كائنات !! و ذلك لأنها متغيرات ساكنة static و سنغير قيمتها لاحقا فعند إضافة طالب جديد ستتم زيادة المتغير AllSize بمقدار 1 ثم سننظر في نتيجة هذا الطالب فإن كان ناجحا سنزيد المتغير PassSize بمقدار 1 و إلا فسنزيد المتغير FailSize بمقدار 1 ، و عند حذف الطالب سنقوم بالعكس : الإنقاص بمقدار 1 .

السطر 9 :
دالة البناء و كما تلاحظ فهي تقوم بزيادة المتغير AllSize (و الخاص بعدد جميع الطلاب) بمقدار 1 ، معنى هذا أنه في كل مرة يتم فيها استدعاء هذه الدالة فسيزيد المتغير الساكن بمقدار 1 ، لا تنس أن الكلاس Student سيرث هذا الكلاس StudentCount و بالتالي فكل كائن من الكلاس Student يتم إنشاؤه فسيتم إستدعاء دالة البناء هذه عند إنشائه ! (ثم يتم أيضا – طبعا – استدعاء دالة البناء الخاصة به ) مما يعني زيادة عدد الطلاب بمقدار 1.

السطر 11 :
دالة الهدم و التي سيتم استدعاؤها عند حذف كائن من الكلاس Student (سجل طالب) و بالتالي سيتم انقاص المتغير AllSize بمقدار 1.

السطر 13 :
الدالة SetPassSize() و التي ستقوم بزيادة المتغير PassSize بمقدار 1 ، هذه الدالة سنستعملها عند إضافة طالب جديد ناجح إلى قاعدة البيانات .

السطر 18 :
الدالة SetFailSize() و هي كسابقتها تقوم بزيادة المتغير FailSize بمقدار 1 ، نستعمل الدالة عند إضافة طالب جديد راسب! .

السطر 23 :
الدالة الساكنة GetAllSize() و التي ستعيد قيمة المتغير الساكن AllSize ، هذه الدالة نستطيع استدعاءها في أي مكان نريد من البرنامج لمعرفة العدد الكلي للطلاب من دون حتى أن نكون قد أنشأنا أي كائن منها ! و كذلك الدالتان الساكنتان الأخريتان! .

السطر 28 :
الدالة الساكنة GetPassSize() و هي تُرجع قيمة المتغير الساكن PassSize .

السطر 33 :
الدالة الساكنة الثالثة GetFailSize() و التي تعيد قيمة المتغير FailSize .

السطر 37 :
الدالة SetPassDec() و التي تقوم بإنقاص المتغير PassSize بمقدار 1 ، نستعمل هذه الدالة عند حذف طالب ناجح بحيث ينقص عدد الطلاب الناجحين بمقدار 1 .

السطر 42 :
الدالة الأخيرة SetFailDec() و التي تقوم بإنقاص المتغير FailSize بمقدار 1 ، و سنستعملها إذا كان الطالب المحذوف راسبا .

الآن أعزائي سنجري بعض التعديلات البسيطة على بقية البرنامج حتى تتم الاستفادة من كلاسنا الجديد و المثير J :
أولا الكلاس Student و الذي سنجعله يرث من الكلاس StudentCount بحيث أن أي نسخة من الكلاس Student (أي سجل طالب ) ستتضمن داخلها تلقائيا نسخة من الكلاس الجديد .
سنقوم بتعديل أول سطر في الكلاس المشتق Student حتى يصبح كالتالي :

Class Student : public StudentCount { //…

أما الكلاس dBase فسنجري عليه عدة تغييرات بحيث سيصبح ملفه التنفيذي كالتالي :
أهم التعديلات هنا الأسطر 167 – 169 حيث ترى أننا قمنا بإستدعاء الدوال الساكنة بدون استعمال أي كائن من الكلاس فقط ذكرنا اسم الدالة مسبوقا باسم الكلاس و بينهما معامل تحديد المدى :: .


أما الكلاس RW2File فسيصبح كالتالي :
تبقى الكلاس UserInterFace و الذي سنعمل به تعديلا بسيطا بحيث يصبح ملفه التنفيذي كالتالي :
بهذا و بحمد الله نكون قد انتهينا من المشروع قم بالترجمة و التنفيذ و جرب البرنامج بصورة مكثفة – و جرب حتى إدخال قيم خاطئة و غير منطقية – لترى كيف يتصرف البرنامج و هل يحتاج إلى أي تعديل ؟ ، مع العلم أنه ليس هناك برنامج كامل ! و بالتالي فإن أي برنامج يمكن تحسينه و تطويره ، و تذكر دائما أن إعادة تصميم البرنامج و تطويره من المسئوليات الحيوية الملقاة على عاتق المبرمج فقط كن صبورا ، فالأمر لا يتطلب سوى بعض الوقت و الخبرة و التفكير .
لتحميل ملفات المشروع كاملة – في صورته النهائية – مضغوط بصيغة RAR إضغط هنــــــا . و لتحميل هذا الموضوع بصيغة مستند .docمضغوط RAR من هنا .


بقي أمر أخير : يتيح لنا المترجم أن نضع الأيقونة التي نريدها للبرنامج و ذلك في خيارات المشروع – Project Options(Alt+P) حيث ستظهر لك النافذة التالية :


حيث يمكنك اختيار أيقونة من مكتبة المترجم Library أو تحديد مكان الأيقونة التي تريد على القرص Browse و هناك خيارات أخرى مثل اسم المنتج و الشركة و رقم الاصدار و غير ذلك ... ستجد الملف التنفيذي (الهدف) في نفس مجلد المشروع و بنفس الاسم و لكن بالإمتداد exe و بالأيقونة التي اخترتها ، قم بتجربته و بالتوفيق ...


في مشروعنا التالي – بحول الله – سنعمل على تطوير نفس هذا المشروع بحيث نعمل له واجهة رسومية جميلة J تسر المستخدم و تجعله يحب التعامل مع البرنامج J.

في الختام لا أطلب منكم سوى الدعوات الصالحات لي و لوالدي حيث أن هذا العمل – و الذي عملت عليه بجد لفترة تقارب الشهر – هو لوجه الله تعالي فكل من أراد نسخه فله ذلك – حتى و لو لم يذكر المصدر – بشرط أن يعمل ذلك لوجه الله و حتى يتعلم منه هو أو غيره من المسلمين ، و الحمد لله رب العالمين و صلى الله على محمد و آله و صحبه و سلم .
محمد الجندي غير متواجد حالياً   رد مع اقتباس
إضافة رد


الذين يشاهدون محتوى الموضوع الآن : 1 ( الأعضاء 0 والزوار 1)
 
أدوات الموضوع إبحث في الموضوع
إبحث في الموضوع:

البحث المتقدم
انواع عرض الموضوع

تعليمات المشاركة
لا تستطيع إضافة مواضيع جديدة
لا تستطيع الرد على المواضيع
لا تستطيع إرفاق ملفات
لا تستطيع تعديل مشاركاتك

BB code is متاحة
كود [IMG] متاحة
كود HTML معطلة

الانتقال السريع


الساعة الآن 09:19 PM

العاب ريماس ورانسي  الاتصال بنا  الأرشيف أعلى الصفحة تصفح سريع أخر الاخبار محركات البحث RSS FEED  كن صديقا لنا  تابعنا على تويتر 

Powered by vBulletin® Copyright ©2000 - 2014, Jelsoft Enterprises Ltd. TranZ By Almuhajir
Adsense Management by Losha