السلام عليكم ورحمة الله وبركاتة
عودا حميدا ان شاء الله
سوف نتناول في هذه المحاضرة المواضيع التالية :
1- أعضاء البيانات ودوال الإعداد set, ودوال التحصيل get.
2- إعطاء الكائنات قيم أولية بالبواني .
3- وضع الصنف في ملف منفصل .
4- فصل واجهة الصنف عن تطبيقة .
5- تدقيق البيانات أثناء دالة الإعداد set.
بسم الله وبه نستعين
أعضاء البيانات , ودوال الإعداد set , ودوال التحصيل get
في أمثلتنا السابقة كانت جميع المتغيرات يعلن عنها في الدالة الرئيسة الـ main, وتسمى
هذه المتغيرات بالمتغيرات المحلية , والمتغيرات المحلية يحدد التعامل معها على أساس عدة
امور أهمها :
1- أن المتغير المحلي يمكن أستخدامة بداية من الصف الذي أعلن عنه فيه إلى نهاية قوس
إغلاق تعريف الدالة.
2- المتغير المحلي يجب أن يكون معرف ( محدد النوع والإسم ) قبل الإستخدام.
3- لا يمكن الوصول للمتغير المحلي من خارج الدالة التي تم الإعلان عنه فيها.
4- عند إنتهاء الدالة التي تم الإعلان عن المعرف فيها تكون قيم هذه المتغيرات فقدت.
وتجدر الإشارة هنا أن هناك إستثناء, وهي المتغيرات المحلية الساكنة التي سوف نتناولها في الفصل السادس.
ذكرنا أيضا أن كل كائن لديه خصائص ( أعضاء بيانات ) تكون محمولة معه في كل برنامج
يستخدم فيه, أيضا لديهم على الأقل دالة عضو واحدة لها نفس ظروف الخاصية ( عضو البيانات ).
دوال الأعضاء تسخدم في إدارة ومعاملة أعضاء البيانات في كل صنف ( في نفس الصنف ).
في كودنا التالي سوف نناقش صنف GradeBook يحتوي على عضو بيانات courseName يمثل
إسم الدورة لكائن الصنف :
كود 1
1 // Fig. 3.5: fig03_05.cpp
2 // Define class GradeBook that contains a courseName data member
3 // and member functions to set and get its value;
4 // Create and manipulate a GradeBook object with these functions.
5 #include <iostream>
6 using std::cout;
7 using std::cin;
8 using std::endl;
9
10 #include <string> // program uses C++ standard string class
11 using std::string;
12 using std::getline;
13
14 // GradeBook class definition
15 class GradeBook
16 {
17 public:
18 // function that sets the course name
19 void setCourseName( string name )
20 {
21 courseName = name; // store the course name in the object
22 } // end function setCourseName
23
24 // function that gets the course name
25 string getCourseName()
26 {
27 return courseName; // return the object's courseName
28 } // end function getCourseName
29
30 // function that displays a welcome message
31 void displayMessage()
32 {
33 // this statement calls getCourseName to get the
34 // name of the course this GradeBook represents
35 cout << "Welcome to the grade book forn" << getCourseName() << "!"
36 << endl;
37 } // end function displayMessage
38 private:
39 string courseName; // course name for this GradeBook
40 }; // end class GradeBook
41
42 // function main begins program execution
43 int main()
44 {
45 string nameOfCourse; // string of characters to store the course name
46 GradeBook myGradeBook; // create a GradeBook object named myGradeBook
47
48 // display initial value of courseName
49 cout << "Initial course name is: " << myGradeBook.getCourseName()
50 << endl;
51
52 // prompt for, input and set course name
53 cout << "nPlease enter the course name:" << endl;
54 getline( cin, nameOfCourse ); // read a course name with blanks
55 myGradeBook.setCourseName( nameOfCourse ); // set the course name
56
57 cout << endl; // outputs a blank line
58 myGradeBook.displayMessage(); // display message with new course name
59 return 0; // indicate successful termination
60 } // end mainالصنف GradeBook مع عضو بيانات ودالة إعداد set , ودالة حصول get :
في الكود السابق, الصنف GradeBook يحتوي على إسم الدورة كعضو بيانات يمكن استخدامة
والتعديل عليه خلال وقف تنفيذ البرنامج. أيضا نلاحظ هناك دوال عضو جديدة وهي setCourseName وَ
getCourseName , بالإضافة إلى دالتنا السابقة وهي displayMessage. وظيفة
الدالة setCourseName هو تخزين إسم الدورة في عضو البيانات في الكائن, والدالة
getCourseName وضيفتها الحصول على إسم الدورة متى ما أردنا استخدامه. والدالة displayMessage
وظيفتها معروفة كما ذكرنا من قبل وهي طباعة رسالة الترحيب بالإضافة إلى اسم الدورة.
لممارسة البرمجة بشكل جيد :
ضع خط فاصل بين تعريف وآخر لدوال العضو في الصنف من أجل وضوح القراءة.
في السطر 39 تلاحظون أنه تم الإعلان عن متغير جديد هو courseName من نوع string وهو
عبارة عن عضو بيانات في الصنف GradeBook. وتلاحظون أيضا أننا استخدمنا
هذا العضو في الدوال كما هو موضح في السطور ( 9-22 , 25-28 , 31-37 ).
والفائدة من إنشاء هذا العضو courseName في الصنف GradeBook هوتمكين دوال
هذا الصنف من إدارته والتعامل معه, وسوف نأتي لتفصيل هذا إن شاء الله .
محدد الوصول private :
عادة ً تكون جميع أعضاء بيانات الصنف معرفة بعد الكلمة المفتاحية private. تعتبر هذه
الكلمة محدد وصول خاص. لاحظ السطر 38, هذا يعني أن عضو البيانات courseName خاص
الوصول, لا يمكن الوصول له من خارج هذا الصنف, على سبيل المثال لا نستطيع أن نصله
من الدالة main ولا من أي دالة أخرى, فقط يمكن الوصول إليه من خلال جسم الصنف وبالتحديد
من دوال الأعضاء لنفس الصنف. حاول أن تصله من الدالة main ولاحظ ما يحدث ؟!!
فكرة جيدة في هندسة البرمجيات :
يجب أن تكون أعضاء البيانات معلن عنها بشكل خاص ودوال الأعضاء يجب أن يعلن عنها بشكل
عام ( يمكن الوصول للأعضاء عن طريق الدوال في نفس الصنف ).
خطأ برمجي شائع :
محاولة الوصول إلى الأعضاء الخاصة عن طريق دوال خارجية خارج الصنف يولد خطأ في الترجمة.
الإعلان عن أعضاء البيانات بمحدد وصول خاص يسمى " إخفاء البيانات " , لأن البيانات لا يمكن
الوصول لها إلا عن طريق دوال الصنف نفسه, فبهدذه الطريقة يصبح الكائن مكبسل ( مخفي ).
في كودنا السابق ( كود 1 ) , دوال الأعضاء setCourseName وَ getCourseName وَ displayMessage هي
فقط التي تستطيع الوصول إلى عضو البيانات courseName مباشرة.
دوال العضو setCourseName وَ getCourseName :
دالة العضو setCourseName ( معرفة في السطور 19-22 ) تستقبل وسيط واحد إسمه name ونوعة
string, ولا ترجع شي. وهي تقوم بإسناد المتغير name إلى عضو البيانات courseName.
دالة العضو getCourseName ( معرفة في السطور 25-28 ) لا تستقبل شي وترجع بيانات من
نوع string وتقوم بإرجاع القيمة courseName وهو عضو البيانات في الصنف.
ولتوضيح مسألة الإرجاع والبيانات المرجعة , سوف نأخذ هذا المثال: عندما نذهب إلى الصراف الآلي ( ATM )
وتريد أن تستعلم عن الرصيد, أنت تدخل ( تعطي الدالة ) الرقم السري ورقم خيار الإستعلام, والصراف يخرج
لك ( يعطيك ) مقدار رصيد حسابك.
خطأ برمجي شائع :
نسيان إرجاع قيمة لدالة من المفترض أن ترجع قيمة معينة يولد خطأ في الترجمة.
قلنا في السابق, أعضاء البيانات الخاصة لا يمكن الوصول لها إلا عن طريق الصنف نفسه, فلو تلاحظ
السطرين 21, 27 , تجد أننا إستخدمنا courseName في هذه الدوال دون الإعلان عنهم فيها.
وهذا ما يفسر ما أشرنا إليه مسبقا. والترتيب مهم هنا , فمثلا أن ننادي الدالة setCourseName لإعداد
العضو ومن ثم مناداة الدالة getCourseName للحصول على القيمة المعدة, فلا يمكن تقديم الثانية على الاولى.
إختبار الصنف GradeBook ( السطور 43-60 ) :
السطر 46 إنشاء كائن GradeBook وتسميته بـ myGradeBook
السطور 49-50 طباعة إسم الدورة قبل كل شي ( دوال الإعداد والحصول ) وبملاحظة أو سطر من الإخراج لا
يعرض إسم الدورة , لأن عضو بيانات الكائن courseName فاضي في هذه اللحظة.
السطر 53 يعطي رسالة للمستخدم لكي يدخل إسم الدورة.
السطر 54 الدالة getline لإدخال إسم الدورة , تم شرحها في المحاضرة الماضية.
السطر 55 يتم استدعاء ( مناداة ) دالة الكائن myGradeBook وهي setCoursName وتقوم
بإرسال المتغير nameOfCourse كمتغير ممرر للدالة ( في مثالنا هذا عند عملية الإستدعاء في هذا السطر
فإن نسخة من المتغير nameOfCourse توضع في الوسيط.).
السطر 58 إستدعاء لدالة العضو displayMessage للكائن myGradeBook لتعرض رسالة ترحيب
بالإضافة إلى اسم الدورة.
هندسة الرمجيات مع دوال الـ set والـ get :
تزويد الصنف بدوال الـ set والـ get تمكن عملاء الصنف من الوصول إلى البيانات المخفية ولكن
بشكل غير مباشر. العميل يعلم كيف يعدل أو يأخذ البيانات, لكنه لا يعلم كيف يتم ذلك.
فكرة جيدة في هندسة البرمجيات :
المبرمجين المحترفين يتوقعون أنه سوف تحدث تغييرات في البرامج, فلذلك يقومون بتكييف أصنافهم على ذلك.
تحديث الـ UML للصنف GradeBook :
الشكل التالي يمثل الـ UML المحدث للصنف GradeBook في البرنامج السابق ( كود 1 ) بعد التعديلات واضافة
دوال الـ set والـ get وبيانات الأعضاء :
التغيير هو اضافة دالتين إضافيتين وخاصية ( عضو بيانات ) جديدة وهي courseName كما
تلاحظون, في المستطيل في المنتصف وضعنا إسم الخاصية متبوعة بنقطتين ثم نوع الخاصية. يسبق
إسم الخاصية إشارة ( - ) وهي تدل على أن هذه الخاصية محددة الوصول ( أي خاصة )
ليصبح تمثيلها كالتالي : - اسم الخاصية : نوع الخاصية
وهناك تغير آخر أيضا , في دالة العضو getCourseName, كما تلاحظون بعد الأقواس
هناك نقطتان عموديتان بعدهما تأتي كلمة string, وجود النقطتين بعد الأقواس يدل على أن
العملية ( دالة العضو ) ترجع بيانات. الـ string هنا تمثل نوع البيانات المرجعة.
إعطاء الكائنات قيم أولية بالبواني
في المثال السابق ( كود 1 ) , لاحظنا عندما انشأنا الكائن وعند طباعة عضو بياناته courseName كيف كان نا تج الطباعة
سطر فاضي , وهذا يدل على أن الكائن وبشكل افتراضي تم إعداده.
عند الإعلان عن الصنف تستطيع إنشاء باني ( وهو عبارة دالة عضو ) , حيث يعطي الكائن عن انشاؤه قيمة أولية عن طريق
أحد اعضاؤه. والفرق بين بالبواني ودوال الأعضاء الأخرى هو أن البواني لا تستطيع أن ترجع قيمة ولا نوع قيمة ( حتى لو
كانت void ). في العادة ,وعند الإعلان عن البواني , يعلن عنها في خانة الوصول العالم ( أي بعد محدد الوصول public ).
بأي حال من الأحوال , عند انشاء كل كائن لأي صنف في الـ C++ فإن ذلك يتطلب إنشاء باني سواء كان افتراضي أو من إنشاء المستخدم.
حيث يـُستدعي هذا الباني عند كل عملية إنشاء للكائن لكي يتأكد من أن الكائن تم انشاؤه بشكل صحيح. أما الباني الإفتراضي فإن المترجم يقوم
بإنشاؤه تلقائيا عندما لا يقوم المستخدم بذلك. على سبيل المثال , في برنامجنا السابق , عند السطر 46, قمنا بإنشاء كائن لدفتر الدرجات , في
هذه الحالة لم يقوم المستخدم بإنشاء باني , وبشكل تلقائي وخفي , قام المترجم بإنشاء باني افتراضي وسوف يتم استدعاؤه. ولمزيد من التوضيح
نأخذ هذا البرنامج كمثال توضيحي :
كود 2 :
1 // Fig. 3.7: fig03_07.cpp
2 // Instantiating multiple objects of the GradeBook class and using
3 // the GradeBook constructor to specify the course name
4 // when each GradeBook object is created.
5 #include <iostream>
6 using std::cout;
7 using std::endl;
8
9 #include <string> // program uses C++ standard string class
10 using std::string;
11
12 // GradeBook class definition
13 class GradeBook
14 {
15 public:
16 // constructor initializes courseName with string supplied as argument
17 GradeBook( string name )
18 {
19 setCourseName( name ); // call set function to initialize courseName
20 } // end GradeBook constructor
21
22 // function to set the course name
23 void setCourseName( string name )
24 {
25 courseName = name; // store the course name in the object
26 } // end function setCourseName
27
28 // function to get the course name
29 string getCourseName()
30 {
31 return courseName; // return object's courseName
32 } // end function getCourseName
33
34 // display a welcome message to the GradeBook user
35 void displayMessage()
36 {
37 // call getCourseName to get the courseName
38 cout << "Welcome to the grade book forn" << getCourseName()
39 << "!" << endl;
40 } // end function displayMessage
41 private:
42 string courseName; // course name for this GradeBook
43 }; // end class GradeBook
44
45 // function main begins program execution
46 int main()
47 {
48 // create two GradeBook objects
49 GradeBook gradeBook1( "CS101 Introduction to C++ Programming" );
50 GradeBook gradeBook2( "CS102 Data Structures in C++" );
51
52 // display initial value of courseName for each GradeBook
53 cout << "gradeBook1 created for course: " << gradeBook1.getCourseName()
54 << "ngradeBook2 created for course: " << gradeBook2.getCourseName()
55 << endl;
56 return 0; // indicate successful termination
57 } // end mainكيف نعرف الباني constructor :
في السطور ( 17-20 ) , قمنا بتعريف باني للصنف GradeBook, ( لاحظ أن الباني يأخذ دائما نفس اسم الصنف ). في كثير من
الأحيان نكلف الباني بمهمة ما من خلال تزويدة ببعض الوسائط لإنجازها.
في السطر 17, يتضح من هذا السطر أن الباني يستقبل متغير من نوع سلسلة رمزية ( string ) ولا يقوم بإرجاع أي شي حتى لو كان ( void ).
في السطر 19, نلاحظ أن هناك إستدعاء ( مناداة ) للدالة setCourseName, وقد قمنا بإرسال القيمة الممررة name لها.
حيث تقوم هذه الدالة بإسناد هذه القيمة الممررة لعضو البيانات courseName كما هو واضح في السطور ( 23-26 ).
فحص الصنف GradeBook :
في السطر 49, ننشيء الكائن ونعطيه قيمة أولية لنتأكد بذلك أن الكائن تم انشاؤه وتشغيله وأن كل شي على مايرام. الكائن اسمه gradeBook1, وقد
قمنا بإرسال القيمة الممرره " CS101 Introduction to C++ Programming " للكائن لكي تكون إسما للدورة كقيمة أولية.
في السطر 50, نكرر العمليه لكائن جديد اسمه gradeBook2, وهذه المره نمرر القيمة " CS102 Data Structure in C++ " لإعطاء
اسم الدورة في هذا الكائن كقيمة اولية أيضا.
السطور ( 53-54 ) تستخدم الدالة getCourse لنحصل على إسم الدورة في كل كائن ونقوم بعرضه للمستخدم.
طريقتين لتزويد الصنف بباني إفتراضي :
أي باني لا يأخذ قيمة ممرره يسمى إفتراضي, الصنف يأخذ باني إفتراضي في حالتين :
1- إذا لم يحدد المستخدم باني افتراضي, المترجم بشكل تلقائي ينشيء باني إفتراضي ( عيك أن تخمن بالضبط مالذي سوف يفعله هذا الباني الإفتراضي ).
2- يقوم المستخدم بإنشاء باني افتراضي, حيث لا يستقبل قيم ممرره. يحتوي هذا الباني عادة على عمليات اسناد أولية لأعضاء بيانات معينة.
ملاحظه : جميع البرامج السابقة التي تحتوي على أصناف, المترجم هو من تولى إنشاء بواني لهذه الأصناف.
فكرة مفيدة في هندسة البرمجيات :
فكرة انشاء بواني بواسطة المستخدم لكل كائن يتم انشاؤه, وذلك للتأكد من سلامة إنشاء الكائن وعدم الإعتماد على كود العميل في ذلك ( التأكد من سلامة الكائن ).
تحديث الـ UML للصنف GradeBook بعد إضافة الباني :
الـ UML يمثل برنامجنا السابق
التغيير هو إضافة باني يحتوي على وسيط واحد ( اسمه name ونوعة string ), وقد ذكرنا سابقا أن الباني يعتبر دالة عضو, لذلك وضعناه في
المستطيل السفلي في أعلى القائمة ( في المكان الذي عادة ً ما يوضع فيه الباني ). ولكي نميز الباني عن دوال الأعضاء الأخرى , وضعنا قبل اسم الباني
الكلمة constructor بين أقواس صغيرة , كما هو موضح في الـ UML في الأعلى .
وضع الصنف في ملف منفصل
الآن نريد أن نتقدم خطوة نحو تعزيز امكانية إعادة استخدام الصنف, وهي وضع الصنف في ملف منفصل حتى يمكن استخدامة من أي
برنامج ( عميل ) , على سبيل المثال دالة الـ main. لو أي مبرمج أراد أن يستخدم صنفنا GradeBook في برنامجه سوف
لن يستطيع ان يضمـّن ملف برنامجنا هذا (كود 2) في برنامجه, وذلك لأن هناك دالة رئيسية أخرى . ( حاول أن تقوم بترجمة برنامج يحتوي
على دالتين رئيسيتين وتأمل رسائل الخطأ التي سوف تظهر لك ).
كود 3 :
1 // Fig. 3.9: GradeBook.h
2 // GradeBook class definition in a separate file from main.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 #include <string> // class GradeBook uses C++ standard string class
8 using std::string;
9
10 // GradeBook class definition
11 class GradeBook
12 {
13 public:
14 // constructor initializes courseName with string supplied as argument
15 GradeBook( string name )
16 {
17 setCourseName( name ); // call set function to initialize courseName
18 } // end GradeBook constructor
19
20 // function to set the course name
21 void setCourseName( string name )
22 {
23 courseName = name; // store the course name in the object
24 } // end function setCourseName
25
26 // function to get the course name
27 string getCourseName()
28 {
29 return courseName; // return object's courseName
30 } // end function getCourseName
31
32 // display a welcome message to the GradeBook user
33 void displayMessage()
34 {
35 // call getCourseName to get the courseName
36 cout << "Welcome to the grade book forn" << getCourseName()
37 << "!" << endl;
38 } // end function displayMessage
39 private:
40 string courseName; // course name for this GradeBook
41 }; // end class GradeBookكود 4
1 // Fig. 3.10: fig03_10.cpp
2 // Including class GradeBook from file GradeBook.h for use in main.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 #include "GradeBook.h" // include definition of class GradeBook
8
9 // function main begins program execution
10 int main()
11 {
12 // create two GradeBook objects
13 GradeBook gradeBook1( "CS101 Introduction to C++ Programming" );
14 GradeBook gradeBook2( "CS102 Data Structures in C++" );
15
16 // display initial value of courseName for each GradeBook
17 cout << "gradeBook1 created for course: " << gradeBook1.getCourseName()
18 << "ngradeBook2 created for course: " << gradeBook2.getCourseName()
19 << endl;
20 return 0; // indicate successful termination
21 } // end mainملفات الترويسة ( header files ):
كل أمثلتنا ( برامجنا ) السابقة تحتوي على ملف واحد له الإمتداد .cpp , وهو ما يسمى بملف كود المصدر source-code file , هذا
البرنامج بشكله سوف يستخدم بشكل خاص ولكي نتيح تعميمة ليتمكن الجميع من استخدامه سوف نقوم بفصل الصنف كاملا بملف ترويسة
إسمه GradeBook.h (كود 3 ) وإلى ملف آخر إسمه fig03_10.cpp كما هو واضح بالشكل ( كود 4 ).
كما تلاحظون في إسم الملف GradeBook.h أنه ينتهي بالاحقة .h وهي تدل على أن هذا الملف ملف ترويسة.
في (كود 3 ) نلاحظ أن الملف يحتوي فقط على الصنف ( السطور 11- 41 ), في السطور ( 3-8 ) الصلاحيات المعطاه
لهذهالنصف يإستخدام مساحات الأسما والمكتبة < string > التي تحتوي على كائنات سلاسل الرموز.
في الملف الثاني ( كود 4 ) توجد الدالة الرئيسية ( mian ) - الدالة السواقة - وفيه استخدمنا موجه المعالج الأولي #include وذلك
لتضمين ملف صنف الـ GradeBook كما تلاحظون في السطر 7.
المبرمجين في الحقيقة عملهم ليس انشاء برامج بشكل دائم , بل يقومون بإنشاء أصناف في ملفات منفصله ويقومون بفحصها عن طريق
البرنامج السواق " البرنامج الذي يحتوي على دالة الـ main " , حيث يقومون بتضمين اصنافهم في هذه البرنامج عن طريق موجه
المعالج الأولي كما في السطر 7 ( كود 4 ) ويستخدم البرنامج الصنف كما لو كان مكتوب في نفس الكود, وايضا تستطيع هذه الأصناف تضمين
هذه الأصناف في أي برنامج نريد عن طريق هذه الموجه.
كيفية تحديد مكان ملفات الترويسة ( header files ):
عندما يواجه المعالج الأولي ملف ترويسة مضمن فإنه يقوم بتحديد مكانه عن طريق مساره الموضح بين علامتي التنصيص, فإذا كان
المسار غير محدد " أي مكتوب إسم الملف فقط " فإنه سوف يبحث عنه بنفس المجلد.
خطأ برمجي شائع :
إستخدام العلامات <> لتضمين ملف ترويسة من تعريف المستخدم , وهذا خطأ , حيث هذه العلامات خاصة بتضمين ملفات الترويسة
التي تكون مكتوبة في مكتبة الـ C++ القياسية فقط. فيجب أن تكتب إذن علامتي التصنيس " " بدلا من ذلك.
مسألة مهمه في هذه الطريقة :
في حقيقة الأمر أن هذه الطريقة تكشف تطبيقات الصنف بشكل كامل, ويكون مكشوف لجميع من تعامل معه هذا الصنف, وهذه طريقة
لا تصلح في تقنية إخفاء البيانات.
ما يحتاجه عملاء الصنف هو معرفة أسماء دوال الأعضاء وماذا تستقبل وماذا تقوم بإرجاعة, ولا يحتاج كود العميل معرفة تفاصيل
تطبيق هذه الدوال. إذن كود العميل يعرف تفاصيل الصنف, ربما يقوم بتأسيس كود يعتمد هذه التفاصيل وهذه خطأ فادح, يجب أن
لا يستطيع العملاء أن يقومون بهذا التغيير.
في موضوعنا القادم سوف نقوم بشرح طريقة فصل واجهة الصنف عن تفاصيل تطبيق دوالها لتلاشي العيب السابق.
فصل واجهة الصنف عن تطبيقة
لكي نعرف ماهية الواجهة سوف نذكر مثال الراديو ونشبه بالنصف لتوضيح ذلك.
نرى في واجهة أي راديو أزرار تحكمات تمكن المستخدم من رفع الصوت وتغيير المحطه , والإختيار ما بين الـ fm والـ am , مختلف
أنواع اجهزة الراديو تحمل هذه الواجه ولكن بأشكال مختلفه.
المستخدم يعلم كيف يرفع الصوت ويغير المحطه ولكن لا يعلم تفاصيل ذلك, بشكل مشابه , واجهة الصنف كذلك. فهي توضح للعميل
ماهي دوال الأعضاء وطريقة طلبها ( ماذا تستقبل وماذا ترجع ).
كود 5
1 // Fig. 3.11: GradeBook.h
2 // GradeBook class definition. This file presents GradeBook's public
3 // interface without revealing the implementations of GradeBook's member
4 // functions, which are defined in GradeBook.cpp.
5 #include <string> // class GradeBook uses C++ standard string class
6 using std::string;
7
8 // GradeBook class definition
9 class GradeBook
10 {
11 public:
12 GradeBook( string ); // constructor that initializes courseName
13 void setCourseName( string ); // function that sets the course name
14 string getCourseName(); // function that gets the course name
15 void displayMessage(); // function that displays a welcome message
16 private:
17 string courseName; // course name for this GradeBook
18 }; // end class GradeBookكود 6
1 // Fig. 3.12: GradeBook.cpp
2 // GradeBook member-function definitions. This file contains
3 // implementations of the member functions prototyped in GradeBook.h.
4 #include <iostream>
5 using std::cout;
6 using std::endl;
7
8 #include "GradeBook.h" // include definition of class GradeBook
9
10 // constructor initializes courseName with string supplied as argument
11 GradeBook::GradeBook( string name )
12 {
13 setCourseName( name ); // call set function to initialize courseName
14 } // end GradeBook constructor
15
16 // function to set the course name
17 void GradeBook::setCourseName( string name )
18 {
19 courseName = name; // store the course name in the object
20 } // end function setCourseName
21
22 // function to get the course name
23 string GradeBook::getCourseName()
24 {
25 return courseName; // return object's courseName
26 } // end function getCourseName
27
28 // display a welcome message to the GradeBook user
29 void GradeBook::displayMessage()
30 {
31 // call getCourseName to get the courseName
32 cout << "Welcome to the grade book forn" << getCourseName()
33 << "!" << endl;
34 } // end function displayMessageكود 7
1 // Fig. 3.13: fig03_13.cpp
2 // GradeBook class demonstration after separating
3 // its interface from its implementation.
4 #include <iostream>
5 using std::cout;
6 using std::endl;
7
8 #include "GradeBook.h" // include definition of class GradeBook
9
10 // function main begins program execution
11 int main()
12 {
13 // create two GradeBook objects
14 GradeBook gradeBook1( "CS101 Introduction to C++ Programming" );
15 GradeBook gradeBook2( "CS102 Data Structures in C++" );
16
17 // display initial value of courseName for each GradeBook
18 cout << "gradeBook1 created for course: " << gradeBook1.getCourseName()
19 << "ngradeBook2 created for course: " << gradeBook2.getCourseName()
20 << endl;
21 return 0; // indicate successful termination
22 } // end mainفصل الواجهه عن التطبيق :
في موضوعنا السابق قمنا بفصل الصنف عن الدالة الـ main . والآن لتحقيق خاصية الكبسلة " التغليف " نريد أن نفصل واجهة
الصنف عن تفاصيل دوال أعضائه " التطبيق " ذلك لمنع كود العميل من إستخدام " وتغيير " هذه التفاصيل.
نقوم بفصل واجهة الصنف عن تطبيقها بشطر " تقسيم " تعريف الصنف في ( الكود 3 ) إلى ملفين , ملف
الترويسة GradeBook.h ( الكود 5 ) الذي تكون فيه الواجهه " تعرييف الصنف " وملف كود
المصدر GradeBook.cpp ( الكود 6 ) وفيه تفاصيل إجراءات الدوال " التطبيق ".
لاحظو أن ملف التطبيق هو عبارة عن كود مصدر .cpp وإسمه نفس إسم الصنف وهذا ما جرت عليه العادة.
وأخيرا, ( الكود 7 ) وهو الملف السواق الذي يقوم بفحص هذه الأصناف.
GradeBook.h تعريف واجهة الصنف مع التوصيفات الأولية للدوال ( prototypes ) :
( الكود 5 ) يحتوي على واجهة الصنف " GradeBook.h " ولو قارنا هذه الواجهه بالكود ( كود 3 ) لوجدنا أن في هذه الواجه
استخدمنا توصيف أولي لدوال الأعضاء بدلا من كتابة كامل تفاصيل الدالة كما في الكود 3.
في الكود 5 السطر 13 يعتبر توصيف أولي ( prototype ) وهي ما يحتاجه كود العميل فقط ( إسم الدالة والوسائط المستقبله
والقيم المرجعه ) وهكذا الحال مع باقي السطور ( 12, 14, 15).
والفرق بين ترويسات الدالة ( كما في السطور 15, 21, 27, 33, في الكود 3 ) والتوصيفات الأولية للدوال ( كما في
السطور 12, 13, 14, 15, في الكود 5 ) شيئين :
1- التوصيف الأولي يحتوي على فاصلة منقوطه في نهاية السطر.
2- التوصيف الأولي للدالة , تسمية الوسائط فيه غير مهمه كما لو كان ترويسة دالة.
خطأ برمجي شائع :
نسيان الفاصلة المنقوطة في نهاية التوصيف الأولي للدالة يولد خطأ نصي .
لممارسة البرمجة بشكل جيد :
كثير من المبرمجين يضعون أسماء للوسائط في التوصيفات الأولية وذلك لغرض التوثيقي والتوضيحي.
فكرة جيدة في منع الأخطاء:
يقوم بالكثير من المبرمجين بنسخ السطر الأول لتعريف الدالة " ترويسة الدالة " ووضعه بدلا من التوصيف الأولي للدالة ووضع الفاصلة
المنقوطة , وذلك لمنع التظليل.
GradeBook.cpp : تعريف دوال الأعضاء في ملف كود المصدر للتطبيق :
في الكود 6 لاحظ أن كل إسم دالة ( ترويسة الدالة ) تكون مسبوقة بإسم الصنف ثم :: , هذه البادئة تلحق كل تعريف لدالة العضو
بصنفها الخاص بها , وبدونها لن يتعرف المترجم عليها, وسوف يعتبرها دوال حره أو مفقودة .
خطأ برمجي شائع :
نسيان البادئه ( إسم الصنف :: ) يولد خطأ ترجمه.
لاحظ في السطر 8 , وهو إدراج واجهة الصنف لكي نشير بذلك لدوال الأعضاء على أنها جز من هذا الصنف.
إختبار الصنف :
لا يوجد بهِ شي جديد عن سابقه , وإذا كان هناك غير هذا عليك بإكتشافه بنفسك.
عملية الترجمة والربط :
الرسم التالي يوضح لنا عمللية الربط والترجمة التي ينتج فيها نلا تطبيق GradeBook قابل للتنفيذ :
لإنشاء برنامج بتقنية برمجة الكائنات الموجهه , في غالب الأحيان يقوم ببرمجته مبرمجين , مبرمج كود العميل , ومبرمج تطبيق الصنف , وناتج
هذا , تطبيق جاهز للإستخدام من قبل المستخدم.
مبرمج تطبيق الصنف مسؤول عن إنشاء صنف GradeBook وذلك بإنشاء ملف الترويسة GradeBook.h وكود الكائن
للصنف GradeBook الذي يحتوي على تعليمات لغة الآلة التي تقوم بتمثيل دوال عضو الـ GradeBook. وبالتالي مبرمج كود
العميل لا يعلم أي تفاصيل عما حدث.
مبرمج كود العميل لكي يستفيد من الصنف ويستطيع إستخدامه يقوم بإدراج هذه الصنف عن طريق موجه المعالج الأولي include#.
عملية الربط في آخر خطوة , بعد عملية الترجمة وأثناء عملية الربط يقوم بربط هذه الأجزاء التالية معا :
1- كود الكائن للدالة main.
2- كود الكائن لتطبيقات دوال أعضاء الصنف GradeBook.
3- كود الكائن الخاص بالمكتبات القياسية المستخدمة في البرنامج " مكتبة الـ string".
الناتج عن هذا , تطبيق دفتر درجات جاهز للإستخدام.
تدقيق البيانات أثناء دالة الإعداد set
عند استخدام دالة setCourseName في المثال ( كود 3 ) , استخدمناها فقط لإعداد عضو البيانات courseName وإسناد
قيمة له " سلسلة رموز " , في الكود التالي نريد أن ندقق في سلسلة الرموز المستقبله ونرى هل عدد رموزها أقل من او
تساوي 25 رمز ( تسمى هذه العملية بالـ validation ) :
كود 8
1 // Fig. 3.15: GradeBook.h
2 // GradeBook class definition presents the public interface of
3 // the class. Member-function definitions appear in GradeBook.cpp.
4 #include <string> // program uses C++ standard string class
5 using std::string;
6
7 // GradeBook class definition
8 class GradeBook
9 {
10 public:
11 GradeBook( string ); // constructor that initializes a GradeBook object
12 void setCourseName( string ); // function that sets the course name
13 string getCourseName(); // function that gets the course name
14 void displayMessage(); // function that displays a welcome message
15 private:
16 string courseName; // course name for this GradeBook
17 }; // end class GradeBookكود 9
1 // Fig. 3.16: GradeBook.cpp
2 // Implementations of the GradeBook member-function definitions.
3 // The setCourseName function performs validation.
4 #include <iostream>
5 using std::cout;
6 using std::endl;
7
8 #include "GradeBook.h" // include definition of class GradeBook
9
10 // constructor initializes courseName with string supplied as argument
11 GradeBook::GradeBook( string name )
12 {
13 setCourseName( name ); // validate and store courseName
14 } // end GradeBook constructor
15
16 // function that sets the course name;
17 // ensures that the course name has at most 25 characters
18 void GradeBook::setCourseName( string name )
19 {
20 if ( name.length() <= 25 ) // if name has 25 or fewer characters
21 courseName = name; // store the course name in the object
22
23 if ( name.length() > 25 ) // if name has more than 25 characters
24 {
25 // set courseName to first 25 characters of parameter name
26 courseName = name.substr( 0, 25 ); // start at 0, length of 25
27
28 cout << "Name "" << name << "" exceeds maximum length (25).n"
29 << "Limiting courseName to first 25 characters.n" << endl;
30 } // end if
31 } // end function setCourseName
32
33 // function to get the course name
34 string GradeBook::getCourseName()
35 {
36 return courseName; // return object's courseName
37 } // end function getCourseName
38
39 // display a welcome message to the GradeBook user
40 void GradeBook::displayMessage()
41 {
42 // call getCourseName to get the courseName
43 cout << "Welcome to the grade book forn" << getCourseName()
44 << "!" << endl;
45 } // end function displayMessageكود 10
1 // Fig. 3.17: fig03_17.cpp
2 // Create and manipulate a GradeBook object; illustrate validation.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 #include "GradeBook.h" // include definition of class GradeBook
8
9 // function main begins program execution
10 int main()
11 {
12 // create two GradeBook objects;
13 // initial course name of gradeBook1 is too long
14 GradeBook gradeBook1( "CS101 Introduction to Programming in C++" );
15 GradeBook gradeBook2( "CS102 C++ Data Structures" );
16
17 // display each GradeBook's courseName
18 cout << "gradeBook1's initial course name is: "
19 << gradeBook1.getCourseName()
20 << "ngradeBook2's initial course name is: "
21 << gradeBook2.getCourseName() << endl;
22
23 // modify myGradeBook's courseName (with a valid-length string)
24 gradeBook1.setCourseName( "CS101 C++ Programming" );
25
26 // display each GradeBook's courseName
27 cout << "ngradeBook1's course name is: "
28 << gradeBook1.getCourseName()
29 << "ngradeBook2's course name is: "
30 << gradeBook2.getCourseName() << endl;
31 return 0; // indicate successful termination
32 } // end mainعملية التدقيق تتم في ملف التطبيق ( الكود 9 ) تحديدا في دالة العضو setCourseName:
في السطر 20 عبارة الـ if تحتوي على التعبير التالي name.length() ( قلنا فيما سبق ان جميع المتغيرات المعرفه على انها سلاسل
رموز " string " تعتبر كائنات معرفة ضمن مكتبة الـ C++ القياسية ) نحن حددنا فيما سبق أن الوسيط name من نوع سلسلة
رمزيه., لذلك , تكون name عبارة عن كائن ضمن مكتبة الـ C++ القياسية تحتوي على دالة أعضاء اسمها length لتحديد طول السلسلة
الرموزية ( أي عدد الرموز ).
في السطر 23 , عبارة If اخرى تحتوي على نفس الدالة السابقة , في حال تحقق هذا الشرط ( وهوي أن السلسلة اطول من 25 ) تقوم الدالة في
السطر 24 name.substr(0 25 ) بإرجاع كائن string جديد بواسطة نسخ جز من السلسلة name يبدأ من
الموضع 0 إلى 25 وإسنادة لعضو البيانات courseName.
إختبار الصنف GradeBook في الكود 10:
في كود 10 إختبار للصنف بعد إجراء التدقيق في الدالة setCourseName.
في السطر 14, قمنا بإنشاء كائن إسمه gradeBook1 وعن طريق هذا الإنشاء قمنا بإستدعاء الباني الذي يقوم ايضا بإستدعاء
الدالة setCoursName التي تقوم بعلمية التدقيق التي قمنا بشرحها سابقا.
فكرة جيدة في هندسة البرمجيات :
عملية التدقيق تدل على دقة البرنامج وصحتة , لذلك ينصح بهذه العمليات عند إعداد دوال الأعضاء للتحقق من أن البرنامج يعمل بكفاءة.
اسئلة التمارين ( اسئلة الواجب وأسئلة التحدي ) في المرفقات.
من لديه سؤال لا يتردد في طرحة , في أي وقت كان , دون أن يخرج عن موضوع المحاضرة
أكررد , في أي وقت أو تاريخ .
المحاضرة القادمة سوف تكون يوم الجمعة القادمة ان شاء الله



