المساعد الشخصي الرقمي

مشاهدة النسخة كاملة : [Cpp] المفاهيم الأساسية للـ OOP من خلال لغة ++C مع الأمثلة ...


الصفحات : [1] 2 3

the.matrix.sdn
02-03-2009, 08:24 PM
http://the.matrix.sdn.googlepages.com/basmala2.gif




إخواني و أخواتي ... أحببت أن أقدم لكم بعض المفاهيم الأساسية في البرمجة كائنية التوجه Object Oriented Programming (OOP) حيث أنها تمثل مستقبل البرمجة لهذا العصر ...




سنتكلم في الموضوعات التالية:





* مقدمة.




* مفهوم الـ(كلاس) class.




* مفهوم الكبسلة .Encapsulating




* مفهوم التوارث . Inheritance




* مفهوم تعدد الأشكال . Polymorphism




* مفهوم الربط الديناميكي . DynamicBinding




* مفهوم زيادة التحميل . Overloading




* مفهوم الدوال الظاهرية . VirtualFunctions




* مفهوم القوالب . Templates




* مفهوم التعامل مع الاستثناءات .
ExceptionHandling





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




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





المراجع :



* C++ Essentials By: SharamHekmat.



* Borland C++ InDepth.



* تعلم سي بلس بلس في 10 دقائق - جيسي ليبرتي - ترجمة مكتبة جرير - الخبر.



* بعض مواقع النت ذات الصلة.





مقدمة:




كان المبرمجون قديما ً - في الأربعينات من القرن الماضي - يستخدمون لغة بيزك Basic لكتابة برامج كبيرة الحجم - صفحات طويلة من الكود - مع استخدام مبسط للمتغيرات من النوع العام Global و قد كان التعامل مع الكود في ذلك الوقت بالغ الصعوبة حيث أن قراءة الكود و فهمه مسألة معقدة جدا ً و بالتالي كان من الصعوبة بمكان تعديل أو صيانة الكود - و كما نعلم فإن صيانة البرنامج مسألة مهمة و حيوية -




في الستينات ظهر مفهوم البرمجة الهيكلية - أو الإجرائية - StructuredProgramming حيث تم استخدام أسماء واضحة للمتغيرات مع التفريق بين المتغيرات العامة Global و المتغيرات المحلية Local و تم تقسيم البرنامج إلى دوال - أو إجراءات - ذات مهام محددة و بالتالي أصبح الكود أسهل قراءة ً و فهما ً و بالتالي أصبحت عملية الصيانة أسهل . و من أشهر اللغات التي عملت بهذا النموذج : C و Pascal و Ada . و لكن هذه السهولة سهولة نسبية إذا صح التعبير لأنه يحدث أن نعدل في جزء من البرنامج فينتج خطأ غير مقصود في مكان آخر من البرنامج لذلك فنحن بحاجة إلى طريقة جديدة للبرمجة تكون فيها عملية فهم الكود و صيانته أكثر سهولة .




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




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




من أشهر لغات الـ OOP لغة سي بلس بلس و لغة جافا و سي شارب ...





آسف على الإطالة - و قد حاولت الاختصار قدر الامكان - و ذلك لأني أحببت تقديم هذه المقدمة قبل الدخول في بقية التفاصيل .





و إلى اللقاء حيث سنكمل في الحلقة القادمة:D:D:D ..



تحياتي للجميع ..

****************
-=: فهرس عام : =-

* الحلقة الأولى : مقدمة . و هي نفس هذه الحلقة :D .

* الحلقة الثانية : مفهوم الكلاس . (http://www.absba.org/showpost.php?p=7136091&postcount=11)

* الحلقة الثالثة : بقية مفهوم الكلاس + تقسيم البرنامج إلى ملفات. (http://www.absba.org/showpost.php?p=7152974&postcount=19)

الحلقة الرابعة : دوال البناء و الهدم . (http://www.absba.org/showpost.php?p=7172779&postcount=26)
حل تمرين الحلقة الرابعة. (http://www.absba.org/showpost.php?p=7200144&postcount=35)

الحلقة الخامسة : مفهوم الكبسلة و زيادة التحميل (إعادة التوصيف) . (http://www.absba.org/showpost.php?p=7219113&postcount=47)

الحلقة السادسة : زيادة تحميل دوال البناء . (http://www.absba.org/showpost.php?p=7245683&postcount=60)

الحلقة السابعة : زيادة تحميل المعاملات . (http://www.absba.org/showpost.php?p=7273083&postcount=77)

الحلقة الثامنة : زيادة تحميل المعاملات + الدوال الصديقة (http://www.absba.org/showpost.php?p=7311199&postcount=87)

الحلقة التاسعة : التوريث Inheritance (http://www.absba.org/showpost.php?p=7452820&postcount=98)

انظر هذا الموضوع ففيه تطبيق عملي لمعظم هذه المفاهيم (http://www.absba.org/showthread.php?p=7359831&posted=1#post7359831)


********
تتبع بقية الحلقات بحول الله لحين الإنتهاء من السلسلة.
لاتنسني أخي الكريم من صالح الدعاء لي و لوالدي.
=============

almstshar
02-03-2009, 09:09 PM
من اهم المواضيع

واصل هذي السلسلة..:)

eldoktor007
03-03-2009, 04:28 PM
الله عليك يا مان روعه ولله ومجهود رائع منك حتى تتم وصول المعلومه للاخوه الاعضاء والزائرين
مشكوور ولله

تقبل تحياتى اخى الكريم واستمر الى الامام

the.matrix.sdn
03-03-2009, 06:31 PM
إخواني : المستشار و الدكتور ، شكرا على المرور ....

mostsoft
03-03-2009, 06:49 PM
موضوع رائع للطرح
لكنة ليس سهلا

لذلك اتمنى فى نهاية الموضوع لو تطرح مشروع او اكثر باستخدام OOP
حيث المشاريع نفسها تعتبر من اهم الوسائل المساعدة على فهم الموضوع خاصة مثل هذا الموضوع OOP

بارك الله فيك وجزاك كل الخير
وبالتوفيق ان شاء الله

the.matrix.sdn
04-03-2009, 12:24 PM
موضوع رائع للطرح
لكنة ليس سهلا

لذلك اتمنى فى نهاية الموضوع لو تطرح مشروع او اكثر باستخدام OOP
حيث المشاريع نفسها تعتبر من اهم الوسائل المساعدة على فهم الموضوع خاصة مثل هذا الموضوع OOP

بارك الله فيك وجزاك كل الخير
وبالتوفيق ان شاء الله

مشكور أخي على المرور ... و فعلا الموضوع ليس سهلا لذلك تم طرحه حتى يستفيد الكاتب و كذلك القراء - على السواء -خاصة بمشاركة الاخوة أصحاب الخبرة ،،، و نتوقع منك ان شاء الله مزيد من المشاركات و طرح المشاريع في الحلقات القادمة قريبا بحول الله ...
تحياتي و تقديري

nahrin
04-03-2009, 05:27 PM
مشكوووووووووووووور وبارك الله فيك

CoDe.NeT
05-03-2009, 04:29 AM
بارك الله فيك

the.matrix.sdn
05-03-2009, 09:53 AM
إخواني : نهرين و كودنت : شكرا جزيلا على المرور...
مع تحياتي للجميع

the.matrix.sdn
05-03-2009, 07:38 PM
...

the.matrix.sdn
05-03-2009, 07:42 PM
الحلقة الثانية :D :



مرحبا بكم في الحلقة الثانية والتي سنواصل فيها ما انقطع من الحديث :)...



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



مفهوم الكلاس Class :



لإنشاء الكائنات في لغة سي بلس بلس نستخدم نوعا جديدا من أنواع البيانات يسمى بالكلاس class


و هذه الكلمة class ترجمتها بالعربية (الطبقة) و في بعض المصادر تترجم بـ(الفئة) - أو (التصنيف) في مصادر أخرى ، و قد فضلت هنا استخدام الكلمة نفسها (الكلاس) لعل هذا يكون أسهل و أوضح.



بعد إنشاء الكلاس - كما سنرى بعدقليل - يمكننا عمل عدد غير محدود من الكائنات - حسب الحاجة - من هذا الكلاس فالكلاس هو نوع بيانات جديد ..



لتوضيح الفكرة :


إذا كنت أريد الإعلان عن متغير اسمه SomeVariable من النوع integer فإن ذلك يتم كالتالي :




int SomeVariable;




و كذلك بنفس الطريقة نقوم بالاعلان عن الكائنات ، فمثلا إذا كان لدي كلاس اسمه Person و أردت الاعلان عن كائن :




Person Ali;




فـ Ali هو كائن من كائنات الكلاس Person


قصدي مما ذكرت أن الكلاس هو نوع بيانات جديد يحتوي على البيانات (متغيرات) و عملياتها (دوال) معا و كل كائن يحتوى داخله على هذه البيانات و على عملياتها .


بطريقة أخرى الكلاس هو مثل المخطط الذي يوضح طريقة بناء مبنىً ما أما الكائن فهو المبنى نفسه و الذي بُني حسب المخطط.



إذن كيف يمكن الإعلان عن الكلاس و مم يتركب ؟



الجواب:


طريقة الإعلان عن الكلاس تشبه السجل structure حيث أن السجل يحتوي داخله متغيرات تسمى حقول أو أعضاء كما في المثال :




struct Person {


char name[30];


char address[30];


int ID;


//...;


}Person;




أعلنا هنا عن سجل إسمه Person يحتوي داخله على مجموعة من المتغيرات (الحقول) ... أما الكلاس فهو يحتوي على متغيرات تسمى عناصربيانات بالإضافة إلى الدوال التي تعمل على هذه المتغيرات و تسمى بالدوال الأعضاء - مع ملاحظة أن بعض الكلاسات تحتوي على متغيرات فقط - وللإعلان عن الكلاس نستخدم الكلمة المحجوزة class ثم اسم الكلاس و هذا يسمى بالرأس، ثم بقية المتن وتكون بين قوسين { }


(مثال 1): الإعلان عن كلاس اسمه Person لإدخال بيانات أشخاص و عناوينهم مع رقم فريد لكل شخص:




1. class Person{


2. int ID;


3. char address[30];


4. char name[30];


5. public:


6. void EnterData(void);


7. void PrintData(void);


8.};




ملاحظة : الأرقام في المثال أعلاه - و كل الأمثلة التالية - هي للتوضيح في التحليل التالي و ليس من صلب الكود.


ملاحظة أخرى: أنا أستخدم بورلاند سي بلس بلس الاصدار الخامس في تحرير و ترجمة الكود فإذا كنت تستخدم مترجما أخر فلا بأس مع مراعاة الفروق ، كما يفضل عمل مجلد منفصل لكل مثال لسهولة المراجعة فيما بعد.



تحليل:


1. هذا رأس الكلاس و فيه استخدمنا الكلمة المحجوزة class ثم اسم الكلاس Person و يشترط في تسمية الكلاس نفس الشروط المطلوبة عند تسمية المتغيرات في لغة سي بلس بلس مع مراعاة أن الأسماء ذات المعاني لها أهمية كبيرة و خاصة في البرامج الكبيرة التي تكثر بها الكائنات و المتغيرات.
2. ~ 4. هذه عناصر البيانات و هي متغيرات يقوم المبرمج بتعريفها حسب الهدف من إنشاء الكلاس .
5. الكلمة المحجوزة public: هذه الكلمة مهمة جدا و هي ضمن ثلاث كلمات محجوزة تميز الكلاس عن السجل:


Private:

أي خاص و هذه الكلمة تفيد المترجم بأن كل ما يليها من الأعضاء (متغيرات أو دوال) محمي من الوصول إليه من خارج الكلاس – و سيأتي مزيد بيان عند الكلام عن مفهوم الكبسلة بحول الله - .


Protected:

أي محمي و ذلك يعني أن ما يليها من الأعضاء (متغيرات أو دوال ) محمي من الوصول من خارج الكلاس و لكن يمكن الوصول إليها عن طريق الكلاسات المشتقة – كما سيأتي عند الكلام عن التوريث ( الوراثة) - .


Public:

و هي الكلمة المستخدمة في هذا المثال و تعني : عام ، أي كل ما يليها من الأعضاء يمكن الوصول إليه من خارج الكلاس ، و هنا لا بد من تسجيل فرق مهم من الفروق بين الكلاس و السجل:
أعضاء السجل تكون عامة بحيث يمكن الوصول إليها مباشرة بعكس الكلاس فالوضع الافتراضي لعناصره أنها خاصة أي لا يمكن الوصول إليها من خارج الكلاس إلا بوضعها تحت الكلمة public: ، و لذلك - في هذا المثال - كل ما قبل public فهو إفتراضيا خاص لا يمكن الوصول إليه إلا عن طريق الدالتين الموضوعتين في القسم العام ، و بالتالي إذا جربت السطر التالي – في الدالة main – لن يقبله منك المترجم :


Ali.ID=1;



- حيث Ali هو كائن من كائنات الكلاس Person – فعندم الترجمة سيعطيك المترجم رسالة خطأ شبيهة بالتالي:


Error: PersonExample.cpp(136,16):'Person::ID' is not accessible


مما يعني أنه لا يمكن الوصول لهذا العضو ( المتغير ) مباشرة إلا عن طريق الدوال المحددة لذلك .

هذه خاصية مهمة جدا توفر الحماية لبيانات الكائن من أن يتم تعديلها من الخارج بطريقة غير مقصودة و هذه من خواص الـOOP المهمة.

و قد يقول قائل : ما دام أن الأعضاء يكونون على الوضع خاص تلقائيا فلماذا نستخدم الكلمة المحجوزة private: ؟ الجواب : من الأفكار الجيدة أن تستخدم الكلمة private: لتوضيح مدى الأعضاء (متغيرات أو دوال ) و ذلك لتجنب جعل الأعضاء الخاصة عامة بدون قصد و لذلك من الأفضل أن يكون مثالنا كالتالي:



class Person{


private:


int ID;


char address[30];


char name[30];


public:


void EnterData(void);


void PrintData(void);


};



مع ملاحظة أن الترتيب ليس شرطا في صحة تعريف (الإعلان عن) الكلاس بحيث يمكن أن نعلن عن الكلاس السابق كالتالي:



class Person{


public:


void EnterData(void);


void PrintData(void);


private:


int ID;


char address[30];


char name[30];


};




فكما تلاحظ تم وضع الدوال في القسم العام أولا و في هذه الحالة سيظل مفعول كلمة public: ساريا إلى نهاية الكلاس أو حتى استخدام كلمة أخرى تبطل مفعولها لذلك استخدمنا الكلمة private: قبل الأعضاء الذين نريد إخفاءهم.



6. هذا السطر يحوي رأس دالة عضو (member function) و تسمى أيضا بالطُرق أو العمليات (methods or operations ) و هذه الدالة و التي تليها ستكونان الواجهة التي سنتعامل بها مع بيانات الكلاس (الكائن) لذلك تم وضعها في القسم العام.


هذه الدالة هي التي ستقوم بإدخال البيانات لذلك سميناها EnterData و هي لا تعيد قيمة ، أو بالأصح تعيد قيمة من النوع void (لا شيء) كما أننا لا نمرر لها أي متغيرات.



7. هذا السطر كذلك يحوي رأس دالة عضو أخرى تقوم بعرض البيانات التي تم إدخالها و سميناها لذلك PrintData و هي كذلك تعيد قيمة من النوع void كما أنها لا تستقبل أي متغيرات (وسائط ) من الخارج.



8. هذا السطر به قوس الاغلاق للكلاس.


.......



نتوقف هنا في هذه الحلقة ;) و سنتكلم في الحلقة القادمة عن متن الدوال المذكورة في الكلاس (مثال 1) و كيفية الإعلان عن الكائن و استعمال دواله ،كما سنقوم بعمل ملف رأس و ملف تنفيذي لهذا الكلاس مع بيان فوائد تقسيم البرنامج إلى ملفات منفصلة و أشياء أخرى :cool: ...



تمرين :


1.1:أذكر الفروق بين السجل structure و الكلاس class .


2.1 : أذكر شروط تسمية المتغيرات في لغة سي و سي بلس بلس.



تحياتي و تقديري و إلى لقاء قريب.

abotanton
06-03-2009, 12:31 PM
بااااااارك الله فيك أخي على الدروس المهمة
وأرجوا منك المواصلة والاستمرار في إكمال مسيرة الـ OOP المعقدة

the.matrix.sdn
06-03-2009, 04:00 PM
أخي أبو طنطون : مشكور على المرور ...

abotanton
06-03-2009, 05:42 PM
إسمي ابو تنتون وليس طنطون :D

eldoktor007
06-03-2009, 05:44 PM
بااااااارك الله فيك أخي على الدروس المهمة
وأرجوا منك المواصلة والاستمرار في إكمال مسيرة الـ OOP المعقدة

الله ينور عليك يا مان

the.matrix.sdn
06-03-2009, 05:58 PM
إسمي ابو تنتون وليس طنطون :D


:D:D:D آسف أخي :D:D:D

the.matrix.sdn
06-03-2009, 06:00 PM
الله ينور عليك يا مان
و عليك أخي الدكتور 007 ...

the.matrix.sdn
08-03-2009, 10:51 AM
UP

the.matrix.sdn
11-03-2009, 12:20 PM
الحلقة الثالثة



تكلمنا في الحلقة السابقة عن الكلاس و كيفية الإعلان عنه (تعريفه) و ذكرنا المثال التالي :



class Person{
private:
int ID;
char address[30];
char name[30];
public:
void EnterData(void);
void PrintData(void);
};

و هو كلاس اسمه Person و يحتوي على مجموعة من المتغيرات في القسم الخاص (Private:) و رأسي دالتين (نموذج أولي Prorotype) .


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



DataType ClassName::FunctionName (Arguments)


{


...//


}



توضيح :


DataType : أي نوع البيانات التي ستقوم الدالة بارجاعها سواء كانت عدد صحيح أو حرفي أو غير ذلك.


ClassName: و يعني اسم الكلاس.


FunctionName: أي اسم الدالة .


(::): - مابين القوسين – و هو معامل تحديد المدي فهو يخبر المترجم أن الدالة – على اليمين – موجودة بالكلاس المكتوب اسمه – على اليسار و بدون استعمال هذا المعامل فستحصل على رسالة خطأ من المترجم بأنه لم يجد هذه الدالة.


Arguments: أي الوسائط التي سيتم تمريرها للدالة.


ثم يلي ذلك متن الدالة بين قوسي البداية و النهاية {}.



و الآن سنقوم بتطبيق ذلك على دالتينا :



الدالة الأولى :


هذه الدالة تقوم بإدخال البيانات لعناصر بيانات الكلاس و بالتالي سنستعمل كائنات الادخال و الاخراج القياسية: cout,cin ، هذه الدالة تعيد قيمة من النوع (لا شيئ) كما أننا لا نمرر لها أي وسائط:



1.void Person::EnterData(void)
{
2.char cr;
cout<<"\nEnter ID: ";
cin>>ID;
cin.get(cr);
cout<<"\nEnter Name: ";
3.cin.get(name,29);
4.cin.get(cr);
cout<<"\nEnter Address: ";
cin.get(address,29);
}

ملاحظة : الأرقام ليست من صلب الكود و إنما هي للتوضيح التالي فقط و ذلك في هذا المثال و كل الأمثلة التالية.


توضيح :


1. رأس الدالة و تم استخدام معامل تحديد المدى :: لإعلام المترجم أن هذه الدالة هي عضو في الكلاس Person .


2. هذا المتغير سنستعمله مع الأمر cin.get() لاحقا و الغرض منه هو تنظيف الـBuffer الخاص بهذه الدالة قبل استعمالها.


3. عند استعمال معامل الإدراج << مع الكائن cin فإنه يقرأ الأحرف المدخلة حتى الوصول إلى مسافة فارغة white space و لن يقرأ ما بعدها بمعنى أنني إذا أردت أن أكتب الاسم :


Mohammed Ali فلن يقرأ إلا الكلمة الأولى فقط Mohammed ، لهذا استعملت الدالة cin.get(name,29) و التي ستقوم بقراءة كل النص الذي تم ادخاله و هذه الدالة نمرر لها ثلاث وسائط الأول هو المتغير الذي سندرج النص به ثم فاصلة عادية ثم الوسيط الثاني و هو عدد الأحرف المطلوب و استعملنا هنا 29 مع أن حجم المصفوفة 30 لأنه كما نعلم فأي مصفوفة في لغة سي بلس باس تستخدم حرفا مخفيا للدلالة على نهايتها و هذا الحرف له القيمة الرقمية صفر'0\' .


أما الوسيط الثالث فهو اختياري و نضع به حرف معين يحدد آخر حرف سيتم قراءته و نحن هنا لا نحتاج لهذا الوسيط..


لاحظ أننا عند استعمالنا الكائن cin لإدراج قيمة عددية للمتغير ID استعملنا معه معامل الادراج بالصورة الاعتيادية.


4. هنا استعملنا المتغير cr مع الدالة cin.get() و ذلك لعمل تنظيف للـbuffer الخاص بهذه الدالة و ذلك تمهيدا لاستعمالها.



الدالة الثانية:


و هي دالة سهلة و بسيطة تقوم فقط بعمل إخراج للبيانات التي سبق إدخالها كالتالي:



void Person::PrintData(void)
{
cout<<"\nID: "<<ID<<endl;
cout<<"Name: "<<name<<endl;
cout<<"Address: "<<address<<endl;
}
و الآن نأتي إلى الدالة main و هي كما نعلم الدالة الرئيسية في البرنامج و ستكون في مثالنا كالتالي :


void main(void)
{
1.Person Ali;
2.Ali.EnterData();
3.Ali.PrintData();
4.getch();
}
توضيح:


1. في هذا السطر أعلنا عن الكائن Ali من الكلاس Person هذا الكائن عند إنشائه سيحمل بداخله كل الأعضاء (متغيرات أو دوال ) الذين سبق و أعلنا عنهم في الكلاس Person.


2. في هذا السطر استخدمنا دالة عضو في الكائن حيث ذكرنا اسم الكائن أولا ثم معامل النقطة (.) ثم اسم الدالة المطلوب تنفيذها ، و بهذا السطر استدعينا دالة ادخال البيانات لإدخال البيانات للكائن Ali لاحظ أنه لايجوز إدخال البيانات مباشرة بدون هذه الدالة كما سبقت الاشارة إليه لأن هذه البيانات تقع في القسم الخاص من الكائن.


3. و هنا استخدمنا دالة عضو أخرى لعرض البيانات التي سبق إدخالها.


4. هذه الدالة تتبع لملف الرأس (مكتبة) conio.h و استعملتها هنا فقط لتثبيت الشاشة حيث أفضل العمل في البورلاند J ،و لكن إذا كنت تستخدم فجوال سي بلس بلس فلن تحتاج لها هنا.



جرب كتابة الكود السابق – الكلاس و دواله و الدالة main في برنامج واحد و انظر كيف سيعمل البرنامج حيث أن اختبار البرنامج من العمليات المهمة التي ينبغي إلتزامها عند كتابة و تطوير البرمجيات.



.......



تقسيم البرنامج إلى ملفات:



من المهم تقسيم البرنامج إلى عدة ملفات برمجة و ذلك لسببين :


1- عندما يصبح البرنامج كبيرا ثم تجري عليه تعديلا فإنك ستحتاج إلى إعادة ترجمة الملف الذي تم تعديله فقط مما سيوفر عليك الوقت.


2- يمكنك استخدام ملفات البرمجة في أكثر من برنامج لكي تستفيد من الأوامر التي كتبتها من قبل .



يتكون ملف البرمجة من ملفين الأول هو ملف الرأس header و ينتهي بالامتداد .h أما الثاني و هو ملف التنفيذ implementation و ينتهي بالامتداد .cpp


و الآن نريد تطبيق هذا الكلام على كلاسنا الصغير J هذا حتى تتضح الفكرة لأنها مهمة جدا :



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




http://absba2.absba.org/teamwork4//761593/PersonClass1.PNG.jpg


إحفظ الملف باسم مناسب في المجلد الذي قمت بإعداده سابقا على أن يكون يالامتداد .h مثلا PersonClass.h


ثم قم بكتابة الكود التالي:




1.#ifndef PersonClassH
2.#define PersonClassH
class Person{
private:
int ID;
char address[30];
char name[30];
public:
void EnterData(void);
void PrintData(void);
};
3.#endif

توضيح :


1. هذا أول سطر في ملف الرأس لهذا الكلاس و يحتوي على توجيه ما قبل المعالجة و يعني هذا التوجيه:" إذا لم يتم التعريف " أو بمعنى آخر : هذا التوجيه يخبر المعالج بأنه إذا لم يتم تعريف الاسم التالي له (هنا PersonClassH) يجب تمرير الأسطر التالية له (حتى #endif ) إلى المترجم.


2. هذا السطر هو الذي يجعل السطر 1 مفيداً . ففي هذا السطر يتم تعريف الاسم (PersonClassH) . الفكرة هنا أننا يمكن أن نستخدم ملف الرأس عدة مرات من داخل عدة ملفات في نفس البرنامج و نريد أن نضمن أن يتم تعريف الاسم PersonClassH مرة واحدة فقط ، لذلك فإننا نختبر لنرى إن كان هذا الاسم معرفا من قبل أم لا . فإذا كان قد تم تعريفه ، فإن المترجم سيتجاهل محتويات ملف الرأس ، و إذا لم يكن قد تم تعريفه فإننا نقوم نقوم بتعريفه و نضع فيه الاعلان عن الكلاس.


6.هو نهاية ملف الرأس.


ملاحظة:


اسم الكلاس يمكن أن يعمل كمساحة أسماء namespace لذلك فإن مساحات الأسماء ليست شائعة الاستخدام في ملفات الرأس و التنفيذ الخاصة بالكلاسات.



و الآن لا تنس حفظ الملف بعد كتابة الكود به ثم ترجمته compile و ليس تنفيذه run !.



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


قم بعمل ملف جديد في البورلاند بنفس الطريقة السابقة و قم بحفظه باسم PersonClass.cpp في نفس المجلد السابق ثم انسخ الكود التالي به :



1.#include <iostream.h>
2.#include "PersonClass.h"
void Person::EnterData(void)
{
char cr;
cout<<"\nEnter ID: ";
cin>>ID;
cin.get(cr);
cout<<"\nEnter Name: ";
cin.get(name,29);
cin.get(cr);
cout<<"\nEnter Address: ";
cin.get(address,29);
}
void Person::PrintData(void)
{
cout<<"\nID: "<<ID<<endl;
cout<<"Name: "<<name<<endl;
cout<<"Address: "<<address<<endl;
}
توضيح :

1. هذا السطر ضمنّا به ملف الرأس iostream.h و ذلك حتى نستخدم كائني cout و cin للإخراج و الادخال.


2. هذا السطر المهم ضمنّا به ملف الرأس الذي قمنا بتكوينه في الخطوة السابقة PersonClass.h و قد وضعناه بين علامتي تنصيص " " حتى نخبر المترجم أنه موجود في نفس المجلد بخلاف ملف الرأس الذي قبله بين علامتي <> حيث تعني هذه أن الملف موجود في مجلد المكتبات القياسي الخاص بالمترجم.


و في السطور التالية الدالتين الأعضاء بكلاسنا الصغير و قد سبق الكلام عليهما .


لا تنس أيضا حفظ الملف بعد كتابة الكود به ثم ترجمته compile .



بهذه الطريقة نكون قد عملنا ملف برمجة منفصل خاص بهذا الكلاس و يحتوي على ملفين الأول ملف رأس و الثاني ملف تنفيذ.



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



و الآن نريد تجربة ما عملناه على مشروع صغير نقوم فيه باستخدام الكلاس (الكائن) الذي قمنا باعداده :


في البورلاند قم بالضغط على قائمة File ثم New ثم من القائمة المنسدلة اختر Project و ذلك لعمل مشروع جديد ، ستظهر لك نافذة إعدادات المشروع قم بضبطها كما في الصورة :



http://absba2.absba.org/teamwork4//761593/personclass2.PNG



1. غير اسم المشروع إلى اسم مناسب مثلا person مع المحافظة على الامتداد .ide و تأكد أن المشروع محفوظ في المجلد المحدد مسبقا.


2. اختر Application.exe


3. برنامجنا سيعمل على الكونسول لذلك اختر هنا dos standard .


4. اختر compact أي مضغوط.


ثم اضغظ ok .


سيقوم البورلاند بإنشاء ملف تنفيذي بنفس الاسم person.exe و إنشاء ملف person.cpp بنفس الاسم و عند الضغط – دبل كليك – على الملف person.cpp سيقوم بفتحه لك و هو ملف نصي فارغ كما في الصورة :




http://absba2.absba.org/teamwork4//761593/personclass3.JPG


الآن قم بكتابة الكود التالي في الملف person.cpp ثم حفظه :




#include <iostream.h>
#include "PersonClass.h"
void main(void)
{
char choice;
1.Person *person;
2.do
{
3.person=new Person;
4.person->EnterData();
5.person->PrintData();
cout<<"\nEnter n to Exit or any key - then Enter- to continio.. ";
cin>>choice;
}
while(choice!='n');
}

توضيح :


1. هنا أعلنا عن مؤشر اسمه pointer من نوع الكلاس Person و ستلاحظ لاحقا عند الترجمة أنه لن يعطيك رسالة خطأ أنه لم يجده أو لم يعرفه لأننا استعملنا ملف الرأس الخاص به في الأعلى.


2. استعملنا عبار do while حيث نريد تكرار الكود طالما لم يضغط المستخدم الحرف n للخروج من البرنامج .


3. يقوم المترجم بانشاء كائن من الكلاس Person و تخزين عنوانه في المؤشر الذي سبق تعريفه و ذلك باستخدام الكلمة المحجوزة new .


4. و 5. في هذين السطرين استعملنا دالتي الكائن و ذلك عن طريق الاشارة إليهما بالمعامل-> مسبوقا بالمؤشر الذي يشير إلى الكائن.



و الآن و قبل الترجمة لابد من إضافة ملف البرمجة الخاص بالكلاس إلى المشروع :


في نافذة المشروع على الجهة اليمين من البورلاند إضغط بالزر اليمين على اسم الملف person.exe و اختر Add node من القائمة سيفتح لك نافذة بها الملفات اختر الملف التنفيذي للكلاس PersonClass.cpp ...



الآن قم بعمل بترجمة الكود السابق ثم نفذ run و اختبر النتائج....



نتوقف هنا أعزائي J لنواصل في الحلقة القادمة ...

------------------------------------
تم رفع الصور على السيرفر بواسطة المشرف العام freethink

mos
11-03-2009, 02:12 PM
بارك الله فيك

اخى العزيز ننتظر منك المزيد لأهمية الموضوع

the.matrix.sdn
11-03-2009, 05:55 PM
بارك الله فيك

اخى العزيز ننتظر منك المزيد لأهمية الموضوع

مشكور أخي الغالي على تشريف الموضوع :)
مع تحياتي...

SmoothCriminal
11-03-2009, 09:36 PM
ممتاز شرحك جدا
بارك الله فيك

أميلكاروس
11-03-2009, 11:10 PM
بارك الله فيك

the.matrix.sdn
12-03-2009, 09:39 AM
ممتاز شرحك جدا
بارك الله فيك

مشكور يا أستاذ ، شرفت الموضوع .


بارك الله فيك

و فيك أخي العزيز .

the.matrix.sdn
13-03-2009, 05:40 PM
للرفع ...

the.matrix.sdn
16-03-2009, 08:45 PM
الحلقة الرابعة :


دوال البناء و الهدم : Constructor & Destructor

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

يجب الانتباه للتالي عند استخدام دوال البناء :
- دوال البناء ( يجب ) أن تكون بنفس اسم الكلاس كما أنها لا تعيد قيمة من أي نوع و لا حتى void !! .
- يمكن تمرير وسائط Arguments لدوال البناء كما أنه يمكن زيادة تحميلها أي يمكن وجود أكثر من دالة بناء - كما سيأتي بحول الله عند الكلام عن زيادة التحميل Overloading -
- يجب الإعلان عن دالة البناء كجزء من القسم العام public في الكلاس و في الغالب يتم ذلك بعد الإعلان عن المتغيرات العامة و قبل أي دالة عامة أخرى ، و من النادر أن يتم الإعلان عن دالة البناء في القسم الخاص private - رغم أن هذا له بعض الاستخدامات - و ذلك لأن هذا سيمنع إنشاء نسخة من الكلاس (الكائن) .

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



1.class Add{
2.private:
3.int xNum;
4.int yNum;
5.public:
6.Add(int x,int y)
{
7. xNum=x;
8.yNum=y;
}
9.int PrintSum(void)
{
10. return (xNum+yNum);
}
};

توضيح :
1. قمنا بالإعلان عن كلاس إسمه Add .
2. الكلمة المحجوزة private: و تعني أن ما يليها من الأعضاء خاص لايمكن الوصول إليه إلا عن طريق الدوال الأعضاء.
3. & 4. قمنا بالاعلان عن متغيرين من النوع integer سنستخدمهما لتخزين عددين بغرض جمعهما .
5. الكلمة المحجوزة public: و تعني أن ما يليها من الدوال - أو المتغيرات - الأعضاء عام .
6. انتبه فهنا مربط الفرس ! هذه دالة بناء و كما تلاحظ فهي بنفس اسم الكلاس Add كما أنها لا تعيد قيمة من أي نوع ! كما أننا قد مرّرنا لها وسيطين من النوع integer - عادة ما تكون دالة البناء الإفتراضية بدون وسائط لكننا نستطيع إضافة الوسائط عند الحاجة لذلك! - .
7. في هذا السطر قمنا بتهيئة المتغير العضو الأول xNum بحيث أسندنا إليه قيمة المتغير الوسيط x .
8. و أيضا هنا قمنا بتهيئة المتغير العضو الثاني yNum بإسناد قيمة المتغير الوسيط الثاني y إليه.
9. أعلنا عن دالة عضو من النوع integer و لم نمرر لها وسائط.
10. متن الدالة العضو و حيث أنه قصير - سطر واحد فقط - قمنا بإدراجه في متن الكلاس ، هذا السطر يقوم بإعادة - ارجاع - مجموع المتغيرين الأعضاء.

ملاحظة :
هناك طريقة أخرى شائعة لتهيئة المتغيرات الأعضاء في دالة البناء بحيث يمكن كتابة دالة البناء في المثال السابق كالتالي:


Add(int x, int y): xNum(x),yNum(y)
{ } ;



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

و الآن نأتي ملف main نستخدم فيه الكلاس أعلاه :




void main(void)

{

int x,y;

cout<<"\nEnter First number: ";

cin>>x;

cout<<"\nEnter Second number: ";

cin>>y;

1.Add Sum(x,y);

2.cout<<"\nSummation = "<<Sum.PrintSum()<<endl;

getch();

}




توضيح :
1. قمنا هنا بالإعلان عن كائن أسميناه Sum من الكلاس Add و قمنا مباشرة بتمرير وسيطين له و هما رقمين مطلوب جمعهما ، يتم استدعاء دالة البناء تلقائيا عند إنشاء كائن من كلاس . لن تحتاج إلى كتابة أي أوامر بنفسك - و لله الحمد - لاستدعاء هذه الدالة ، حيث يتم استدعاؤها بواسطة الأوامر التي يقوم المترجم بتوليدها عند إنشاء نسخة من الكلاس.
2.قمنا باستدعاء دالة ارجاع ناتج الجمع PrintSum و لاحظ أننا استعملنا الكائن cout لتمرير الناتج مباشرة باستعمال هذه الدالة.
الآن أخي الدارس قم بكتابة الكود كاملا و اختبر النتائج.

المثال السابق بين لنا استخدام دوال البناء لتهيئة المتغيرات الأعضاء بقي لنا الاستخدام الآخر و هو حجز مواقع في الذاكرة في وقت التشغيل من الكومة Heap و سنوضحه في مثال و لكن بعد الكلام عن دوال الهدم .
ملاحظة:
الحجز الديناميكي للذاكرة –أثناء وقت التشغيل - أي على الكومة Heap مفيد جدا في تطبيقات هياكل البيانات Data Structure المبنية على السلسلة (القوائم) المتصلة Linked List و أشجار البحث الثنائية Binary Search Tree و أمثالهما من التطبيقات المهمة.

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

في الغالب نحتاج إلى دالة الهدم عندما نقوم بتخصيص متغيرات أعضاء من الكومة Heap – و ذلك باستخدام المعامل new في دالة البناء – فيجب أن نستخدم المعامل delete لحذف المتغيرات الأعضاء التي قمنا بإنشائها من الكومة و ذلك تجنبا للوقوع في فخ تسرب الذاكرة Memory Leak ، و نقصد به أن تمتلىء الذاكرة بمتغيرات قمنا بحجزها على الكومة ثم لم نعد بحاجة لها فلابد من حذفها حتى لا تنفد الذاكرة مع الوقت مما يسبب عبئا على النظام – و هذه مسألة مهمة جدا و حيوية - لابد للمبرمج الذكي من الانتباه لها - إلا أن المكان لا يتسع للإطالة بها - .
دالة الهدم مثلها مثل دالة البناء لابد أن تكون بنفس اسم الكلاس كما أنها لا تعيد قيمة من أي نوع ، و هناك شيء إضافي في دالة الهدم و هو ان اسمها لابد أن يكون مسبوقا بالرمز ~ كما أن دالة الهدم لا تحمل أي وسائط أي لا يمكن تمرير أي متغيرات لها و أيضا فدالة الهدم – على خلاف دالة البناء – لايمكن زيادة تحميلها كما سنراه بحول المولى عز و جل عند الكلام على الـOverloading.

و الآن نأتي إلى مثال يوضح لنا أولا إستخدام دالة بناء لحجز مواقع في الذاكرة من الكومة Heap ثم استخدام دالة هدم تقوم بتحرير هذه الذاكرة عند تدمير الكائن ( إنتهاء البرنامج )...

مثال 3:
المطلوب عمل كائن يقوم بإنشاء سلسلة متصلة Linked List و يقوم بتخزين مجموعة من الأرقام داخلها ثم يقوم بعرض هذه السلسلة على الشاشة.
الحل:

أولا الإعلان عن الكلاس و أعضاءه :
و فيه سنستعمل السجل structure داخل الكلاس حيث أن القوائم المتصلة هي مجموعة من السجلات المرتبطة في شكل سلسلة و كل سجل (عقدة) يحتوي بداخله على حقلين الأول لتخزين البيانات و الثاني هو عبارة عن مؤشر يشير للعقدة التالية .


1.class CList
2.{
3.public:
4.struct node
5.{
6.int data;
7.struct node* next;
8.};
9.CList(const int thesize):size(thesize){Head=new node; };
10.~CList(void){ delete Head; };
11.void Create(void);
12.void Display(void);
13.private:
14.int size;
15.struct node* Head;
16.};



توضيح :

1. أعلنا عن كلاس بالإسم :CList .
2. قوس البداية للكلاس.
3. الكلمة المحجوزة public: و تعني أن جميع ما يليها من الأعضاء يحمل الصفة : عام.
4. هنا أعلنا عن سجل structure يحمل الاسم node
5. قوس البداية للسجل node.
6. الحقل الأول بالسجل هو متغير من النوع integer و هذا المتغير هو الذي سيحمل الأرقام التي نريد إدخالها في السلسلة.
7. الحقل الثاني في السجل و هو مؤشر من نفس نوع السجل و الغرض منه هو تخزين عنوان العقدة التالية في السلسلة .
8. قوس إغلاق السجل node.
9. هنا دالة بناء و قد قامت بوظيفتين مهمتين ، أولاهما أنها قامت بتهيئة متغير عضو بالكلاس – لم نصل إليه بعد بالرقم 14 – و هو من النوع integer و سيأتي الكلام عنه بعد قليل و لكن لاحظ أننا أستعملناه هنا مع أن تعريفه يأتي لاحقا في الكلاس و بالرغم من ذلك فقد تمكن المترجم من معرفته ! و قد مرّرنا وسيط لدالة البناء ، هذا الوسيط نقوم عن طريقه بتهيئة المتغير العضو size كما لاحظ أيضا أن الإعلان عن الوسيط - الذي هو من النوع integer – أعلنا عنه باستخدام الكلمة المحجوزة const و التي تعني ثابت و بهذا نضمن أن القيمة التي سنمررها ستكون ثابتة بحيث لن يسمح المترجم بتعديلها و بالتالي نتجنب حدوث خطأ غير مقصود!. أما الوظيفة الأخرى التي قامت بها دالتنا هذه فهي أنها قامت بحجز ديناميكي في الذاكرة أي على الكومة Heap و ذلك باستعمال المعامل new و هذه هي أول عقدة في السلسلة حيث أن المؤشر Head هو رأس السلسلة حيث يشير إلى أول عناصرها كما سيأتي بالرقم 15.
10. هذه دالة هدم كما تلاحظ حيث سبقها الرمز ~ كما أنها تحمل نفس اسم الكلاس و كما تلاحظ أنها لا تحمل أي وسائط و قد استعملنا المعامل delete و ذلك لحذف المؤشر Head و بالتالي تحرير الذاكرة التي سبق حجزها بالمعامل new في سطر 9.
11. هذا السطر رأس دالة عضو تحمل الاسم Create و هي الدالة التي سنستخدمها لإدخال عناصر السلسلة.
12. و هذا أيضا رأس دالة عضو أخرى بالاسم Display و هي ستقوم بعرض عناصر السلسلة المتصلة.
13. الكلمة المحجوزة private: و هي تعني أن جميع ما يليها من الأعضاء هو خاص..
14. أعلنا عن متغير اسمه size من النوع integer و هذا المتغير سيحمل عدد عناصر (عقد) السلسلة المتصلة و هذا المتغير سيتم تعيين قيمته (تهيئته) في دالة البناء المذكورة بالأعلى بحيث أننا نحدد عدد العقد عند إنشاء الكائن بواسطة دالة البناء ، و قد وضعناه في القسم الخاص حتى نضمن عدم تغيير عدد العقد من الخارج أي من خارج الكلاس بالخطأ.
15. هذا متغير عضو و هو مؤشر من نفس نوع السجل و في الحقيقة فهو أهم متغير لدينا حيث يشير إلى أول عقدة في السلسلة و عن طريقه يمكن الوصول لجميع عقد السلسلة و بالتالي وضعناه في القسم الخاص لحمايته..
16. هذا قوس إغلاق الكلاس.

ثانيا ً متن الدوال الأعضاء التي لم يُذكر متنها بالكلاس – و ذلك لطولها - :


void CList::Create(void)
{
1.node *link,*ptr;
2.cout<<"\nEnter Data for node (1): ";
3.cin>>(Head->data);
4.Head->next=NULL;
5.link=Head;
6.for(int i=1;i<size;i++)
{
7.ptr=new node;
8.cout<<"\nEnter Data for node ("<<(i+1)<<"): ";
9.cin>>(ptr->data);
10.ptr->next=NULL;
11.link->next=ptr;
12.link=ptr;
}
};
void CList::Display()
{
13.node *pt;
14.pt=Head;
15.for(int i=0;i<size;i++)
{
16.cout<<"Key(data) No.("<<(i+1)<<"): "<<(pt->data)<<endl;
17.pt=pt->next;
}
};

توضيح :
1. هذا أول سطر في متن الدالة Create و فيه أعلنا عن مؤشرين من نفس نوع السجل أولهما اسمه link و هو عبارة عن مؤشر مؤقت للربط بين العقد كما سنرى بعد قليل و الآخر ptr و نستخدمه عند إنشاء عقدة جديدة.
2. عبارة تطلب من المستخدم إدخال عنصر العقدة الأولى و قد ميزنا العقدة الأولى هنا من بقية العقد لأهميتها حيث أن جميع العقد الأخرى متصلة بها.
3. قمنا هنا بإدخال عنصر العقدة الأولى باستخدام الكائن cin و لاحظ أننا استخدمنا المؤشر Head و الذي ذكرنا سابقا أنه يُشير للعقدة الأولى.
4. هنا نجعل المؤشر next في العقدة الأولى يساوي NULL أي خالي و ذلك لأننا لا نعلم كم عدد عقد السلسلة بالضبط لذلك فكل مؤشر next سنعطيه قيمة أولية NULL ثم نغير هذه القيمة لاحقا إذا كانت هناك عقدة تالية.
5. هنا أسندنا قيمة المؤشر Head إلى المؤشر link و ذلك تمهيدا لربط هذه العقدة بالعقدة التالية إن وُجدت.
6. استعملنا هنا حلقة تكرار for لإدخال بقية عناصر العقد التالية و لاحظ هنا أن عداد الحلقة و هو المتغير i أعطيناه القيمة الأولية 1(واحد) و ليس القيمة 0 (صفر) كالمعتاد ، حيث أن أول عقدة قد سبق إنشاءها لذلك فهذه الحلقة تقوم بإنشاء بقية العقد التي تلي العقدة الأولى ، أما القيمة النهائية للحلقة فيحكمها المتغير size و هو كما سبق متغير عضو خاص في الكلاس يحمل عدد العقد و الذي قمنا يتهيئته سابقا.
7. نقوم هنا بإنشاء عقدة جديدة و تخزين عنوانها في المؤشر ptr .
8. هذا السطر يطلب من المستخدم إدخال عنصر العقدة و يقوم بذكر ترتيب العقدة المطلوب إدخال عنصرها.
9. نقوم بإدخال العنصر باستعمال الكائن cin و بدلالة المؤشر ptr .
10. كما سبق بالرقم 4..
11. هذا السطر المهم يتم به الربط بين العقدة السابقة و العقدة الحالية.
12. نخزن عنوان العقدة الحالية في المتغير link تمهيدا لربطها بالعقدة التالية ..
13. هذا أول سطر في دالة العرض Display و فيه نعلن عن مؤشر اسمه pt من نفس نوع السجل.
14. هنا نقوم بإسناد قيمة المؤشر Head إلى المؤشر الذي أعلنا عنه و ذلك تجنبا لتغيير قيمة المؤشر Head لأهميته.
15. هنا حلقة for كالسابقة إلا أنها تقوم بعرض عناصر جميع العقد بدلالة المؤشر pt .
16. هذا السطر يغير قيمة المؤشر pt الذي يحمل عنوان العقدة الحالية إلى عنوان العقدة التالية.

و بهذا – و الحمد لله – نكون قد انتهينا من الكلاس و دواله بالتفصيل الممل J و باقي الآن الدالة main التي سيعمل بها الكائن :

void main(void)
{
1.clrscr();
2.CList s(6);
3.s.Create();
clrscr();
4.s.Display();
getch();
}


توضيح :

1. هذه دالة لمسح الشاشة و هي تتبع للمكتبة conio.h و هي اختيارية.
2. أعلنا هنا عن كائن اسمه s من الكلاس CList و قد مررنا له القيمة 6 مباشرة أي أن هذا الكائن سيحمل 6 عقد متصلة Linked List .
3. قمنا باستدعاء الدالة العضو Create حتى تقوم بإنشاء السلسلة – أي إدخال عناصرها - .
4. و الآن استدعينا دالة العرض لعرض هذه العناصر بالسلسة المتصلة.
ملاحظة :
لاحظ أن الدالة main و باستخدام مفهوم الكلاس أصبحت أكثر وضوحا و أسهل فهما و أقل حجما ...
و الآن أخي الدارس عليك كتابة كامل الكود لهذا البرنامج لتجربته و اختباره.

نتوقف هنا بحول الله لنواصل في الحلقة القادمة ..

لحظة !! J تمرين للأذكياء فقط :D:D;) ! :
1. قم بتعديل الكلاس في المثال 2 و الذي يقوم بعملية جمع عددين ليصبح بإمكاننا جمع أو طرح أو ضرب أو قسمة عددين.
2. قم بتعديل الكلاس في المثال 3 بحيث نتمكن من إضافة عقدة جديدة في آخر السلسلة أو حذف عقدة موجودة بالسلسلة.
ملاحظة : أرجو من الاخوة النجباء J :D الذين يقومون بحل التمرين أن يضعوه هنا حتى نتناقش فيه لتعم الفائدة.

تحياتي للجميع ;).

eldoktor007
17-03-2009, 08:06 AM
مشكووور يا مان على الحلقه الرابعه
وعقبال الخامسه
بس يا ريت تبقى تشرح على برنامج ++Borland C
وترفق مشاريع وامثله حتى تفيد الاخوه اكثر

abotanton
17-03-2009, 08:19 AM
مشكور مشكور مشكور
الله يبارك فيك أخي
وانتظر المزيد من الشرح عن البرمجة الشيئية
جزاك الله خيراً

mos
17-03-2009, 08:34 AM
السلام عليكم

الله يفتح عليك
وننتظر دائما منك الاضافة سواء فى هذا الموضوع او اى موضوع جديد لما تطرحه من مواضيع مهمة ومميزه

شكرا

the.matrix.sdn
17-03-2009, 10:34 AM
مشكووور يا مان على الحلقه الرابعه
وعقبال الخامسه
بس يا ريت تبقى تشرح على برنامج ++Borland C
وترفق مشاريع وامثله حتى تفيد الاخوه اكثر

الشكر لك أنت يا مان على مرورك و تشجيعك ...
أنا أعمل على برنامج البورلاند فعلا و قد كنت كتبت في الحلقة السابقة عن عمل مشروع جديد على البورلاند بصورة مبسطة و بالتالي فكل الأمثلة التالية يمكن عملها بنفس الطريقة ;) ..
بالنسبة للمشاريع فمعك حق أستاذي حيث أن المشاريع تساعد بشكل ملحوظ على الفهم العميق خاصة في لغات البرمجة القوية مثل سي ++ و لكن في الحقيقة فقد أحببت أولا عرض المفاهيم الأساسية بشكل عام و سريع مع ذكر أمثلة مُبسطة ثم بعد ذلك - بحول الله - نقوم بدراسة مشروع أو أكثر بعد أن نكون قد مررنا على المفاهيم الأساسية و على العموم فسأعمل على زيادة الأمثلة في الحلقات القادمة :) إن شاء الله.
تحياتي و تقديري لك أخي الكريم .



مشكور مشكور مشكور
الله يبارك فيك أخي
وانتظر المزيد من الشرح عن البرمجة الشيئية
جزاك الله خيراً

العفو أخي الفاضل و جزاك الله ألف خير.
تحياتي و تقديري..



السلام عليكم

الله يفتح عليك
وننتظر دائما منك الاضافة سواء فى هذا الموضوع او اى موضوع جديد لما تطرحه من مواضيع مهمة ومميزه

شكرا

و عليكم السلام و رحمة الله و بركاته
الله يحفظك أخي و شكرا على مرورك و كلماتك الجميلة.

Jinan
17-03-2009, 09:07 PM
أكمل على بركة الله..

شرح سهل للمبتدئين..

the.matrix.sdn
18-03-2009, 07:24 PM
أكمل على بركة الله..

شرح سهل للمبتدئين..

شكرا أخي (أختي) الكريمـ(ـة) على المرور و بالتوفيق ...

أيوب1980
18-03-2009, 08:56 PM
بارك الله فيك

the.matrix.sdn
21-03-2009, 02:22 PM
بارك الله فيك
و فيك أخي الكريم و شكرا على المرور .

the.matrix.sdn
24-03-2009, 08:31 PM
السلام عليكم و رحمة الله و بركاته

ما زالت هذه السلسلة مستمرة و سنرى قريبا الحلقة الخامسة - إن شاء الله - و قبل ذلك أضع هنا حل التمرين الخاص بالحلقة السابقة - الرابعة - كالآتي :

السؤال الأول :

1. قم بتعديل الكلاس في المثال 2 و الذي يقوم بعملية جمع عددين ليصبح بإمكاننا جمع أو طرح أو ضرب أو قسمة عددين.

الحل:

قمنا بتعديل الكلاس ليصبح كالتالي:

أولا ملف الرأس للكلاس:

#ifndef CalcClassH
#define CalcClassH
#include <iostream.h>
class Calc{
private:
int xNum;
int yNum;
float Result;
public:
Calc(int x,int y):xNum(x),yNum(y){}
void Add(void)
{
Result=xNum+yNum;
}
void Subt(void)
{
Result=xNum-yNum;
}
void Mult(void)
{
Result=xNum*yNum;
}
void Devid(void)
{
if(yNum!=0) Result=xNum/yNum;
else {
cout<<"\nError ! can not devid by Zero"<<endl;
Result=0;}
}
float PrintResult(void)
{
return (Result);
}
};
#endif


و قد أضفنا عدة دوال له للجمع Add و الطرح Subt و الضرب Mult و القسمة Devid و لإعطاء النتيجة PrintResult كما أضفنا متغير عضو في القسم الخاص و أسميناه Result لتخزين النتيجة.

أما ملف main :

#include <iostream.h>
#include <conio.h>
#include "CalcClass.h"
void main(void)
{
clrscr();
int x,y;
char choice;
cout<<"\nEnter Your First Number: ";
cin>>x;
cout<<"\nEnter Your Second Number: ";
cin>>y;
Calc Number(x,y);
cout<<"\n+ To Add."<<endl;
cout<<"- To Substract."<<endl;
cout<<"* To Multiply."<<endl;
cout<<"/ To Devid."<<endl;
cout<<"\t\tEnter Your Choice: ";
cin>>choice;
switch(choice)
{
case '+':
Number.Add();
cout<<"\nResult= "<<Number.PrintResult()<<endl;
break;
case '-':
Number.Subt();
cout<<"\nResult= "<<Number.PrintResult()<<endl;
break;
case '*':
Number.Mult();
cout<<"\nResult= "<<Number.PrintResult()<<endl;
break;
case '/':
Number.Devid();
cout<<"\nResult= "<<Number.PrintResult()<<endl;
break;
default :
cout<<"\nError bad choice!!.";
break;
}
getch();
}


و يمكن تحميل رابط ملفات المشروع مباشرة من الرابط التالي - و قد تمت كتابته على البورلاند 5 و يحتاج لتعديل بسيط فقط ليعمل على الفجوال سي ;):
حمل حل السؤال الأول من هنا . (http://the.matrix.sdn.googlepages.com/CalcProject.rar) :cool:

====================

نأتي الآن إلى حل السؤال الثاني :


2. قم بتعديل الكلاس في المثال 3 بحيث نتمكن من إضافة عقدة جديدة في آخر السلسلة أو حذف عقدة موجودة بالسلسلة.


الحل :
تم التعديل كالتالي :

* ملف الرأس للكلاس:

#ifndef CListClassH
#define CListClassH
class CList
{
public:
struct node
{
int data;
struct node* next;
};
CList(const int thesize):size(thesize){Head=NULL; };
~CList(void){ delete Head; };
void Create(void);
void Display(void);
void AddTail(int);
node* Find(int);
void Delete(int);
private:
int size;
struct node* Head;
};
#endif


و فيه أضفنا دالة للحذف Delete و دالة لإضافة عدد لذيل السلسلة AddTail و دالة أخرى مهمة للبحث Find .

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

#include <iostream.h>
#include "ClistClass.h"
void CList::Create(void)
{
node *link,*ptr;
Head=new node;
cout<<"\nEnter Data for node (1): ";
cin>>(Head->data);
Head->next=NULL;
link=Head;
for(int i=1;i<size;i++)
{
ptr=new node;
cout<<"\nEnter Data for node ("<<(i+1)<<"): ";
cin>>(ptr->data);
ptr->next=NULL;
link->next=ptr;
link=ptr;
}
};
void CList::Display()
{
if(Head==NULL)
{
cout<<"\nError! Enter List keys first."<<endl;
return;
}
node *pt;
pt=Head;
for(int i=0;i<size;i++)
{
cout<<"Key(data) No.("<<(i+1)<<"): "<<(pt->data)<<endl;
pt=pt->next;
}
};

void CList::AddTail(int key)
{
if(Head==NULL)
{
cout<<"\nError! Enter List keys first."<<endl;
return;
}
if(Find(key)!=NULL)
cout<<"\nError This key alraedy exist!."<<endl;
else
{
node* parent,*NewNode,*ptr=Head;
while(ptr!=NULL)
{
parent=ptr;
ptr=ptr->next;
}
NewNode=new node;
NewNode->data=key;
NewNode->next=NULL;
parent->next=NewNode;
size+=1;
}
};
CList::node* CList::Find(int key)
{
node* ptr=Head;
while(ptr!=NULL)
{
if(key==(ptr->data))
break;
ptr=ptr->next;
}
return ptr;
};
void CList::Delete(int key)
{
if(Head==NULL)
{
cout<<"\nError! Enter List keys first."<<endl;
return;
}
node* ptr=Find(key);
if(ptr==NULL)
cout<<"\nKey not found !"<<endl;
else
{
node* parent,*pt=Head;
if(ptr->data==Head->data)
Head=Head->next;
else
{
while(pt!=NULL)
{
parent=pt;
pt=pt->next;
if(ptr->data==pt->data)
break;
}
parent->next=ptr->next;
delete ptr;
}
}
size-=1;
};


* تبقى فقط ملف main كما يلي:

#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
#include "ClistClass.h"
void main(void)
{
clrscr();
int size;
cout<<"\nEnter Size of the linked list: ";
cin>>size;
CList X(size);
int s,element;
char choice[30];
for(;;)
{
clrscr();
cout<<"\n1.Enter Linked List Keys."<<endl;
cout<<"\n2.Display Linked List Keys."<<endl;
cout<<"\n3.Add Key to tail of List."<<endl;
cout<<"\n4.Delete Key from List."<<endl;
cout<<"\n5.Quit."<<endl;
cout<<"\n\t\tEnter Your Choice: ";
cin>>choice;
s=atoi(choice);
switch(s)
{
case 1:
X.Create();
cout<<"\n\t"<<endl;
system("pause");
break;
case 2:
X.Display();
cout<<"\n\t"<<endl;
system("pause");
break;
case 3:
cout<<"\nEnter new Key to add it to List tail: ";
cin>>element;
X.AddTail(element);
cout<<"\n\t"<<endl;
system("pause");
break;
case 4:
cout<<"\nEnter Key to Delete: ";
cin>>element;
X.Delete(element);
cout<<"\n\t"<<endl;
system("pause");
break;
case 5:
exit(0);
default :
cout<<"\nWrong choice!, try again."<<endl;
break;
}
}
}


حمل المشروع كاملا من هنا (http://the.matrix.sdn.googlepages.com/LinkedListProject.rar) :D.

طبعا هذا الحل ليس نهائيا بمعنى أنه قابل للتطوير و التحسين لأنه كما نعلم ليس هناك برنامجا كاملا ;) و أي برنامج في الدنيا قابل للتعديل و التطوير .

أتمنى أن يعجبكم الحل و إذا كان هناك أي استفسار أو إضافة فأنا حاضر.
تحياتي للجميع.

eldoktor007
24-03-2009, 10:43 PM
الله ينور عليك اخى الكريم على هذه الحلقه
ومن نجاح الى نجاح
تم التقييم بخمس نجوووم علشان خاطر عيوونك وتواصلك فى تقديم يد العون للاخوه الاعضاء
تقبل تحياتى اخى الكريم

the.matrix.sdn
25-03-2009, 08:46 AM
الله ينور عليك اخى الكريم على هذه الحلقه

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

تقبل تحياتى اخى الكريم


ألف شكر يا دكتور على دعمك و تشجيعك و كلماتك الحلوة و التي بلا شك ستكون دافع كبير للإستمرار.. :D :D و ألف مبروك الترقية إلى مراقب بالقسم و بلا شك فإن الاختيار صادف أهله :cool: .
تحياتي لك أخي الكريم و للجميع .

elqnas1
25-03-2009, 02:34 PM
مشكور جدا اخى العزيز

ولكن كنت عاوز منك حضرتك حاجة بخصوص
file organization

المفروض انى اعمل كلاس person
واخد داتا من اليوسر واخزنها فى ال file
كنت عاوز شرح لطريقة ترتيب الداتا فى الفيل (record)

من خلال

fixed length size
delemiter matching
key word

the.matrix.sdn
26-03-2009, 09:25 AM
مشكور جدا اخى العزيز

ولكن كنت عاوز منك حضرتك حاجة بخصوص
file organization

المفروض انى اعمل كلاس person
واخد داتا من اليوسر واخزنها فى ال file
كنت عاوز شرح لطريقة ترتيب الداتا فى الفيل (record)

من خلال

fixed length size
delemiter matching
key word

العفو أخي الفاضل و الشكر لك أنت .

بالنسبة لما ذكرت فما رأيك أن نقوم بعمل الكلاس أولا ثم نأتي إلى ما ذكرت؟ . أتمنى أن تذكر ما وصلت إليه إليه في الكود حتى نستطيع التعديل أو الإضافة .... بانتظارك.
تحياتي .

elqnas1
26-03-2009, 02:04 PM
دة اللى انا وصلت لية لو تعرف حضرتك تكمل علية بحيت انة ياخد اكتر من record
وياريت شرح ازاى نعمل delete
وى اضافة لى
record

#include<fstream>
#include<iostream>
#include<fstream>
using namespace std;

class Person{

private:


char firstname[30];
char lastname[30];
char address[30];
char city[30];
char state[30];
char zipcode[30];

public :

void getinfo()
{

cout<<"\nlast name: ";
cin.getline(lastname,29);

cout<<"\nfirstname: ";
cin.getline(firstname,29);

cout<<"\nEnter address: ";
cin.getline(address,29);

cout<<"\nEnter city: ";
cin.getline(city,29);

cout<<"\nEnter state: ";
cin.getline(state,29);

cout<<"\nEnter zipcode: ";
cin.getline(zipcode,29);
}

void copytofile()
{
fstream f ;


f.open("my file.txt",ios::out);


f<<lastname<<"|";
f<<firstname<<"|";
f<<address<<"|";
f<<city<<"|";
f<<state<<"|";
f<<zipcode<<"|";

}



};

int main()
{

char xx;

Person s;


s.getinfo();
s.copytofile();


fstream m ;

m.open("my file.txt",ios::in);

cout<<endl;
cout<<endl;

char ff[30];

m.getline(ff,30,'|');
cout<<"last name :"<<ff<<endl;


m.getline(ff,30,'|');
cout<<"firstname :"<<ff<<endl;

m.getline(ff,30,'|');
cout<<"Address :"<<ff<<endl;

m.getline(ff,30,'|');
cout<<"city :"<<ff<<endl;

m.getline(ff,30,'|');
cout<<"state :"<<ff<<endl;

m.getline(ff,30,'|');
cout<<"zipcode :"<<ff<<endl;

cin>>xx;

return 0;

}

the.matrix.sdn
26-03-2009, 02:25 PM
جاري دراسة الكود الذي ذكرته - مشكورا - و سأعطيك رأيي لاحقا بحول الله ..
بالنسبة لأخذ أكثر من record فأعتقد هنا أننا سنحتاج لعمل سجل structure ضمن الكلاس بحيث يقوم بتسجيل عدة سجلات ثم يكتبهم في الملف ....
على العموم سأحاول التعديل في الكود تبعك لمحاولة تحقيق المطلوب ...

elqnas1
26-03-2009, 03:26 PM
جاري دراسة الكود الذي ذكرته - مشكورا - و سأعطيك رأيي لاحقا بحول الله ..
بالنسبة لأخذ أكثر من record فأعتقد هنا أننا سنحتاج لعمل سجل structure ضمن الكلاس بحيث يقوم بتسجيل عدة سجلات ثم يكتبهم في الملف ....
على العموم سأحاول التعديل في الكود تبعك لمحاولة تحقيق المطلوب ...

وبردة ممكن حضرتك تعمل
do while
زى الشرح اللى كان فى الاول
بس نخلى الملف على mode
ios::out,ios::app

اما بخصوص
search
add
delete

ممكن نحط اى علامة ادام record اللى عاوزين نمسحة مثلا
زى
*
ونعملة
ignore

دية بعض الافكار

بس انا محتاج لو سمحت شرح وافى لترتيب الداتا فى الملف
بطرق
fixed leghth
delimiter
key word
انا مستعمل الطريقة التانية فى الكود اللى انا وضعة
بس مش عارف ان كانت صح ام خطا

the.matrix.sdn
27-03-2009, 05:54 PM
بالنسبة لبرنامجك هذا فأعتقد أنك ممكن تعمل مجموعة كلاسات :
الأول يقوم بإنشاء السجلات في الذاكرة و أخذ البيانات من المستخدمين ....
الثاني هو المسؤول عن كتابة هذه الملفات في القرص ثم قراءتها لاحقا ...
ثم يمكن أن يكون هناك كلاس آخر ليعمل كواجهة للمستخدم يحوي على قائمة من الخيارات ..
و أخيرا كلاس يعمل على ترتيب هذه البيانات حسب ما تراه أو حذفها الخ ...
و هذه الكلاسات بإمكاننا تطبيق مفاهيم الـ OOP كالوراثة و تعدد الأشكال الخ عليه ...
و لعلنا نعمل عليه سويا لاحقا لأني في الواقع أعمل على إعداد الحلقة التالية في هذه السلسلة ..

انظر هذا الموضوع بالنسبة للترتيب - و هو موضوعك أنت و فيه رد جميل جدا من الأخ الكريم BaderEX
http://www.absba.org/showthread.php?t=762507

elqnas1
27-03-2009, 08:35 PM
مشكور جدا اخى العزيز
ارجو منك العمل على فكرتى ايضا

|A V G|
28-03-2009, 07:08 PM
واصل حفظك الله ونفع بك الإسلام والمسلمين

the.matrix.sdn
29-03-2009, 09:19 AM
واصل حفظك الله ونفع بك الإسلام والمسلمين
شكرا أخي الكريم و جزاك الله خيرا ...

the.matrix.sdn
30-03-2009, 06:32 PM
الحلقة الخامسة : :D:cool:




* الكبسلة : Encapsulation




مفهوم الكبسلة سبقت الاشارة له في الحلقات السابقة و يقصد به تغليف المتغيرات الأعضاء (عناصر البيانات) مع الدوال الأعضاء المتعلقة بها (و تسمى أيضا بالطرق Methods أو العمليات Operations ) في غلاف واحد : هو الكلاس ، و ذلك شبيه بالكبسولة التي تحتوي بداخلها الدواء ...




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




أيضا مفهوم الكبسلة يتضمن ما يُسمى بإخفاء البيانات Data Hiding حيث يسمح لك هذا المفهوم بإخفاء عناصر البيانات (متغيرات أو دوال) عن طريق وضعها في القسم الخاص :Private و نستفيد من هذا حماية البيانات من التعديل من خارج الكلاس بحيث أن هذه البيانات لا يجوز تعديلها إلا من خلال الطرق أو الدوال الأعضاء التي تم تصميمها للتعامل مع هذه البيانات لذلك تسمى هذه الدوال الأعضاء بالواجهة الخارجية للكلاس و هذا يقودنا إلى نقطة مهمة جدا و هي أنه يمكنك استخدام كلاس قد صممه مبرمج آخر غيرك حتى و لو لم تعرف كيفية التصميم الداخلي للكلاس لأنك تتعامل مع الواجهة الخارجية للكلاس أي : لا تخبرني كيف تقوم بالعمل و إنما فقط قم بإنجازه .. و يتضح هذا جليا في ملفات الرأس التي يستخدمها الجميع مثل iostream.h و conio.h و stdlib.h الخ فهذه الملفات في الحقيقة تتضمن الكثير من الكلاسات (الكائنات) التي نقوم باستخدامها بغض النظر عن التفاصيل الداخلية لهذه الكلاسات (الكائنات ) و التي قد لا نكون ملمين بها و لكن ذلك لا يمنعنا من استخدامها و الاستفادة منها ...




ملاحظة مهمة :


بالرغم مما ذكرت أعلاه إلا أن هناك طريقة واحدة نتمكن بها من تعديل عناصر الكلاس المخفية - الخاصة - من خارج الكلاس ! :( و ذلك عن طريق مايسمى بالدوال الصديقة Friend Function و هي دوال ليست أعضاء في الكلاس إلا أنه يسمح لها بتعديل عناصره المخفية ! :eek::mad: و قد نتعرض لها لاحقا.




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





* مفهوم زيادة التحميل Overloading :


هذا أيضا من المفاهيم المهمة جدا و الذي يعطينا مرونة عالية في التعامل مع الكود و هذه الكلمة Overloading تترجم في بعض المصادر بإعادة التوصيف ...


هذا المفهوم يتيح لنا استخدام نفس الاسم لأكثر من دالة في الكلاس ، و على العموم فهذا المفهوم يقسم إلى نوعين :


* زيادة تحميل الدوال الأعضاء : member function overloading


و هذا ما ذكرته في الأعلى ، استخدام نفس الاسم لأكثر من دالة ، إفترض أن لديك كلاس يقوم بمعالجة بيانات مثلا عدد صحيح أو نَص أو عدد حقيقي ... الخ و لديك دوال كل واحدة منها تقوم بذلك بحيث تمرر لها قيمة معينة للعمل على معالجتها ، مثلا ( ProcessInt(int للعدد الصحيح و ( ProcessReal(float للعدد الحقيقي و ( ProcessText(char للنصوص ، ألن يكون من الأفضل و الأظرف :D أن تتمكن من استدعاء جميع هذه الدوال باسم واحد هو ()Process مثلا و تترك المترجم يخمن النسخة التي يجب استدعاؤها بحسب الوسائط التي تمررها ;) - و تسمى بتوقيع الدالة - ، بحيث تصبح الدالة الأولى ( Process(int و الثانية ( Process(float و الثالثة ( Process(char فالاسم أصبح واحدا و لكن الوسائط التي سيتم تمريرها مختلفة و بالتالي فإذا أردت معالجة العدد الصحيح تقوم باستدعاء الدالة مع تمرير وسيط من النوع عدد صحيح و إذا أردت معالجة العدد الحقيقي فستقوم باستدعاء الدالة مع تمرير عدد حقيقي و هكذا و ذلك بغض النظر عن القيمة التي ستقوم الدالة بإرجاعها حيث أن توقيع الدالة – و الذي عن طريقه يحدد المترجم الدالة المقصودة – هو الوسائط و ليس القيمة التي تقوم الدالة بإرجاعها ، فلو فرضنا أن لدينا دالتين بنفس الاسم إلا أن أولاهما تعيد قيمة من النوع عدد صحيح int و الأخرى تعيد قيمة من النوع الحرفي char مثلا فسيعطيك المترجم رسالة خطأ تفيد بوجود غموض في التفريق بين الدالتين لأن إختلاف القيمة المرجعة لا يكفي في التفريق بينهما..




لو قال متفلسف :D:p : طيب يا أخي ما لزوم كل هذا ؟ و لماذا لا ندع كل شيء على ما هو عليه ؟ فالجواب : أن التطور و التطوير ضروري للوصول للأفضل حيث أن هذا المفهوم – زيادة تحميل الدوال - يقدم لنا فوائد كبيرة بحيث يسهل قراءة الكود و خاصة في البرامج الكبيرة حيث يمكنك جعل الدوال القريبة الشكل و الوظيفة تحمل اسما واحدا بحيث تصبح واضحة عند القراءة ضمن الكود الطويل هذا من ناحية و من ناحية أخرى فهذا المفهوم يُمكّن نفس الدالة من التعامل مع أنواع مختلفة من البيانات بمرونة مدهشة ....




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





أعتقد أننا الآن بحاجة ماسّة لمثال أو أكثر للتوضيح بعد كل هذا الكلام النظري الممل بالأعلى :D:D:D;) :





مثال 4. ( مثال رياضي لمحبي الرياضيات :D ) :




قم بكتابة كلاس يقوم بحساب جيب sine و جيب تمام cosine و ظل tangent الزاوية بحيث يتم تمرير قيمة الزاوية له ليعطي الجيب و جيب التمام و الظل كما أنه يقبل قيمة الزاوية بالصورة العشرية العادية و بالصورة الدائرية أي الدرجات و الدقائق و الثواني :confused: .




الحل:




نقوم أولا بفتح البورلاند و عمل مشروع جديد نعطيه الاسم المناسب - راجع الحلقات السابقة للإطلاع على الطريقة - ، ثم نقوم بعمل ملف جديد لكتابة ملف الرأس للكلاس AngleClass.h و هو كالتالي :





#ifndef AngleClassH
#define AngleClassH

class Angle
{
1.double angle;
2.double a_sine;
3.double a_cosine;
4.double a_tangent;
public:
5. void angl_calc(double);
6. void angl_calc(char*);
};
#endif





توضيح :


1. أعلنا عن متغير من النوع double لتخزين قيمة الزاوية المطلوب حساب جيبها و جيب تمامها و ظلها.


2. – 4. ثلاث متغيرات أولها لتخزين قيمة الجيب الناتجة و الثاني لتخزين قيمة جيب التمام و الثالث لتخزين قيمة الظل.


5. رأس دالة باسم( ang_calc(double تعيد قيمة من النوع void و نمرر لها قيمة عدد حقيقي من النوع double و هذا العدد سيكون هو الزاوية المطلوب إيجاد حسابها كما سنرى بعد قليل و هذه هي الدالة الافتراضية.


6. دالة أخرى بنفس الاسم كما تلاحظ ، تعيد أيضا قيمة من النوع void و لكن الاختلاف بينهما في أن هذه نمرر لها قيمة عبارة عن مؤشر حرفي هذا المؤشر يشير إلى سلسلة من الأحرف هي قيمة الزاوية بالتقدير الدائري : درجات ، دقائق ، ثواني..




قم بكتابة الكود أعلاه في ملف رأس بنفس الاسم أعلاه و احفظه ، ثم قم بكتابة الكود التالي في ملف منفصل و هو عبارة عن الملف التنفيذي لنفس الكلاس أعلاه و هو يحوي متن الدوال الأعضاء بالكلاس:




#include <iostream.h>
1.#include <math.h>
2.#include <string.h>
3.#include "AngleClass.h"
4.#define DEG_to_RAD 0.0174532925

void Angle::angl_calc(double degree)
{
5.angle = degree;
6.a_sine = sin( angle * DEG_to_RAD );
7.a_cosine = cos( angle * DEG_to_RAD );
8.a_tangent = tan( angle * DEG_to_RAD );


9. cout << "\nFor an angle of: " << angle << " degrees : " << endl;
10.cout << "The sine is : " << a_sine << endl;
11.cout << "The cosine is : " << a_cosine << endl;
12.cout << "The tangent is : " << a_tangent << endl;
}

void Angle::angl_calc(char *dat)
{
13.char *deg,*min,*sec;

14.deg = strtok( dat,"@" );
15.min = strtok( 0,"'" );
16.sec = strtok( 0,"\"" );
17.angle = atof(deg) + ((atof(min))/60.0) + ((atof(sec))/360.0);

a_sine = sin( angle * DEG_to_RAD );
a_cosine = cos( angle * DEG_to_RAD );
a_tangent = tan( angle * DEG_to_RAD );

cout << "\nFor an angle of: " << angle << " degrees : " << endl;
cout << "The sine is : " << a_sine << endl;
cout << "The cosine is : " << a_cosine << endl;
cout << "The tangent is : " << a_tangent << endl;
}


توضيح :



1. تضمين ملف الرأس math.h و هو مكتبة تحوي دوال رياضية منها دوال ايجاد جيب الزاوية و جيب تمامها و ظلها و التي سنستعملها هنا كما سيأتي بعد قليل.

2. تضمين ملف الرأس string.h و هو مكتبة غنية بالدوال الفعالة و القوية للتعامل مع سلاسل الحروف .


3. تضمين ملف رأس الكلاس و الذي قمنا بإعداده سابقا بالاسم AngleCalss.h .


4. أعلنا عن ثابت عن طريق عبارة define# و هي إحدى الطرق للإعلان عن الثوابت في لغة سي ، هذا الثابت سنستخدمه في ايجاد جيب و جيب تمام و ظل الزاوية و قيمته تساوي ( باي pi مقسوما على 180).


5. هذا أول سطر في متن الدالة الأولى و هي الافتراضية و التي سيتم زيادة تحميلها في الدالة الثانية ، في هذا السطر نقوم بتهيئة المتغير العضو angle بإسناد قيمة المتغير degree – و الذي تم تمريره – إليه و هذه القيمة التي تم تمريرها و إسنادها إلى العنصر angle هي قيمة الزاوية المراد إيجاد جيبها و جيب تمامها و ظلها .


6. في هذا السطر يتم استخدام الدالة ()sin – و هي تتبع للمكتبة math.h - لإيجاد جيب الزاوية angle و كما نرى فقد مررنا للدالة الزاوية *(في) الثابت المذكور بالأعلى و الناتج الذي هو الجيب سيتم إسناده للمتغير العضو a_sine و هكذا تم ايجاد جيب الزاوية و تخزينه في متغير عضو بالكلاس.


7. نفس الطريقة المذكورة بالأعلى لإيجاد جيب تمام الزاوية باستخدام الدالة() cos ...


8. و هنا نوجد ظل الزاوية بالدالة ()tan .


9. – 12. هذه الأسطر نقوم عن طريقها بعرض النواتج : الزاوية و جيبها و جيب تمامها و ظلها ، نستعمل في ذلك طبعا الكائن cout .


13. هذا أول سطر في متن الدالة الأخرى التي تم زيادة تحميلها بحيث نستطيع تمرير الزاوية بالصيغة الدائرية على شكل سلسلة حروف تحوي الدرجات و الدقائق و الثواني و نحن نمررها بصيغة مؤشر حرفي يُشير إلى سلسلة الحروف التي تمثل الدرجة و الدقائق و الثواني ، و في هذا السطر أعلنا عن ثلاث مؤشرات من النوع الحرفي char حيث سنحتاج لها لاحقا عند استعمال الدالة () strtock . .


14. استعملنا هنا الدالة القوية و الفعالة () strtock و هي عضو في المكتبة string.h ، هذه الدالة تقوم بقراءة سلسلة من الحروف و تعيد عنوان يُشير إلى هذه السلسلة بل و يمكنها تقسيم هذه السلسلة من الحروف إلى عدة سلاسل عن طريق وضع عدة علامات أو محددات delimiters ، هذه المحددات تستطيع من خلالها الدالة معرفة حدود كل جزء من السلسلة نريد فصله ، إفترض أننا نريد إيجاد الجيب و جيب التمام وظل زاوية بالصيغة الدائرية مثلا : 35 درجة و 76 دقيقة و 20 ثانية لذلك نمررهها على الصورة: ""\20 '76 @35" : الرمز @ أي درجة - و في الواقع فليس هذا هو الرمز الفعلي للدرجة :D و القصد هو التمثيل - و الرمز ' أي دقيقة و الرمز \ أي ثانية ، هذه الثلاث رموز هي محددات بحيث أن القسم الأول ينتهي عند الرمز @ و هذا القسم هو للدرجة لذلك نستعمل الدالة كالتالي strtock(dat,"@")o هdat هي مؤشر حرفي لكامل السلسلة و الذي قمنا بتمريره ثم نضع المُحدد @ بين علامتي تنصيص "" لنخبر الدالة بالقراءة من السلسلة حتى الوصول للرمز @ و هنا تتوقف الدالة عن القراءة من السلسلة ثم ترجع عنوان يشير إلى هذا الجزء من السلسلة و هذا العنوان نخزن قيمته في المؤشر الحرفي deg و الذي أعلنا عنه في الأعلى ، و قد يسأل سائل : ما الغرض من كل هذه العملية ؟ فالجواب هو استخلاص الدرجات و الدقائق و الثواني من السلسلة ثم تحويلها إلى زاوية بالصورة العشرية العادية تمهيدا لإيجاد الجيب و جيب التمام و الظل.


15. هنا نواصل القراءة من السلسلة حتى الوصول إلى المحدد ' ثم نخزن عنوان هذا الجزء من السلسلة في المتغير min.


16. و نستمر في القراءة من السلسلة حتى المحدد \ أما علامة التنصيص في الآخر " فهي تخبر الدالة بنهاية السلسلة ثم نخزن العنوان في المؤشر sec ، و هكذا نكون قد قسمنا السلسلة المُمَرّرة إلى ثلاث أقسام أولها للدرجة و الثاني للدقائق و الثالث للثواني و قد خزنا عناوين هذه السلاسل في ثلاث متغيرات حرفية تمهيدا لاستخدامها فيما يلي.


17. في هذا السطر نقوم بتحويل كل سلسلة من السلاسل الثلاث التي جهزناها بالأعلى إلى أرقام عشرية (حقيقية double) ثم نجمعها لتعطينا القيمة العشرية للزاوية المطلوبة ، و لعمل ذلك نستخدم الدالة ()atof من المكتبة math.h و هذه الدالة نمرر لها قمة حرفية تحولها إلى قيمة عشرية (حقيقيةdouble ) و نستعملها هنا ثلاث مرات لتحويل الدرجات و الدقائق و الثواني من الصورة الحرفية إلى الصورة العشرية (الحقيقية ) و ذلك حتى نتمكن من التعامل معها رياضيا بإيجاد مجموعها و هو الزاوية المطلوبة لتخزينها في المتغير العضو angle ، ثم تأتي بقية السطور التي تقوم بإيجاد الجيب و جيب التمام و الظل ثم عرض النواتج و قد تكلمنا عنها بالأعلى .




آسف على الإطالة و لكن كان لابد من الإسهاب في بعض المواضع.




قم بحفظ الملف ثم أضفه إلى المشروع الذي قمت بعمله في الأعلى ...




نأتي الآن إلى الدالة main و التي سنقوم فيها بالإعلان عن كائن و ليكن Data من الكلاس Angle ثم نقوم باستدعاء دالتينا بحيث نمرر الزاوية بالتقدير العشري العادي مرة و بالتقدير الدائري مرة أخرى أخرى و هكذا و يأتي الناتج : جيب و جيب تمام و ظل الزاوية :




#include <conio.h>
#include "AngleClass.h"

void main(void)
{
Angle Data;

Data.angl_calc(75.0);
Data.angl_calc("35@ 75' 20\"");
Data.angl_calc(145.72);
Data.angl_calc("65@ 45' 30\"");


getch();
}


و بعد تنفيذ البرنامج سيكون الناتج كالتالي :



http://absba2.absba.org/teamwork4//761593/Ovrldng1.JPG




يتبقى لنا في هذا النوع الكلام عن زيادة تحميل دوال البناء constructor overloading بالاضافة الى النوع الثاني من زيادة التحميل و هو زيادة تحميل المعاملات كمعامل الجمع + و الطرح - .. الخ و سنتكلم عنها في الحلقة القادمة بحول الله لأن الحلقة إذا أصبحت طويلة جدا فستكون مملة – على ما أعتقد ! – :D:D:D;)




تحياتي و تقديري للجميع...

تم رفع الصورة على السيرفر بواسطة المشرف العام freethink

Fawaz555
31-03-2009, 02:11 PM
تقبل تحياتى اخى

The Dead
31-03-2009, 08:42 PM
كمل يا باشا زي الفل

the.matrix.sdn
01-04-2009, 11:38 AM
تقبل تحياتى اخى


مشكور أخي الكريم على تشريفك الموضوع .


كمل يا باشا زي الفل


متشكرين يا باشا و ربنا يخليك .


تحياتي للجميع.