الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

سي++ من المستوى المبتدئ الى المتوسط

بدأه Wajdy Essam في 25 أغسطس 2008 · 7 رد · 2,626 مشاهدة · في المواضيع والدروس
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ورحمه الله وبركاته ،

سي++ تعتبر الأن اللغه الأساسيه في تطوير الكثير من المشاريع التجاريه والتعليميه أيضا ، والسبب لما تحتوي هذه اللغه من أمكانيات فهي تدعم مفهوم OOP ومفهوم Procedural Programming بالأضافه الى مفهوم الـ Generic Programming والتي من خلاله نستطيع عمل مكتبات يستخدمها الـ client بكل سهوله وبأي نوع من البيانات ..

كل هذه المفاهيم التي تدعمها Hybrid Langauge جعلتها اللغه المفضله للكثير من المبرمجين ومهندسي البرمجيات . ولكن بالمقابل جعلت عمليه تعلم اللغه أكثر غموضا عما كانت عليه في لغات أخرى كـ سي مثلا .

هذا المقال موجه للمبتدئين فقط ، أقصد بمبتدئ هو من يفهم الأساسيات في لغه سي++ ، من Operator Overloading ، من أبسط مفاهيم OOP وكيفيه استخدامها ، من exception وكيفيه استخدامه ..

قد تتسائل "هل أكون مبتدئ بعد أنهائي لكل هذا ؟" ، الجواب بالطبع تكون مبتدئ .. فاحتراف لغه كسي++ يحتاج الى ثلاثه مراحل :

1) تعلم الـ Syntax الخاص باللغه وكيفيه أستخدام الأمور السابقه ذكرها أعلاه .

2) كيفيه تحليل و تقسيم المشكله وكتابه كلاسات مكبسله بشكل جيد C++ Design ..

3) تعلم الأستخدام الأمثل لكل أداه ومتى لا يفضل استخدام بعض الأدوات ومتى يفضل ..

في المرحله الأولى كما ذكرت ، سوف تتعلم كيفيه عمل كلاس من نوع X ، كيفيه عمل كائن ، ربما ستدرك بعض من مفاهيم OOP بشكل جيد وتقوم بكتابه متغيراتك بشكل محمي private وتقوم بتوفير Accessory Function .. وسوف تستطيع استخدام الوراثه وسوف تعرف ما هو ال Polymorphism وما المقصود به ..

المرحله الثانيه ، وهي التي تحدد هل أنت مبرمج سي++ محترف أم مبتدئ ، وهي خاصه بكيفيه تقسيم البرنامج ، وكتابه كلاسات تتفاعل مع بعضها بشكل جيد .. مع مراعاه تغير المتطلبات Change of requirement فيما بعد والتي هي أمر مفروغ منه في عمليه تطوير البرمجيات Software Development .. متطلبات العميل تتغير مع مرور الزمن والتصميم الجيد لكلاساتك هو الذي يضمن أنك ستراعي هذا التغيير بأقل تكلفه ممكنه وبأقل جهد ممكن ...

المرحله الثالثه ، هي مرحله وسيطه بين الأولى والثانيه ، وهي تهتم بالأستخدام الأكفأ effective Usage ، فمثلا دائما ما نفضل أن نرسل المتغيرات العاديه بالقيمه والكائنات اذا لم نكن نريد تغييرها By Reference to const . وتهتم هذه المرحله بالتعريف عن الأمور الغامضه في اللغه مثلا الكلمه المحجوزه mutable (والتي من خلالها في حال كان متغير معرف بها تستطيع تغييرها داخل الـ const member function) .. ولماذا يفضل أن تكون دائما داله البناء explicit ، ولماذا ولماذا ... هذا ما يعرف بالأستخدام الأكفئ للأدوات ... وكما تلاحظ ليس ضروري 100% لكي يعمل البرنامج ولكن بدون تلك الأمور سوف تخسر الكثير من الذاكره وتقوم بعمل الكثير من الأستدعائات لدوال أنت في غني عنها ، أيضا سوف يكون برنامج في Safe Side ولن يقوم بعمل أمور ليست في الحسبان .

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

التصميم الجيد للبرنامجك سوف ينتج تبعا له سهوله في الصيانه Maintenance ، سهوله في اضافه المزيد من الكلاسات ، سهوله في اعاده الأستخدام Reuseability ، سهوله في حل المشكله وبمصطلحات المشكله نفسها (وهي ميزه في OOP) .

C++ designer أصعب بكثير جدا من C++ Programming ، لذلك دائما في مجال البرمجيات نجد أن هناك من يطلق عليه senior وهو الذي يساعد المبرمج المبتدئ في البرنامج بيحث يقوم برسم الكلاسات الرئيسيه وكيف تتفاعل مع بعضها البعض وبعدها يقوم المبرمج المبتدئ بالتطبيق لهذا الرسم ..

مقالنا الليله وكبادئه باذنه تعالى سوف يكون مثال عملي للموضوع ، والمقالات القادمه سوف نغوص أكثر في موضوع الـ Object Oriented Analysis And design وأختصارا OOA/D .. فأنت لا تستطيع البرمجه بشكل OO بدون أي يكون هناك تحليل وتصميم مناسب تماما للبرنامج OOA/D .

بما أننا نريد أن يكون الجزء عملي بشكل أكبر حتى يحفز الأعضاء بالبحث في موضوع OOA/D ( لا نريد أن ندخل في Theory مباشره ، قد يُكرٍّه الجميع بما فيهم كاتب الموضوع :) ) . فنأخذ مثال بسيط وهو عباره عن برنامج يقوم بحفظ بيانات طلاب في مكان ما (مصفوفه ، قائمه متصله ، داتا بيز ، ملف ) كما ذكرت المتطلبات قد تتغير .. ويوفر واجهه بسيطه من خلالها نستطيع عمل الأتي :

ادخال بيانات طالب جديد

بحث عن طالب باسمه ، وربما برقمه

معرفه نتيجه الطلاب جميعا

معرفه نتيجه الطلاب الناجحين

معرفه نتيجه الطلاب الراسبين

وحاليا نريد أن نستخدم أبسط الطرق حتى نصل للهدف من الموضوع وهو تقسيم الكلاسات بشكل جيد وكما سنرى فيما بعد في الـ Maintenance لن تحتاج الى تغيير جزء في شفره العميل Interface ، فقط في الـ Implementation .

نبدأ على بركه الله ،

في البدايه أجلب ورقه وقلم ، وحاول أن تتخيل العمليه بشكل مجرد تماما من التفاصيل Thinking Abstract ، وأجب عن السؤال التالي : ما هي الأشياء الأساسيه في البرنامج ؟ (عليك أن تتذكر أن المتطلبات قابله للتغيير) لذلك فكر بشكل عام General وليس مخصص بشئ ما specialization.

نعم بعد قليل من التفكير ستقول هناك "طالب" ، وهناك "مكان" للتخزين ، وهناك "واجهه" تدخل عن طريقها المعلومات . ممتاز ممتاز أنت تبلي حسنا .. دائما عليك في البدايه أن تتخيل البرنامج بأبسط ما يمكن وهو ما يعرف بالـ abstract .. أذا مفهوم الـ Abstraction هو تسهيل وتخيل للعمليه بأبسط شكل ممكن بدون الدخول في الدوال والمؤشرات والمصفوفات وو فقط الوظيفه الأساسيه للكلاس ..

عندما تبدأ بتعريف الكلاس الأول "الطالب" ستقوم بتطبيق ما تعلمته في OOP وعليك بتذكر Access specifies (والتي هي private , public , protected ) وتقوم بتوفير دوال وصول سواء Setter أو Getter . هكذا تكون عرفت "الطالب" ، هكذا تكون عرفت نوع بيانات جديد ليس موجود باللغه New Data Type . ومع "تبسيط" الكلاس سوف ينتج لدينا Abstract Data Type وأختصار ADT .

لماذا قمنا بفصل "الطالب" عن "مكان التخزين" لأن مكان التخزين ليس له علاقه بالطالب ، مكان التخزين يمكن أن يكون مصفوفه Array يمكن أن يكون Linked List ، يمكن أن يكون Data Base .. بالتالي نكون بسطنا الكلاس وأخرجنا الدوال التي لا علاقه لها بالطالب كـ "مكان التخزين" و "وواجهه المستخدم" . وهو ما يعرف بتقسيم مسؤليات كل كائن division of Responsibility .

"واجهه المستخدم" يمكن أن تكون console يمكن أن تكون GUI لا يهم الأن بتاتا هذا الأمر ...

نقوم الأن برسم الكلاسات الأساسيه هنا :

عمليه رسم هذه الكلاسات تختلتف من شخص لأخر ، من مؤسسه لأخرى ، ويمكن أن نعتمد على لغه UML حيث هي لغه تستخدم لوصف الكائنات والعلاقه بينها بمخططات أكثر من واضحه ، وهكذا تكون Standard لأي مبرمج وأخر ..

لكن بما أننا في البدايه ، سنعتمد على أي رسمه (دائره ، مربع لا يهم ) المهم فكره التقسيم تتضح بشكل جيد :

StudentSystem.PNG

نبدأ الأن بالكلاس الأول : الطالب ..

ماذا يريد الـ Customer هنا ؟

لاحظ الأن قلنا Customer لأننا لن نبرمج برنامج لأنفسنا ، ولكن لـ Customer سوف يعطينا نقود في النهايه (أنت أخي القارئ تمثل الـ Customer ، لا تنسى $ :) )

كما عليك أن تلاحظ أن "الطريقه" المستخدمه في الـ Software Development Cycle هي التي تحدد متى تبدأ عمليه البرمجه ، بعد أن تجمع جميع المتطلبات كما في الـ Waterfall Model أو أن تقوم في كل مرحله من مراحل التطوير بأخذ المزيد من الـ Requirement بعد أن تري العميل Demo Version أو Prototype وبعدها تأخذ منه الـ FeedBack على ذلك ومنها ستجري التعديلات بناء على ما يريد ، وتكمل هكذا جمع وأنتزاع المتطلبات منه .. بالمناسبه Requirement extracting أمر ليس بهذه السهوله .. خاصه في حال كان الـ customer هو "مبرمج" ... كما حدث معي في أحد المرات لم أستطيع أخذ المتطلبات جميعها الا بعد فتره طويله لأن customer (المبرمج) كان لا يراها ضرويه .. وغير ذلك من الأمور ، ما علينا منها الأن :) .

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

نعود للطالب مره أخرى ، وبعد الجلوس مع العميل تبين أننا في البرنامج نريد في النهايه أن :

نعرف الأسم الأول والثاني للطالب ودرجته ونتيجته (ناجح أم لا)

نستعلم عن الطالب برقمه أو أسمه الأول والثاني

نخرج نتيجه جميع الطلاب

نخرج نتيجه جميع الطلاب الناجحين

نخرج نتيحه جميع الطلاب الراسبين

نزيد درجات للطالب الفلاني

ننقص درجات من الطالب الفلاني

بالنظر الى المتطلبات سوف ندرك أننا في الكلاس الطالب سوف نحتاج الى المتغيرات :

اسم الطالب والأخير

رقم الطالب

درجه الطالب

نتيجه الطالب

كما تعلم سوف نضع هذه المتغيرات في الجزء private ، حتى نوفر حمايه لها .. وبالتالي سوف نضطر الى توفير دوال Set و Get لتلك المتغيرات ..

داله وضع قيمه لاسم الطالب الأول والأخير

داله وضع قيمه لدرجه الطالب

داله وضع قيمه لنتيجه الطالب

---

داله لارجاع اسم الطالب الأول والأخير

داله لارجاع درجه الطالب

داله لارجاع نتيجه الطالب

هكذا نكون أنهينا دوال Setter & Getter .. ماذا نحتاج بعد ذلك ؟

داله لعرض بيانات الطالب (اسمه ، رقمه ، نتيجته)

داله لانقاص درجه الطالب

داله لزياده درجه الطالب

الأن سوف نقوم بكتابه داله نسميها display لطباعه بيانات الطالب .. وسوف نكتب داله نسميها sub و .. مهلا مهلا مهلا ..

أنت مبرمج سي++ ؟ أليس كذلك

أنت لديك أدوات في اللغه ليست موجوده في جميع اللغات ...

أنت لديك Operator Overloding .. لماذا لا تستفيد منها .

حول الداله Display الى اعاده تعريف الـ Extracting Operator >> .

حول الداله add الى اعاده تعريف المعامل + وبالتالي سوف نجمع الكائن مباشره مع الرقم ..

حول الداله sub الى اعاده تعريف المعامل - وبالتالي سوف نطرح الكائن مباشره مع الرقم ..

سوف تخرج لك شفره جميله .. More Simplicity و More Readability .. ماذا تريد أكثر من هذا :) .

نشاهد محتويات الكلاس Student :

(الملف Student.h) وقد فصلنا الواجهه عن التطبيق (تصريح الدوال عن تعريف الدوال) .

StudentSystem1.PNG

تطلع في الملف Student.h وأنظر مليا ...

أعرف أننا قمنا بجعل الدوال Get هي const وذلك من مبدأ الحمايه ، حيث const Member Function لن تغير في قيمه المتغيرات ..

أعرف أننا قمنا بعمل Operator OverLadoing لبعض المعاملات والسبب عمليه الوضوح وسهوله الأستخدام فيما بعد ..

أعرف أننا قمنا بوقع الكلاس بالكامل داخل namespace أسميناها MyRecord وذلك حتى لا يتعارض أسماء الدوال هنا مع دوال أخرى موجوده في كلاسات أخرى قد تستخدمها .. أنظر لجميع كلاسات سي++ القياسيه جميعها موجوده في std namespace .. نحن الان سنضع كل عملنا في MyRecord namespace.. والسبب كما ذكرت الأبتعاد عن مشكله الـ Name collision .

أنتهينا من الملف Student.h ..

دعنا الأن نقوم بتعريف الدوال الموجوده به .. في ملف Student.cpp ..

في داله البناء سوف نعطي المتغيرات قيمه ابتدائيه هنا ..

دوال Set تستقبل قيمه وتضعها في المتغير

دوال Get ترجع قيمه المتغير .

دوال الـ Operator OverLoading تقوم بالعمل المطلوب ..

(اذا كانت هناك مشكله في Operator OverLoading ، راجع الدروس في قسم سي++ ، هو ليس موضوع حديثنا بتاتا ) .

ملف student.cpp :

   // Student.cpp

					#include <iostream>
					#include "Student.h"
					using namespace std;

					namespace MyRecord
					{
						// Constructor
						Student :: Student ()
						{
							firstName = "No";
							lastName  = "Name";
							studentNumber = 0;
							studentDegree = 0;
							studentResult = false;
						}

						// getter function
						string Student ::  getFirstName ()	const {	return firstName;		}
						string Student ::  getLastName  ()	const	{	return lastName;		 }
						int	Student ::  getStudentDegree() const	{	return studentDegree;	 }
						int	Student ::  getStudentNumber() const	{	return studentNumber;	}

						bool   Student ::  getStudentResult()	 {	
							if ( studentDegree <= FAILER_MARK )
								studentResult = false;
							else
								studentResult = true;

							return studentResult;
						}

						// setter Function
						void Student :: setFirstName ( string fName )		 {	firstName = fName;		}
						void Student :: setLastName  ( string lName )		{	lastName  = lName;		}
						void Student :: setStudentDegree ( int degree )		{	studentDegree = degree;}
						void Student :: setStudentNumber ( int number )	 {   studentNumber = number;}
						void Student :: setStudentResult ( bool result)	 {	studentResult = result;}

						// adding and subtracting degree from student
						Student& Student ::  operator + ( int degree )
						{
							studentDegree += degree;
							return *this;
						}

						Student& Student ::  operator - ( int degree )
						{
							studentDegree -= degree;
							return *this;
						}

						// display function
						ostream& operator << (ostream& out ,  Student& s)
						{
							out << "nStudent  : "   << s.getFirstName()	  << " " 
								<< s.getLastName()   << endl
								<< "Number   : "	 << s.getStudentNumber()  << endl
								<< "Degree   : "	 << s.getStudentDegree()  << endl
								<< "Result   : "	 << (s.getStudentResult() ? "PASS" : "FAIL" )
								<< endl;

							return out;
				}
			}

لاحظ أننا ما زلنا نعمل في MyRecord ، وكل الكلاسات والمتغيرات سوف نضعها هنا في هذه ال Namespace ..

أنتيهنا من كائن الطالب بالكامل ... نبدأ الأن بالكائن الثاني ، مهلا مهلا مهلا .. توقف !!

من قال لك بأن كلاس Student.cpp يعمل ؟

من قال لك بأنه ليست هناك مشاكل فيه ؟

عليك بأختباره أولا ، دائما أختبر الـ Component بعد تطويره وهو ما يزال صغير .. لأننا اذا لم نختبره من الأن صدقوني في حال واجهنا مشكله فيما بعد سوف تصعب للغايه أكتشاف هذا الخطأ ..

لذلك unit Testing بسيط تتأكد من أن الأمور تعمل ، ويكون هذا First Prototype نقدمه للعميل ..

هيا بنا نكتب : StudentTest.cpp :

[color= #007f00;]// Test Student class

 

        [color= #007f00;]#include <iostream>

        [color= #007f00;]#include "Student.h"

 

        [color= #0000ff;]using [color= #0000ff;]namespace std;

        [color= #0000ff;]using [color= #0000ff;]namespace MyRecord;

 

        [color= #0000ff;]int main [color= #000000;]([color= #0000ff;]int argv , [color= #0000ff;]char[color= #000000;]** argc[color= #000000;])

        [color= #000000;]{

        Student std1;

        Student std2;

 

        std2.[color= #808000;]setFirstName[color= #000000;]([color= #A31515;]"Wajdy"[color= #000000;]);

        std2.[color= #808000;]setLastName[color= #000000;]([color= #A31515;]"Essam"[color= #000000;]);

        std2.[color= #808000;]setStudentNumber[color= #000000;]([color= #ff0000;]3300[color= #000000;]);

        std2.[color= #808000;]setStudentDegree[color= #000000;]([color= #ff0000;]60[color= #000000;]);

 

        std2 [color= #000000;]= std2 [color= #000000;]+ [color= #ff0000;]3;

        cout [color= #000000;]<< std2;

 

        cout [color= #000000;]<< endl [color= #000000;]<< endl;

 

        cout [color= #000000;]<< std1;

 

        [color= #0000ff;]return [color= #ff0000;]0;

        [color= #000000;]}

جرب البرنامج ..

StudentSystem2.PNG

واووو ..

الكائن الأول يعمل ، والداله + زادت 3 بشكل صحيح ..

والكائن الثاني يعمل أيضا ، ولكنه فارغ ..

الأن نقوم بالذهاب الى الـ Customer ، نريه هذا العمل البسيط ، هل يريد زياده بيانات في الطالب مثلا رقم تلفونه ومعلومات أخرى .. بالتأكيد لن تكلفك شيء سوى عمل المتغير ودوال Set و Get لذاك المتغير ، وزياده سطر في داله الطباعه .. فقط ، ما أحلاه من تغيير ... أنت سعيد جدا لأنه سهل ولا توجد مشكله ، الـ Customer سوف يكون سعيد أيضا لأن التغيير لن يكلفه الكثير (حتى بعد أن تسلمه البرنامج كامل وطلب هذا التعديل ، النتيجه واحد ) أنا أيضا سعيد لأني سأحصل على حقي من $ :) .

الكلاس الثاني : "مكان التخزين" ..

لغرض التبسيط في البرنامج سيكون مكان التخزين هو مصفوفه ، ربما تطور البرنامج فيما بعد وتستخدم Vector أو Linked List أو حتى تستخدم driver لكي تتصل بقاعده بيانات .. لا يهم هذا ..

الأن نريد تخزين معلومات الطالب في مصفوفه ، ماذا سنحتاج ؟

بالتأكيد مصفوفه بحجم مناسب .

داله لادخال طالب في المصفوفه

داله لارجاع طالب حسب الأسم الأول والثاني

داله لعرض الطلاب الناجحين

داله لعرض الطلاب الراسبين

داله لعرض الطلاب جميعا

أخر ثلاث دوال لم نضعها في الكلاس student والسبب بالتأكيد أن هذه الدوال لا علاقه لها بالطالب ، هي تتعامل مع مكان التخزين وتقوم بالتعامل معه وليس مع الطالب ..

نبدا في ملف DataBase.h :

StudentSystem3.PNG

أنظر مليا في الكلاس ،

ستجد بأن هناك متغير باسم nextStudentNumber هو الذي يقوم بتحديد رقم الطالب التالي (تلقائيا) من نفسه ..

ستجد بأن المتغير nextSlot هو الذي يقوم بمعرفه المكان التالي في المصفوفه ..

الثابت FIRST_STUDENT_NUMBER هو الذي سيحدد رقم الطالب الأول ، وnextStudentNumber هو الذي سيتحرك من هذه النقطه ..

الدوال البقيه معروفه من الأسم ، تذكر التسميه باسم واضح تعتبر Good Programming Practice تسهل لك ولغيرك العمل ..

لا تنسى أننا ما زلنا نضع كل شيء في MyRecord ..

نشاهد ملف DataBase.cpp وهو تقريبا أهم كلاس هنا في البرنامج ، ركز جيدا !!

 // DataBase.cpp

			#include <iostream>
			#include "DataBase.h"

			using namespace std;

			namespace MyRecord 
			{
				DataBase :: DataBase ()
				{
					nextSlot = 0;
					nextStudentNumber = FIRST_STUDENT_NUMBER;
				}

				DataBase :: ~DataBase() {	}

				Student& DataBase :: addStudent (string firstName , string lastName , int degree)
				{
					if ( nextSlot >= MAX_SIZE) 
					{
						cerr << "There is no place in memory ... !";
						throw exception();
					}

					Student& st = student[nextSlot++];
					st.setFirstName(firstName);
					st.setLastName(lastName);
					st.setStudentDegree(degree);
					st.setStudentNumber(nextStudentNumber++);

					return st;
				}

				Student& DataBase :: getStudent (int studentNumber )
				{
					for (int i=0; i<nextSlot; i++)
						if ( student.getStudentNumber() == studentNumber )
							return student;

					cerr << "Cannot find student with number : " << studentNumber << endl;
					throw exception();
				}

				Student& DataBase :: getStudent (string firstName , string lastName )
				{
					for (int i=0; i<nextSlot; i++)
						if ( student.getFirstName() == firstName &&
							 student.getLastName () == lastName )
								return student;

					cerr << "Cannot find student : " << firstName << " " << lastName << endl;
					throw exception();
				}

				void DataBase :: displayAll () 
				{
					for (int i=0; i<nextSlot; i++)
						 cout << student;
				}

				void DataBase :: displayPass () 
				{
					for (int i=0; i<nextSlot; i++)
						if ( student.getStudentResult() )
							 cout << student;
				}

				void DataBase :: displayFail () 
				{
					for (int i=0; i<nextSlot; i++)
						if ( ! student.getStudentResult() )
							  cout << student;
				}
			}

داله البناء تعطي الموقع nextSlot الأول في المصفوفه ..

وتهيئ وnextStudentNumber بالرقم الأول الثابت FIRST_STUDENT_NUMBER ..

لماذا ؟

لأنه في حال طلب العميل أن يبدأ ترقيم الطلاب من 1000 ، فقط سوف نغير الثابت ، ولن تغير شيئا أبدا !

يمكن أن نتجاهل ذلك الثابت ، ولكن نضع القيمه 1000 مباشره في nextStudentNumber ، وفي حال طلب العميل التغير نغير ذلك الرقم ، ولكن يفضل الثابت ، الأمور هكذا أكثر ترتيب وأكثر حذرا وأكثر ترقب للتغير في المستقبل ..

لن نحتاج لداله هدم ، لا يوجد شيء نحتاج الى تحريره ..

الأن الداله addStudent سوف تستقبل أسم الطالب الأول والثاني والدرجه .. لكي تضيف الطالب في المصفوفه (أو في مكان التخزين في حال غيرنا الـ Implementation) ..

الداله في البدايه تختبر هل nextSlot وصل الى أقصى حد للمصفوفه ، وفي تلك الحاله سوف نطبع باستخدام الكائن cerr رساله توضح المشكله ونولد exception بحيث يتوقف التنفيذ عند هذه النقطه ، ويكمل في جزء Catch في الداله التي استدعيت هذه الداله ..

الأن تبدأ عليه تخزين الطالب ، سوف نحصل على reference من الموقع التالي في المصفوفه ، ونخزن فيه الأسم والدرجه والرقم .. ونرجع هذا الـ alises .

دوال getStudent سواء باالأسم أم بالرقم ، سوف تبحث من البدايه الى الموقع الحالي ، وترجع الطالب اذا تحقق الشرط ، غير ذلك سوف تقوم بتوليد exception ..

دوال الطباعه ، displayAll سوف تطبع جميع الطلاب (أنظر فائده operator OverLoading هنا ) ، displayPass سوف تطبع الطلاب الناجحين getStudentResult = ناجح true .. أما الداله displayFail سوف تطبع العكس للداله السابقه (!) ..

أنتيهنا من ملف DataBase.cpp ، أعرف أنك لم تنسى الـ Unit testing ، نقوم بكتابه DataBase Test :

[color= #007f00;]// Test DataBase class

 

           [color= #007f00;]#include <iostream>

           [color= #007f00;]#include "DataBase.h"

 

           [color= #0000ff;]using [color= #0000ff;]namespace std;

           [color= #0000ff;]using [color= #0000ff;]namespace MyRecord;

 

           [color= #0000ff;]int main [color= #000000;]([color= #0000ff;]int argc , [color= #0000ff;]char[color= #000000;]** argv [color= #000000;])

           [color= #000000;]{

               DataBase myDB;

 

               Student[color= #000000;]& s1 [color= #000000;]= myDB.[color= #808000;]addStudent[color= #000000;]([color= #A31515;]"Wajdy",[color= #A31515;]"Essam",[color= #ff0000;]90[color= #000000;]);

               Student[color= #000000;]& s2 [color= #000000;]= myDB.[color= #808000;]addStudent[color= #000000;]([color= #A31515;]"Ali",[color= #A31515;]"Omer",[color= #ff0000;]40[color= #000000;]);

               s2 [color= #000000;]= s2 [color= #000000;]+ [color= #ff0000;]4;

 

               Student[color= #000000;]& s3 [color= #000000;]= myDB.[color= #808000;]getStudent[color= #000000;]([color= #A31515;]"Wajdy",[color= #A31515;]"Essam"[color= #000000;]);

 

               cout [color= #000000;]<< [color= #A31515;]"All Student :n";

               myDB.[color= #808000;]displayAll[color= #000000;]([color= #000000;]);

 

               cout [color= #000000;]<< [color= #A31515;]"nnPass Student :n";

               myDB.[color= #808000;]displayPass[color= #000000;]([color= #000000;]);

 

               cout [color= #000000;]<< [color= #A31515;]"nnFailer Student :n";

               myDB.[color= #808000;]displayFail[color= #000000;]([color= #000000;]);

 

               [color= #0000ff;]return [color= #ff0000;]0;

           [color= #000000;]}

قمنا بعمل طلاببن وأدخلناهم في الDataBase ، واحد ناجح والثاني راسب :( ، والثالث نسخه من الأول .. وبالتالي المخرج يكون كالتالي :

StudentSystem5.PNG

واوو ، كل هذا الشغل في برنامج بسيط مثل هذا ؟

نعم ، أنت الأن C++ professional وليس C++ newbie :)

تبقى الكلاس الأخير "الواجهه" User Inerface ، وهنا نريد أي واجهه نستطيع من خلالها أدخال المعلومات واٍستخراجها ..

لن نحتاج الى كتابه كلاس كامل في واجهتنا هذه ، فقط جمله while مستمره ، وفيها قائمه تخرج للمستخدم وجمله switch تختار الخيار المناسب ..

ملف UserInterface.cpp :

[color= #007f00;]// UserInterface.cpp

 

         [color= #007f00;]#include <iostream>

         [color= #007f00;]#include "DataBase.h"

 

         [color= #0000ff;]using [color= #0000ff;]namespace std;

         [color= #0000ff;]using [color= #0000ff;]namespace MyRecord;

 

         [color= #0000ff;]int menu [color= #000000;]([color= #000000;]);

         [color= #0000ff;]void addStudent [color= #000000;](DataBase[color= #000000;]& db[color= #000000;]);

         [color= #0000ff;]void addDegree  [color= #000000;](DataBase[color= #000000;]& db[color= #000000;]);

         [color= #0000ff;]void subDegree  [color= #000000;](DataBase[color= #000000;]& db[color= #000000;]);

 

         [color= #0000ff;]int main [color= #000000;]([color= #0000ff;]int argc , [color= #0000ff;]char[color= #000000;]** argv[color= #000000;])

         [color= #000000;]{

             DataBase studentDB;

             [color= #0000ff;]bool state [color= #000000;]= true;

 

             [color= #0000ff;]while [color= #000000;]( state [color= #000000;])

             [color= #000000;]{

                 [color= #0000ff;]switch [color= #000000;]( menu[color= #000000;]([color= #000000;]) [color= #000000;])

                 [color= #000000;]{

                     [color= #0000ff;]case [color= #ff0000;]1[color= #000000;]:

                         addStudent[color= #000000;](studentDB[color= #000000;]);

                         break;

 

                     [color= #0000ff;]case [color= #ff0000;]2[color= #000000;]:

                         addDegree[color= #000000;](studentDB[color= #000000;]);

                         break;

 

                     [color= #0000ff;]case [color= #ff0000;]3[color= #000000;]:

                         subDegree[color= #000000;](studentDB[color= #000000;]);

                         break;

 

                     [color= #0000ff;]case [color= #ff0000;]4[color= #000000;]:

                         studentDB.[color= #808000;]displayAll[color= #000000;]([color= #000000;]);

                         break;

 

                     [color= #0000ff;]case [color= #ff0000;]5[color= #000000;]:

                         studentDB.[color= #808000;]displayPass[color= #000000;]([color= #000000;]);

                         break;

 

                     [color= #0000ff;]case [color= #ff0000;]6[color= #000000;]:

                         studentDB.[color= #808000;]displayFail[color= #000000;]([color= #000000;]);

                         break;

 

                     [color= #0000ff;]case [color= #ff0000;]7 [color= #000000;]:

                         state [color= #000000;]= false;

                         break;

 

                     [color= #0000ff;]default [color= #000000;]:

                         cerr [color= #000000;]<< [color= #A31515;]"Invalid Choice , try Again ...";

                         break;

 

                 [color= #000000;]}

             [color= #000000;]}

 

             [color= #0000ff;]return [color= #ff0000;]0;

         [color= #000000;]}

 

         [color= #0000ff;]int menu [color= #000000;]([color= #000000;])

         [color= #000000;]{

             cout [color= #000000;]<< endl [color= #000000;]<< endl  [color= #000000;]<< endl;

 

             cout [color= #000000;]<< [color= #A31515;]"Student DataBase " [color= #000000;]<< endl 

                  [color= #000000;]<< [color= #A31515;]"-----------------" [color= #000000;]<< endl

                  [color= #000000;]<< [color= #A31515;]"1) Add Student "      [color= #000000;]<< endl

                  [color= #000000;]<< [color= #A31515;]"2) Add Degree  "   [color= #000000;]<< endl

                  [color= #000000;]<< [color= #A31515;]"3) Sub Degree  "   [color= #000000;]<< endl

                  [color= #000000;]<< [color= #A31515;]"4) List All Student " [color= #000000;]<< endl 

                  [color= #000000;]<< [color= #A31515;]"5) List All Pass Student " [color= #000000;]<< endl

                  [color= #000000;]<< [color= #A31515;]"6) List All Fail Student " [color= #000000;]<< endl

                  [color= #000000;]<< [color= #A31515;]"7) Close DataBase System " [color= #000000;]<< endl

                  [color= #000000;]<< [color= #A31515;]"t --> ";

 

             [color= #0000ff;]int selection;

             cin [color= #000000;]>> selection;

 

             [color= #0000ff;]return selection;

         [color= #000000;]}

 

         [color= #0000ff;]void addStudent [color= #000000;](DataBase[color= #000000;]& db[color= #000000;]) 

         [color= #000000;]{

             string firstName;

             string lastName;

 

             cout [color= #000000;]<< [color= #A31515;]"Enter First Name : ";

             cin [color= #000000;]>> firstName;

 

             cout [color= #000000;]<< [color= #A31515;]"Enter Last Name : ";

             cin [color= #000000;]>> lastName;

 

             [color= #0000ff;]int degree;

             cout [color= #000000;]<< [color= #A31515;]"Enter Degree : ";

             cin [color= #000000;]>> degree;

 

             [color= #0000ff;]try

             [color= #000000;]{

                 db.[color= #808000;]addStudent[color= #000000;](firstName,lastName,degree[color= #000000;]);

             [color= #000000;]}

             [color= #0000ff;]catch [color= #000000;](exception ex[color= #000000;])

             [color= #000000;]{    cerr [color= #000000;]<< [color= #A31515;]"Unable To Add New Student !n";    [color= #000000;]}

         [color= #000000;]}

 

         [color= #0000ff;]void addDegree  [color= #000000;](DataBase[color= #000000;]& db[color= #000000;]) 

         [color= #000000;]{

             string firstName;

             string lastName;

 

             cout [color= #000000;]<< [color= #A31515;]"Enter First Name : ";

             cin [color= #000000;]>> firstName;

 

             cout [color= #000000;]<< [color= #A31515;]"Enter Last Name : ";

             cin [color= #000000;]>> lastName;

 

             [color= #0000ff;]int degree;

             cout [color= #000000;]<< [color= #A31515;]"Enter Addtion Degree : ";

             cin [color= #000000;]>> degree;

 

             [color= #0000ff;]try

             [color= #000000;]{

                 Student[color= #000000;]& s [color= #000000;]= db.[color= #808000;]getStudent[color= #000000;](firstName,lastName[color= #000000;]);

                 s [color= #000000;]= s [color= #000000;]+ degree;

             [color= #000000;]}

             [color= #0000ff;]catch [color= #000000;](exception ex[color= #000000;])

             [color= #000000;]{    cerr [color= #000000;]<< [color= #A31515;]"Unable to Add Degree to Student !n";    [color= #000000;]}

         [color= #000000;]}

 

         [color= #0000ff;]void subDegree  [color= #000000;](DataBase[color= #000000;]& db[color= #000000;]) 

         [color= #000000;]{

             [color= #0000ff;]int number;

             cout [color= #000000;]<< [color= #A31515;]"Enter Student Number : ";

             cin [color= #000000;]>>  number;

 

             [color= #0000ff;]int degree;

             cout [color= #000000;]<< [color= #A31515;]"Enter Subtraction Degree : ";

             cin [color= #000000;]>> degree;

 

             [color= #0000ff;]try

             [color= #000000;]{

                 Student[color= #000000;]& s [color= #000000;]= db.[color= #808000;]getStudent[color= #000000;](number[color= #000000;]);

                 s [color= #000000;]= s [color= #000000;]- degree;

             [color= #000000;]}

             [color= #0000ff;]catch [color= #000000;](exception ex[color= #000000;])

             [color= #000000;]{    cerr [color= #000000;]<< [color= #A31515;]"Unable to Sub Degree From Student !n";    [color= #000000;]}

         [color= #000000;]}

قمنا بتعريف 3 دوال ، addStudent لادخال بيانات طالب من الكيبورد وبعدها تدخله في المصفوفه .. جمله الأدخال في المصفوفه تكون في try و catch وفي حال حدثت مشكله هناك سوف يتنفذ جزء catch هنا . أما addDegree فتقوم بالحصول على Reference للطالب وبعدها تقوم باجراء الزياده ونفس الأمر مع subDegree ..

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

g++ *.cpp -o n.exe

وسيخرج لك الملف n وهو الملف التنفيذي .. المخرج من البرنامج :

StudentSystem4.PNG

Congratulation

أنت الأن في الطريق الصحيح لاحتراف سي++ :)

ماذا بعد ذلك ؟ .....

أقرأ :

professional C++ Programmer

كتاب متقدم كما هو واضح من الأسم ،،، للمعلوميه أستفدت منه للغايه في البرنامج ، حيث يوجد برنامج مشابه له في الكتاب ;) .

Thinking in C++

كتاب ممتاز جدا ، يتحدث عن فلسفه سي++ ويقارنها بسي ، مفيد جدا لمبرمجي سي .

وأي كتاب يتحدث عن OOA/D ، سنذكر بعضا منها المره القادمه ان شاء الله ..

في المرفق جميع الملفات ، بالأضافه الى مجلد test يحتوي على ملفات الأختبار ..

الى اللقاء .

Student_Mark_System.rar

تم تعديل هذه المشاركة بواسطة اللؤْلؤة رغَـــد في 25 أغسطس 2008 في 17:08

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#2

السلام عليكم ورحمة الله وبركاته

شكرا لك أخي Wajdy Essam

موضوع جدا أفادني وأعطاني فكرة جيدة عن طريقة التحليل لتصميم الطبقات .

لكن عندي سؤال بسيط لو سمحت ...

في أخر كود ..

كتبت

void addStudent (DataBase& db)

لماذا يكون الوسيط إشارة(مرجع) إلى DataBase ونحن نريد حفظ القيم المدخلة وهي موجودة في متغيرات الطبقة Student

الطبقة DataBase لا تحمل متغيرات لحفظ بيانات الطالب الجديد فلماذا جعلناها وسيطا لدالة إضافة طالب ..

أتمنى يكون واضحة المشكلة

ويعطيك ألف ألف عافية يا أخ Wajdy Essam

تحيااااتي ,,

للصداقة البرمجية

pro-vb6@hotmail.com

#3

روعة10

:happy:

مقال غنيّ و كثيف بالمعلومات حول OOD و OOP...

,.,.,.,..,,.,.

اقتراح:

وضع الشيفرات الموجودة بالمقال داخل صندوق الوسْم GCode = cpp, افضل..او تعطيني الإذن للتحرير؟ او كما تريد اخي وجدي..ولكن الأفضل للقراءة للشيفرة

,.,.,.,.,.,.,.,.

سأضع رابط إن شاء الله لهذا المقال ضمن كتاب الأعضاء المثبت, شاكرة لك هذا المجهود اخي وجدي

و هل من مزيد؟

:happy:

[وسط]

♥ Countess ♥

57899411.gif

♥

[/وسط]

#4

الله يخليكم ،

أخ أبو فهيد سؤالك جيد ،

أولا الداله addStudent في الواجهه داله مهمتها الأدخال في DataBase ، جميل ...

اذا رأيت في أول ملف الواجهه UserInterface قمنا بانشاء الـ DataBase في بدايه main ..

الأن لكي أضيف أي طالب الى هذه الـ DataBase يجب أن يكون عن طريق كائن من DataBase ، اليس كذلك ..

لذلك أرسلت الكائن (بالمرجع) لأنه أفضل من تمريره بالقيمه -قد تكلمنا عنه في القسم ، راجع الكتاب المثبت- ... ومن داخل الداله سأضيف الأسم والدرجه في الDataBase عن طريق استدعاء الداله

db.addStudent(firstName,lastName,degree);

لا تكون لخبطت بين الداله addStudent الموجوده في الواجهه والتي تستقبل Reference to DataBase

مع الداله addStudent الموجوده في DataBase والتي تستقبل اسم ودرجه وتدرج الطالب في المصفوفه ..

ولكي تفهم أكثر ، جرب في الكود في الواجهه ، وخذ تعريف الداله addStudent وضيفه في المكان المناسب في switch ، يعني عندما يختار المستخدم اضافه طالب لا يكون هناك استدعاء لداله (ضع الكود هنا فقط وهو ادخال الأسم والدرجه ومن ثم ادخاله الى DataBase ) ..

وهنا سوف تدرك أننا في الواجهه سوف نقوم فقط بادخال الأسم والدرجه ومن ثم استدعاء الداله addStudent عن طريق كائن الDataBase ...

أخت رغد ، حياك الله ...

بالتأكيد قومي بتغييرها ... فلقد حاولت بنفسى عده مرات ولم أستطيع :( .. لم أجد ذلك التنسيق الجميل ..

اقتباس
و هل من مزيد؟

باذنه تعالى ، لكن بعدما تريني كيف تنسقي الكود ، والا ما في :lol:

بالتوفيق ،

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#5

:wacko: :wacko:

انت عامل Tab بشكل مبالغ اخي وجدي!!!

,.,.,.,.,.,.,.,..,,.

وحاول مرة تانية لا تعامل الشيفرة(الصندوق والنصوص تبعت الشيفرة) متل باقي النصوص...أنا عارفة انت بتكتب كل المقال وبتعمل select all وبتعطيهن تنسيق واحد..صح؟؟ :happy:

.,,,.,.,.,.,.,.,.,.

حاول تعمل تخفيف على الأقل للـ Tab وانا إن شاء الله رايحة اعملهن...يعني الآن رايحة اعدلهن وانت دورك تحرر مرة تانية وفقط تعمل تنسيق للمسافات الـ tab في الشيفرة او تعيد نسخهن وإلصاقهن مرة تانية من المحرر الخاص بك..

,.,.,.,.,.,.,.,.

:happy: لحظات وما تخاف

[وسط]

♥ Countess ♥

57899411.gif

♥

[/وسط]

#6

وعليكم السلام ورحمة الله ,

- قرأت المقال ,, مقال مميز .. وشرحك واضح .. وأسلوبك سلس .. وتنسيق ممتاز , ما شاء الله .

- لي ملاحظة على العنوان ,, عنوان الموضوع .. كنت أتوقع من قراءة العنوان أنك تريد أن تتكلم عن Hello World , بينما الموضوع هو أعمق من ذلك بكثير ’

الأخت رغد اضافة مقالك لقائمة الكتاب بعنوان أكثر وضوح :

مقال|مثال عملي حوْل مفاهيم OOA/D

طبعا الرأي لك أخي وجدي , والعنوان لا يمثل شيء أمام المحتوى ,, لكن هي مجرد ملاحظة عابرة ,

أتطلّع لمواضيع على شاكلة موضوعك هذا ..

وشكرا لقائمة الكتب التي أضفتها ,, سأبحث عن : professional C++ Programmer .

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#8

شكرا لك أخي Wajdy Essam على التوظيح

يعطيك العافية,,

تحيااااتي,,

للصداقة البرمجية

pro-vb6@hotmail.com

مواضيع مشابهة