Professional Documents
Culture Documents
- - كتاب-برمجة1 (1) -
- - كتاب-برمجة1 (1) -
- - كتاب-برمجة1 (1) -
والعلوم التطبيقية
إعـ ـ ـ ــداد
د .خميس ُعمر د .خليل الهندي
د .يوسف أبو زر د .عماد نزال
د .نبيل عرفان
د .عمــــــــاد نزال -د .يو�سف �أبو زر إعداد المادة العلمية
د .نبيل عرمان
د .حممد حممود ذويب ا لتحـــــــــكـيم
د .خليل خليفة التحـــرير اللغــوي فريق
�أ .فريهان بركات التصميم التعــليمي التطوير
�أ .حممد ج .الطيطي التصمــيم الفـــني
�أ .فريهان بركات التدقيق الطـــباعي
�أ .عي�سى الهندي � -أ .عالية �أبو رمان التنضيد الطـــباعي
�أ .ن�سيم �أحمد احلو�ساين المراجع�ة النهائي�ة
2010
حقوق النشر والطبع محفوظة جلامعة القدس املفتوحة
محتوى املقرر:
ويتألف هذا املقرر من ست وحدات دراسية على النحو التالي:
الوحدة األولـى :تطوير البرامج الكينونية ،تهدف هذه الوحدة إلى تبيان املفاهيم املستخدمة
في البرمجة الكينونية والتحليل الكينوني والتصميم الكينوني.
الوحدة الثانيـة :التراكيب واألصناف ،تعرض هذه الوحدة أهم أنواع البيانات املركبة .وتركز
بشكل خاص على األصناف والكائنات والدوال املنتمية .وهي بذلك أولى
الوحدات التي تناقش املفاهيم األساسية للغة .C ++
الوحدة الثالثـة :املصفوفات واألصناف ،تشرح هذه الوحدة كيفية التعامل مع قائمة من
الكائنات بطرق مختلفة.
الوحدة الرابعة :الدوال الصديقة والعمليات ،تناقش كيفية تعريف الدوال الصديقة والعمليات
وكيفية استخدامها.
الوحدة اخلامسة :األصناف املشتقة والقوالب ،تناقش هذه الوحدة الطرق املختلفة إلعادة
استخدام البرامج عن طريق اشتقاق األصناف والوراثة والدوال واألصناف
القالبية.
تتطرق هذه الوحدة إلى تعريف هيكل البياناتالوحدة السادسة :معاجلة امللفات بلغة ّ ،C ++
وموقع امللفات بينها والطرق املختلفة في تنظيم امللفات والينابيع وكيفية
إنشاء امللفات التتابعية وامللفات العشوائية وقراءتها وحتديثها وأيض ًا إدخال
الكائنات وإخراجها.
ب
محتويات المقرر
الصفحة عنوان الوحدة رقم الوحدة
ج
د
الوحدة األولى
تطوير البرامج الكينونية
Object-Oriented Programs Development
2
تطوير البرامج الكينونية الوحدة األولى
محتويات الوحدة
الصفحة امل ــوض ـ ـ ـ ــوع
5 .1املقدمة
5 1.1متهيد
5 2.1أهداف الوحدة
6 3.1أقسام الوحدة
6 4.1القراءات املساعدة
6 5.1ما حتتاج إليه لدراسة الوحدة
7 .2املفاهيم األساسية لتطوير البرامج الكينونية
10 .3التحليل الكينوني ()Object-Oriented Analysis
10 1.3حتديد الكينونات
10 2.3حتديد خصائص الكينونات
11 3.3حتديد العالقات بني الكينونات
13 4.3جتميع الكينونات
14 .4التصميم الكينوني ()Object-Oriented Design
14 1.4بناء العمليات
14 2.4اختيار لغة البرمجة الكينونية
16 .5البرمجة الكينونية ()Object-Oriented Programming
16 1.5خصائص البرامج اجليدة
17 2.5اخلصائص األساسية في لغات البرمجة الكينونية
18 .6اخلالصة
18 .7حملة عن الوحدة الدراسية الثانية
19 .8إجابات التدريبات
19 .9مسرد املصطلحات
20 .10املراجع
3
تطوير البرامج الكينونية الوحدة األولى
4
تطوير البرامج الكينونية الوحدة األولى
.1املقدمة
1.1متهيد
ال بك إلى الوحدة األولى من مقرر برمجة ( )1وهي بعنوان عزيزي الدارس ،أه ً
تطوير البرامج الكينونية.
تهدف ه��ذه الوحدة إل��ى إعطائك معلومات أساسية فيما يتعلق بتطوير البرامج
الكينونية وخاصة التحليل والتصميم الكينوني وتوضيح بعض املفاهيم واملصطلحات
األساسية اخلاصة بذلك.
تتناول هذه الوحدة تعريف املفاهيم األساسية اخلاصة بتطوير البرامج الكينونية إضافة
إلى تعريف مفهوم التحليل الكينوني من حيث حتديد الكينونات وخصائصها والعالقات
بينها .ويتلو ذل��ك مناقشة التصميم الكينوني م��ن حيث بناء العمليات واختيار لغة
البرمجة الكينونية املناسبة .وأخيرا توضيح املفاهيم األساسية املرتبطة بالبرمجة الكينونية
وخصائص البرمجة اجليدة إضافة إلى اخلصائص األساسية للغات البرمجة الكينونية.
وقد زودت هذه الوحدة بالعديد من األمثلة التوضيحية ،والتدريبات ،وأسئلة
التقومي الذاتي إضافة إل��ى بعض الرسومات التي توضح بعض املفاهيم ،وستجد،
ال للتدريبات التي تصادفها في نهاية الوحدة.عزيزي الدارس ،حلو ً
مرة أخرى ،نرحب بك ،آملني أن تستمتع بدراستك للمادة التي نعرضها لك في
هذه الوحدة.
2.1أهداف الوحدة
ينتظر منك ،عزيزي الدارس ،بعد فراغك من دراسة هذه الوحدة ،أن تكون
قادر ًا على أن:
ّ .1
تعرف املفاهيم املستخدمة في البرمجة الكينونية (.)Object-Oriented Programming
ّ .2
تعرف املفاهيم املستخدمة في التحليل الكينوني (.)Object-Oriented Analysis
ّ .3
تعرف املفاهيم املستخدمة في التصميم الكينوني (.)Object-Oriented Design
ّ .4
تطبق املفاهيم املستخدمة في البرمجة الكينونية (.)Object-Oriented Programming
5
تطوير البرامج الكينونية الوحدة األولى
3.1أقسام الوحدة
تتكون هذه الوحدة من أربعة أقسام رئيسة .فالقسم األول يبحث في املفاهيم
املستخدمة في البرمجة الكينونية وبذلك يتحقق الهدف األول .أما القسم الثاني
فيوضح املفاهيم املستخدمة في التحليل الكينوني بذلك يتحقق الهدف الثاني.
أما القسم الثالث فيشرح تعريف املفاهيم املستخدمة في التصميم الكينوني وبذلك
يتحقق الهدف الثالث .وأخيرا فإن القسم الرابع يوضح املفاهيم املستخدمة في
البرمجة الكينونية وبذلك يتحقق الهدف الرابع.
4.1القراءات املساعدة
لقد قدمنا الكثير من املفاهيم واملواضيع األساسية املرتبطة بتطوير البرامج
الكينونية في هذه الوحدة .ولذلك من املفيد ج��دا ،عزيزي ال��دارس ،أن تعود
إلى املصادر التالية ملا تتضمنه من مادة علمية إضافية وأمثلة وإيضاحات ستساعدك
على استيعاب املادة التي تضمنتها هذه الوحدة ،وهذه املصادر هي:
1. Adams, J. and Nyhoff, L., “C++ An Introduction to Comput-
ing,” Prentice Hall, 3rd Edition, 2003.
2. Deitel, H. and Deitel, P., “C++ How to Program,” ISBN: 0-13-
185757-6, Deitel & Associates, 5th Edition, 2006.
3. Skansholm, J., “C++ From the Beginning,” Addison Wesley, 2nd
Edition, 2002.
4. Stoustrup, B., “The C++ Programming Language,” Addison
Wesley, 2nd Edition, 1993.
6
تطوير البرامج الكينونية الوحدة األولى
.2املفاهيم األساسية لتطوير البرامج الكينونية
يعد مصطلح “الكينونية” من املصطلحات املهمة في عالم احلاسوب هذه األيام ،فأنت، ّ
عزيزي الدارس ،تسمع مصطلحات كثيرة لها عالقة بهذا املصطلح مثل التحليل الكينوني،
التصميم الكينوني ،البرمجة الكينونية وغير ذلك من املصطلحات ذات الصلة .ولعلك تتساءل
عن املعنى الدقيق لهذا املصطلح الذي أصبح يتردد بصورة كبيرة بني الدارسني لتخصصات
احلاسوب إضافة إلى العاملني في مجاالت احلاسوب املختلفة ،وحقيقة األمر أنه ال يوجد
تعريف أو معنى دقيق ومطلق ملصطلح “الكينونية” ولكننا نستطيع القول أنه طريقة أو أسلوب
جديد في تطوير البرامج واألنظمة احلاسوبية من خالل التركيز على الكينونات/الكائنات
ال من التركيز على الوظائف الرئيسة التي يجب على هذه األنظمة إجنازها. الرئيسة فيها بد ً
في هذه الوحدة ،سنعرض ،عزيزي الدارس ،املفاهيم األساسية املتعلقة بالتحليل الكينوني
باعتبارها خطوة أساسية في أي عملية تطوير للبرامج الكينونية من خالل حتديد الكينونات
ثم ،سنعرض املفاهيم واألفكار الرئيسة املتعلقة بالتصميم وخصائصها والعالقات بينها ومن ّ
الكينوني من خالل بناء العمليات الرئيسة املرتبطة بالكينونات املختلفة إضافة إلى اختيار لغة البرمجة
الكينونية املناسبة ،وأخيراً ،سنعرض املفاهيم األساسية املتعلقة بالبرمجة الكينونية من خالل التركيز
على خصائص البرامج الكينونية اجليدة واخلصائص األساسية في لغات البرمجة الكينونية.
وكما أشرنا ،عزيزي الدارس ،فإن التطوير الكينوني هو طريقة أو أسلوب جديد في تطوير
ال من التركيز علىالبرامج واألنظمة احلاسوبية من خالل التركيز على الكينونات الرئيسة فيها بد ً
الوظائف الرئيسة التي يجب على هذه األنظمة إجنازها ،ولكن ما هو الكائن/الكينونة؟
إن مفهوم الكائن ( )Objectمرتبط مبجموعة من اخلصائص التي حتدد من خالل مجموعة
من املتغيرات التي تصف حالته ومجموعة من العمليات التي يستطيع إجنازها .إن كل كائن
( )Objectله حالة ميكن أن تتغير أثناء تنفيذ البرنامج .ولكن من أين نحصل على الكينونات/
تعد األصناف ( )Classesمصنع ًا للكائنات ،حيث تستخدم إلنشاء الكائنات التي الكائنات؟ ّ
حتتوي على متغيرات ودوال منتمية .وستتعرف ،عزيزي الدارس ،على كيفية إنشاء الكائنات
والتعامل معها في الوحدات القادمة من هذا املقرر.
تعد البرمجة الكينونية أسلوب ًا جديد ًا في البرمجة ،حيث توفر إمكانيات متعددة مثل التنظيم
تعد أسلوب ًا جديد ًا في التفكير
اجليد ،واالستخدام املتعدد ،واملقاطع البرمجية املتكاملة :كما ّ
وفي بناء البرامج؛ حيث جتمع البرامج حقول البيانات مع البرامج الفرعية التي تستند في عملها
7
تطوير البرامج الكينونية الوحدة األولى
على تلك احلقول في بنية واحدة تسمى كائن ًا ( )Objectويتم التعامل مع الكائن ()Object
وكأنه وحدة واحدة.
ومن املناسب أن يكون هدفنا عند استخدام الكينونات أن نبني نظام ًا أو منوذج ًا يحاكي
املسألة قيد الدراسة كما هو في الواقع ،ولتقريب ذلك إلى األذهان ،ميكن اعتبار احلاسوب
منوذجاً ،ومن ثم ،فإن هذا النموذج ميتلك كينونات تشكل جزءا من تركيبته ،وكل كينون له
مهمة محددة ويستخدم أدواته اخلاصة به وله روابط مع الكينونات األخرى ،فعلى سبيل املثال،
تعد لوحة املفاتيح والفأرة ووحدة املعاجلة املركزية والقرص الصلب كينونات تشكل جزء ًا من ّ
تركيبة احلاسوب ولكل منها مهمة محددة.
مثال ()1
اكتب برنامج ًا حلساب صافي رواتب عدد من املوظفني في دائرة معينة ،وإمكانية معرفة
صافي راتب أي موظف عند اللزوم.
8
تطوير البرامج الكينونية الوحدة األولى
ومن اجلدير بالذكر أن البرمجة الكينونية تهدف إلى إضافة ميزات التعميم عند تصميم
الكينون األمر الذي يتطلب حتديد مجموعة من العمليات على الكينون وتعريفها وهي التي
تساعد املستخدم على تنفيذ مهامه .ومن العمليات في الكينون «موظف» ،نذكر ما يلي:
-1بناء/إنشاء الكينون وحتديد املعطيات األولية.
-2حتديد اسم املوظف وإسناده.
-3تعيني البيانات اخلاصة باملوظف.
-4حساب صافي الراتب.
-5إظهار النتائج.
-6إنهاء وجود الكينون.
وبالطريقة نفسها حتدد العمليات املطلوبة للكينون “الدائرة” .كما أنه من اجلدير
بالذكر أن املهام التي يقوم بها كل كينيون تشكل اخلدمات التي يقدمها الكينون للكينونات
األخرى ،فإذا مت استدعاء عملية من خالل كينون معني ،فإنه يطلب منه تأدية مهمة تفيد
كينون ًا آخر.
9
تطوير البرامج الكينونية الوحدة األولى
Object-Oriented Analysis .3التحليل الكينوني
إن الهدف من التحليل الكينوني هو أن تصبح ،عزيزي الدارس ،على معرفة أكبر
مبتطلبات البرنامج أو النظام والبدء في عملية بناء لنموذج أولي للبرنامج من خالل التركيز
على الكائنات األساسية ،حيث ميكن تلخيص هذه العملية مبا يلي:
-حتديد الكائنات في هذا النموذج األولي.
-وصف خصائص الكائنات.
-ربط الكائنات املختلفة مع بعضها.
-جتميع الكائنات املختلفة.
1.3حتديد الكينونات
قد تبدو عملية حتديد الكائنات األساسية معقدة في البداية ،إال أن التفكير في البرنامج
الكينوني والنظر إليه باعتباره منوذج ًا لتمثيل الواقع يجعل هذه العملية سهلة نسبي ًا وذلك
من خالل جعل كل شيء له خصائص معينة ميكن متثيلها من خالل بيانات معينة في هذا
الواقع كائناً ،كما أن هناك طريقة أخرى لتحديد الكائنات وذلك من خالل التركيز على
األسماء األساسية املستخدمة في وصف وظائف البرنامج أو النظام والنظر إليها باعتبارها
كائنات محتملة في البرنامج الكينوني.
مثال ()2
لتمثيل الصنف “العربة” التي لها اخلصائص “السرعة ،اللون ،الوزن” والعملية
“تغيير السرعة” ،نستخدم الرمز اخلاص بذلك ونقوم بتقسيمه حسب ما مت توضيحه
سابق ًا كما يلي:
العربة
السرعة
اللون
الوزن
تغيير السرعة
الشكل ()1
مكون من
ٌ أما العالقة الثانية فهي عالقة ميلك ( ،)hasالتي تصف حقيقة أن كائن ًا معين ًا
أجزاء أخرى حيث إن كل جزء ميثل كائن ًا آخر .فعلى سبيل املثال ،يتكون جهاز احلاسوب
من شاشة ،لوحة مفاتيح ،فأرة... ،إلخ ويوضح الشكل ( )2عالقة ميلك ( )hasبني
جهاز حلاسوب وأجزائه املختلفة.
الشكل ()2
أما العالقة الثالثة في عملية تطوير البرامج الكينونية فهي عالقة يكون ( )isالتي
تصف حقيقة أن كائن ًا معين ًا يكون له خصائص عامة يشترك بها مع أصناف أخرى حيث
يتم وصف اخلصائص العامة من خالل صنف رئيس ،فعلى سبيل املثال ،فإن السيارات،
والدراجات والقطارات لها خصائص مشتركة مثل السرعة والوزن حيث ميكن تعميمها
في صنف رئيس يسمى العربة كما هو موضح في الشكل (.)3
احلاسوب
الشكل ()3
12
تطوير البرامج الكينونية الوحدة األولى
وهناك بعض اخلصائص احمللية التي تكون خاصة باألصناف الفرعية وال تنطبق على
الصنف الرئيس مثل قوة احملرك للسيارة وعدد العربات للقطار ،وهل هناك غيار أم ال
للدراجة .وباستخدام هذه العالقة فإنه ميكن القول أن السيارة عربة وأن الدراجة عربة وكذلك
القطار فإنه عربة .متثل هذه العالقة خاصية أساسية في البرمجة الكينونية وهي مفهوم الوراثة
( )Inheritanceالتي متكننا من استخدام كائنات عرفت سابق ًا وتعديلها واإلضافة عليها مبا
يتناسب مع متطلبات برامج جديدة.
وهناك مالحظة بخصوص الرموز املستخدمة في وصف العالقات املختلفة؛ إذ قمنا
باستخدام الرموز املستخدمة في لغة بناء النماذج املوحدة املسماة Unified Modeling
Language) UMLالتي تعد لغة شبه قياسية في بناء النماذج اخلاصة بتطوير البرامج واألنظمة
الكينونية.
4.3جتميع الكينونات
إن البرامج الكبيرة نسبيا تشتمل عادة على عدد كبير نسبيا من األصناف ،ولذلك فإنه من
املفيد جد ًا جتميع األصناف املترابطة مع بعضها في مجموعة من الرزم ( )Packagesوذلك
لتسهيل عملية التعامل مع البرنامج ولتنظيم األصناف املختلفة مبا يكفل تسهيل عملية التعديل
والصيانة لهذه األصناف.
تدريب ()1
إذا علمت أن السيارة مكونة من محرك ،وعجالت ،وجسم ،فمثل ذلك
باستخدام العالقة املناسبة.
تدريب ()2
إذا علمت أن األشخاص املوجودين في جامعة القدس املفتوحة ينتمون إلى عدة فئات؛
فمنهم اإلداريون ،واألكادمييون والدارسون ،فمثل ذلك باستخدام العالقة املناسبة.
1.4بناء العمليات
ففي هذه املرحلة ،يتم البدء بالكائنات التي حددت في مرحلة التحليل الكينوني وتتم
إضافة التفصيالت الضرورية التي قد تشمل تطبيق مجموعة من اخلوارزميات املالئمة
واتخاذ القرارات اخلاصة بكيفية إجناز العمليات املختلفة واملعلمات ( )Parametersالتي
حتتاجها ،إضافة إلى اختيار الطريقة األنسب للتمثيل الداخلي لهذه الكائنات.
فعلى سبيل املثال ،إذا كنا بصدد بناء برنامج للتعامل مع األشكال الهندسية املختلفة
مثل املستطيل ،املربع ،الدائرة ،املثلث ،فإنه من الواضح أن املستطيل هو أحد األصناف
األساسية هنا .إن هناك مجموعة من اخلصائص التي ميكن حتديدها للمستطيل مثل
الطول والعرض حيث يتم ذلك في العادة أثناء مرحلة التحليل .أما في مرحلة التصميم
فإننا نحتاج إلى حتديد العمليات األساسية على هذا املستطيل مثل عملية حساب مساحة
املستطيل حيث نقوم بتحديد كيفية حساب املساحة من خالل عملية ضرب طول املستطيل
بعرضه .من الواضح أن هذه العملية حتتاج ،بصورة عامة ،إلى معلمتني أساسيتني وهما
طول املستطيل وعرضه ،وسوف تقوم بإعادة قيمة املساحة.
14
تطوير البرامج الكينونية الوحدة األولى
واجلدير ذكره أنه يفضل أن تتم عملية تنفيذ التصميم الكينوني بلغة برمجة كينونية
يعد شرط ًا أساسياً؛ إذ ميكن أن تتم عملية التنفيذ بلغة غير كينونية وذلك
إال أن ذلك ال ّ
يعد أمر ًا
من خالل التعامل مع األصناف وكأنها تراكيب بيانات جتريدية ،إال أن ذلك ال ّ
محبذ ًا ألننا سنفقد اخلصائص واحلسنات األساسية املرتبطة بتطوير البرامج الكينونية
وخاصة الفائدة من إعادة استخدام بعض األصناف والوراثة وإخفاء املعلومات وغيرها
من امليزات اجليدة لتطوير البرامج الكينونية.
15
تطوير البرامج الكينونية الوحدة األولى
.5البرمجة الكينونية Object-Oriented Programming
إن الهدف من مرحلة البرمجة في تطوير البرامج واألنظمة ،بصورة عامة ،هو تنفيذ
النظام والوصول إلى برامج قابلة للتنفيذ ،إال أن كون البرنامج قاب ً
ال للتنفيذ ال يعني
بالضرورة أنه برنامج جيد باملقاييس العامة كما أن هناك مجموعة من اخلصائص اجليدة
للغات البرمجة الكينونية وهو ما سنقوم بتوضيحه في األقسام التالية من هذه الوحدة.
وتعد اخلصائص السابقة من اخلصائص األساسية للبرامج اجليدة ،بصورة عامة ،إال أن ّ
هناك مجموعة أخرى من اخلصائص املهمة التي يجب توافرها في البرامج ،بصورة عامة،
مثل أن يكون برنامج ًا مرن ًا وقاب ً
ال للتكيف مع الظروف والبيئات التي سيعمل بها وأن تكون
تكلفة صيانته منخفضة.
16
تطوير البرامج الكينونية الوحدة األولى
2.5اخلصائص األساسية في لغات البرمجة الكينونية
إن من اخلصائص األساسية ،عزيزي الدارس ،للغات البرمجة الكينونية هو توافر
املكونات الرئيسة التي متكننا من حتقيق فائدتني أساسيتني في البرمجة الكينونية وهما:
.1خاصية إخفاء املعلومات ()Information Hiding
ان املقصود بهذه اخلاصية في لغة البرمجة الكينونية أن حتتوي هذه اللغة على تركيب معني
ميكننا من تعريف خصائص الكائن ووضعه ،من متغيرات ودوال منتمية ،في مكان واحد في
البرنامج وبحيث ال تتمكن الكائنات األخرى من الوصول إلى هذه اخلصائص أو رؤيتها.
17
تطوير البرامج الكينونية الوحدة األولى
.6اخلالصة
مت في هذه الوحدة إعطاؤك معلومات أساسية فيما يتعلق بتطوير البرامج
الكينونية ،وخاصة التحليل ،والتصميم الكينوني ،وتوضيح بعض املفاهيم
واملصطلحات األساسية اخلاصة بذلك .لقد تناولت هذه الوحدة تعريف املفاهيم
األساسية اخلاصة بتطوير البرامج الكينونية إضافة إلى تعريف مفهوم التحليل
الكينوني من حيث ،حتديد الكينونات وخصائصها والعالقات بينها .وتبع ذلك
مناقشة التصميم الكينوني من حيث ،بناء العمليات واختيار لغة البرمجة الكينونية
املناسبة .وأخيرا توضيح املفاهيم األساسية املرتبطة بالبرمجة الكينونية وخصائص
البرمجة اجليدة إضافة إلى اخلصائص األساسية للغات البرمجة الكينونية.
18
تطوير البرامج الكينونية الوحدة األولى
.8إجابات التدريبات
تدريب ()1
السيارة
تدريب ()2
الشخص
.9مسرد املصطلحات
البرمجة الكينونية :Object-Oriented Programmingهي البرمجة التي تستخدم -
الكينون أساس ًا لبناء البرامج حيث تتعامل الكينونات مع بعضها لتحقيق أهداف البرمجة.
التحليل الكينوني :Object-Oriented Analysisهو التحليل الذي يستند إلى -
أساسيات البرمجة الكينونية ويستخدم الكينون أساس ًا لتحليل النظام.
التصميم الكينوني :Object-Oriented Designهو التصميم الذي يستند إلى -
أساسيات البرمجة الكينونية ويستخدم الكينون أساس ًا لتصميم النظام.
19
تطوير البرامج الكينونية الوحدة األولى
. مجموعة من األصناف املترابطة مع بعضها:Package الرزمة -
، يستخدم لوصف مجموعة كينونات لها السمات أو املالمح نفسها:Class الصنف -
.وتقوم بعمليات متشابهة
.جزء من البرنامج يضم البيانات والعمليات في قالب واحد:Object الكائن -
برامج فرعية ميكنها التعامل مع عدة:Generic Subprograms برامج فرعية عامة -
.أمناط من البيانات
عملية وضع قيود على الوصول:Information Hiding خاصية إخفاء املعلومات -
. إال باألساليب والصالحيات احملددة،إلى املعلومات
استخدام السمات واملالمح نفسها املوجودة لدى: Inheritance خاصية الوراثة -
حيث ميكن أن يرث كينون ما بعض سماته وخصائصه من،كينون بوساطة كينون آخر
.كينون آخر
املراجع.10
1. Adams, J. and Nyhoff, L., “C++ An Introduction to Computing”,
Prentice Hall, 3rd Edition, 2003.
2. Deitel, H. and Deitel, P., “C++ How to Program”, ISBN: 0-13-
185757-6, Deitel & Associates, 5th Edition, 2006.
3. Keogh, J., “Object Oriented Programming: Principles and Fun-
damentals”, Dreamtech Press, 2004.
4. Oestereich, B., “Developing Software with UML: Object-Oriented
in Design and Practice”, Harlow: Addison-Wesley.
5. Skansholm, J., “C++ From the Beginning”, Addison Wesley, 2nd
Edition, 2002.
6. Stoustrup, B., “The C++ Programming Language”, Addison Wes-
ley, 2nd Edition, 1993.
7. Wirfs-Brock, R., Wilkerson, B., Wiener, L., “Designing Object-Ori-
ented Software”, Prentice Hall, Inc.
20
تطوير البرامج الكينونية الوحدة األولى
الوحدة الثانية
التراكيب واألصناف
Structures and Classes
22
التراكيب واألصناف الوحدة الثانية
محتويات الوحدة
الصفحة امل ــوض ـ ـ ـ ــوع
25 .1املقدمة
25 1.1متهيد
25 2.1أهداف الوحدة
26 3.1أقسام الوحدة
26 4.1القراءات املساعدة
26 5.1ما حتتاج إليه لدراسة الوحدة
27 .2التراكيب Structures
32 .3األصناف والكائنات Classes & Objects
32 1.3تعريف الصنف
35 2.3إنشاء الكائنات من الصنف
38 3.3اإلعالن عن حقول الصنف ومحددات الوصول لها Access Modefiers
40 .4دوال األصناف
40 1.4الدوال املنتمية () Member Function
49 2.4إعادة التحميل للدوال Function Overloading
52 3.4العوامل التلقائية Default Arguments
54 4.4الدوال املنتمية الثابتة والكائنات الثابتة
const member functions & const objects
56 .5اإلدخال واإلخراج في لغة C ++
56 1.5اإلدخال Input
58 2.5اإلخراج Output
59 .6اخلالصة
60 .7حملة عن الوحدة الدراسية الثالثة
60 .8إجابات التدريبات
66 .9مسرد املصطلحات
67 .10املراجع
23
التراكيب واألصناف الوحدة الثانية
24
التراكيب واألصناف الوحدة الثانية
.1املقدمة
1.1متهيد
ف��ي ه��ذه ال��وح��دة ،ع��زي��زي ال���دارس ،سنعرض بالتفصيل ملفهومني مهمني وهما
التراكيب واألصناف .تقابل التراكيب Structuresالسجالت Recordsفي لغات
برمجة أخ���رى .أم��ا األص��ن��اف Classesفهي الفكرة الرئيسة ف��ي ل��غ��ات البرمجة
الكينونية Object Oriented Languagesوهي جزء من لغة C ++وليست جزء ًا
من لغة Cاألصلية .كما سنناقش مفاهيم رئيسة أخرى في لغات البرمجة الكينونية،
مثل الكائنات واملتغيرات والدوال املنتمية وكيفية تعريفها واستخدامها في لغة .C ++
وتعد هذه املفاهيم أساسية لفهم بقية الوحدات في هذا املقرر ،لذا نرجو منك ،عزيزيّ
الدارس ،التأكد من فهم هذه الوحدة قبل االنتقال إلى بقية الوحدات .وقد مت تزويد
هذه الوحدة بالعديد من األمثلة والتمارين التي نرجو منك إعطاءها الوقت واجلهد
الالزمني حللها .تذكر ،عزيزي الدارس ،أن إجابات هذه التمارين موجودة في نهاية
هذه الوحدة ،ولكن ،وحتى تتحقق الفائدة القصوى ،عليك ،عزيزي الدارس ،أن
حتاول بكل جد حل هذه التمارين قبل النظر إلى احلل .كذلك نرجو منك القيام بجميع
النشاطات الواردة في هذه الوحدة.
2.1أهداف الوحدة
ينتظر منك ،عزيزي الدارس ،بعد فراغك من دراسة هذه الوحدة أن تكون قادرا
على أن:
تعرف وتستخدم التراكيب .structures
ّ .1
.2تستخدم مصفوفات من التراكيب .arrays of structures
.3تعرف وتستخدم األصناف .classes
.4متيز بني التراكيب واألصناف.
.5تنشئ الكائنات وتستخدمها بطريقة صحيحة.
.6تكتب الدوال املنتمية وتستدعيها بطريقة صحيحة.
.7تكتب الدوال السطرية.
.8حتمل الدوال أكثر من تعريف واحد.
.9تدخل البيانات وتخرجها بوساطة cinو .cout
25
التراكيب واألصناف الوحدة الثانية
3.1أقسام الوحدة
تتكون هذه الوحدة من أربعة أقسام رئيسة .يبحث القسم األول في موضوع التراكيب
واملصفوفات وهو بذلك يحقق الهدفني األول والثاني .أما القسم الثاني فيبحث في
موضوع األصناف والكائنات وهو بذلك يحقق األهداف :الثالث والرابع واخلامس.
أما القسم الثالث فيبحث في كيفية تعريف الدوال املنتمية وكيفية استدعائها ويحقق هذا
القسم الهدف السادس ،كما يبحث كيفية حتميل الدوال أكثر من تعريف واحد وكيفية
كتابة الدوال السطرية وهذا يحقق الهدفني السابع والثامن .أما القسم الرابع فيبحث في
عمليات اإلدخال واإلخراج في لغة C ++ويحقق هذا القسم الهدف التاسع.
4.1القراءات املساعدة
لقد حاولنا تقدمي الكثير من املفاهيم املهمة في هذه الوحدة بشكل مبسط .ولكن
كي تتعلم لغة C ++بشكل جيد جدا ستجد أنه من املفيد أن تدرس أكبر عدد ممكن
من األمثلة والتمارين .لذا ننصحك بالرجوع إلى أكبر عدد ممكن من كتب لغة .C ++
وفيما يلي جتد قائمة من هذه الكتب:
1. Cannon, Scott, “Understanding Programming: An Introduction
Using C++”. Brooks/Cole Publishing Co, 2001.
2. Nell Dale, Chip Weems, “Programming and Problem Solving
with C++”, Fourth Edition. Jones and Bartlett Publishers, 2004.
3. Savitch, Walter, “Problem Solving with C++”, 7/E Addison-
Wesley Publishing Company, Inc 2008.
26
التراكيب واألصناف الوحدة الثانية
Structures .2التراكيب
عزيزي الدارس ،ال بد ،وأنك عند دراستك للمتطلب السابق لهذا املقرر ،لو
حاولت أن تستذكر كثير ًا من األمثلة التي تصادفك يومي ًا لوجدت لدى زيارتك ملؤسسة
حكومية ،خدماتية ،طبية أو تعليمية...إلخ ،أنها حتتفظ بقيود خاصة مبراجعيها وذلك
خلدمة األغراض اخلاصة باملؤسسة ،حيث إنها تشترك جميع ًا بخاصية أساسية وهي أن
هذه القيود مصممة بطريقة تتيح ملن يستخدمها الرجوع لها واإلفادة منها؛ فاملعلومات
املتصلة بكل حالة من احلاالت الفردية التي متثلها تصف هذه احلالة دون غيرها وتعبر عنها
تعبير ًا منهجي ًا محدداً.
عندما نتعامل مع هذه املمارسات العملية في نطاق لغات البرمجة فإننا نحتاج إلى
وسيلة مناسبة لالحتفاظ باملعلومات واحملافظة على تكاملها ومنهجيتها .والتركيب هو
وبناء عليه فإن التركيب :هو أسلوب منظمً أحد الوسائل التي توفرها لغة .C/C++
لالحتفاظ باملعلومات املتصلة بأحد األشياء .فاملقصود أن التركيب قد يتضمن عنصر ًا أو
أكثر من عناصر البيانات وهذا ما يكسبه سمة التركيب ،أي أن التركيب ينتمي إلى النمط
املركب ( )Structured Typeمن أمناط البيانات ،شأنه في ذلك شأن املصفوفات ،وهنا ال
بد أن نالحظ أن عناصر البيانات التي يتضمنها التركيب ال تنتمي إلى نوع واحد من أنواع
البيانات كما هو احلال مع املصفوفات .فسجل الزبون يتضمن اسمه ،رقمه ،عنوانه
،...وغير ذلك من املعلومات .وكما تالحظ ،فإن هذه املعلومات تختلف عن بعضها
في طبيعة تكوينها وأسلوب متثيلها ،وهذا على خالف ما هو قائم مع املصفوفات.
بناء عليه ،جند أن التركيب هو مجموعة أو حزمة من احلقول fieldsاملتعلقة ً
بكائن objectمعني يجمعها اسم تركيب محدد .وجند أن الشكل العام لإلعالن عن
التركيب في لغة C ++يأخذ الشكل التالي:
struct structure_name
;{type1 member_name1
;type2 member_name2
. .
. .
. …………….
;typeN member_nameN
;)} object_name (copyname
27
التراكيب واألصناف الوحدة الثانية
على سبيل املثال ،قد يحتوي التركيب اخلاص بطالب معني على احلقول التالية:
StName اسم الطالب
StNumber رقم الطالب
Department القسم
Grade العالمة
يشكل هذا التعريف نوع ًا جديد ًا هو التركيب Studentبحيث يحتوي على احلقول
السالف ذكرها .الحظ ،عزيزي الدارس ،أن تعريف التركيب يجب أن ينتهي بفاصلة
منقوطة كبقية اجلمل .كما أن هذا التعريف أنشأ نوع ًا جديد ًا يستخدم إلنشاء متغيرات من
نوع Student.ويعرف املتغير Xمن النوع Studentكما يلي:
;struct Student X
واجلملة
struct ;Student Z,Y
تعرف املتغيرين Zو Yمن النوع Studentولتمييز هذه املتغيرات عن بقية املتغيرات
البسيطة فإننا ندعوها باملتغيرات املركبة.
ومن املمكن دمج تعريف النوع وإنشاء املتغير املركب كما يلي:
struct Student
;]{char StName[21
;int StNumber
;]char Department[7
float ;Grade
; } X,Y,Z
28
التراكيب واألصناف الوحدة الثانية
وهكذا نكون قد عرفنا تركيبا من نوع Studentوأنشأنا املتغيرات املركبة Xو Yو Z
من ذلك النوع .Studentوملعاجلة أي حقل في املتغير املركب Xفإننا نستخدم اسم املتغير
متبوعا بنقطة ثم اسم احلقل .فمثال ،اجلملة:
;X.StNumber = 123
تخزن الرقم 123في احلقل StNumberاخلاص باملتغير Xواجلملة:
;”Z.StName=”Ali Ahmad
تخزن القيمة Ali Ahmadفي احلقل StNameاخلاص بالتركيبZ .
ومن املمكن في لغة ( C ++لكن ليس في لغة )Cتعريف متغير مركب من نوع
Studentبدون كتابة الكلمة structكما يلي
;Student X
بعد أن تعرفنا على كيفية تعريف التراكيب ،فإن املثال التالي يوضح كيفية التعامل
مع تركيب الوقت املسمى ،Timeانظر إلى التعريف:
{ struct Time
int ;hours
int ;minutes
int ;seconds
};
املثال التالي يعمل على تعريف دالة حلساب عدد الثواني للوقت املدخل ،انظر تعريف
الدالة:
)int toSeconds(Time now
;{return 3600*now.hours + 60*now.minutes + now.seconds
}
وكما ترى من التعريف ،فإن هناك معام ً
ال مركب ًا للدالة سمي nowوهو من
النوع . Timeأما في الدالة الرئيسة فأنشأ متغير ًا مركب ًا سمي tوهو من النوع Time
أيضاً ،وبعدها مت إدراج اجلمل املخصصة إلدخال الوقت من لوحة املفاتيح ،انظر إلى
البرنامج بشكله الكامل:
>#include <iostream
//=== define new types
29
التراكيب واألصناف الوحدة الثانية
struct Time {
int hours;
int minutes;
int seconds;
};
//==== prototypes
int toSeconds(Time now);
//==== main
int main()
{Time t;
while (cin >> t.hours >> t.minutes >> t.seconds) {
cout << «Total seconds: « << toSeconds(t) << endl;
}
return 0;
}
//=== toSeconds
int toSeconds(Time now)
{return 3600*now.hours + 60*now.minutes + now.seconds;
}
)1( تدريب
30
التراكيب واألصناف الوحدة الثانية
•التراكيب واملصفوفات
لتمثيل شعبة من الطلبة في مادة ما فإننا نستطيع استخدام مصفوفة من التراكيب ميثل
كل عنصر فيها طالباً .فمثالً ،اجلملة:
;]struct Student CS[40
تعرف املصفوفة CSبحجم 40عنصراً .وملعاجلة أي تركيب في هذه املصفوفة
فإننا نستخدم اسم املصفوفة ورقم التركيب (العنصر) داخل أقواس مربعة [ ] ك ــما في
]CS[5و ، ]CS[10وملعاجلة حقل معني فإننا نستخدم النقطة ( ) periodثم اسم احلقل
املطلوب ،فمثالً ،اجلملة ;CS[5].StNumber=123
تخزن في احلقل StNumberاخلاص بالطالب السادس القيمة .123
وكمثال على استخدام املصفوفة CSمن خالل دالة ،functionسنكتب الدالة
Averageالتي حتسب لنا معدل Nمن الطلبة في املصفوفة ،حيث تقوم بجمع عالمات
الطلبة ثم تعيد لنا حاصل قسمة املجموع على عدد الطلبة.
)float Average(Student* St, int N
;{ float Sum=0.0
;int i
)for(i=0; i<N;i++
;Sum+=St[i].Grade
;return Sum/N
}
وبالطبع ،تستدعى هذه الدالة بكتابة اسمها ،ثم بتحديد القيم احلقيقية للعوامل كما
في اجلملة ;)float Av=Average(CS,40
حيث متثل CSاسم مصفوفة الطلبة و 40عدد الطلبة في املصفوفة ،وبعد حساب
املعدل فإنه يخزن في املتغير احلقيقي .Av
نشاط ()1
قم ،عزيزي الدارس ،بكتابة برنامج متكامل لقراءة مصفوفة من التراكيب من
نوع studentواستدعاء الدالة Averageأعاله حلساب معدل عالماتهم.
31
التراكيب واألصناف الوحدة الثانية
Classes & Objects .3األصناف والكائنات
تعد األصناف جوهر لغات البرمجة الكينونية وأهم إضافة في لغة C ++على لغة C ّ
األصلية .ويختلف الصنف عن التراكيب بأنه ال يعرف فقط حزمة من احلقول ،بل يعرف
أيضا الدوال الالزمة ملعاجلة هذه احلقول ،بينما جند أن تشابه الصنف مع التراكيب في
أن كليهما نوع مستنتج (ينتجه املبرمج) وعناصرهما لها أنواع قد تكون مختلفة ،وعلى
عكس التراكيب ،فإن الصنف يحتوي على دوال وأن عناصره ميكن أن تكون دوال مبا في
ذلك املعامالت .ميكن القول أن الصنف هو مبثابة القالب (النموذج ،التصميم) الذي
على أساسه نستطيع استخراج أعضاء وكائنات تشترك في الصفات .فإن الصنف يعد
ال من إنشاء الصنفوصف ًا للكائن ،والكائن هو املثيل عن صنفه .وبناء على ذلك ال بد أو ً
ليتسنى إنشاء أي من الكائنات املثيلة للصنف ،حيث إن الكائن يعد مركبة متكاملة تخزن
بياناتها ولها دوالها ،وإن األداء للكائن يحدد دورة حياته وآلية تأديته للوظائف املنوطة
به بنفسه.
1.3تعريف الصنف
إن الشكل العام لتعريف الصنف في لغة C ++يأخذ الصيغة التالية:
{ class class_name
access_specifier_1:
;member1
access_specifier_2:
;member2
...
;} object_names
كما ذكرنا سابقاً ،يعد الصنف نوع ًا مستنتجاً ،وعليه ،فإن الصنف هو منط بياني
جديد غير معرف مسبق ًا في لغة ،C ++لذا يتوجب اإلعالم عن مكوناته وصفاته من
خالل تعريفه .ويتضح من صيغة التعريف أن التعريف يبدأ بالكلمة املفتاحة class
متبوعة باسم يختاره املبرمج بشكل مناسب وينتهي التعريف بالفاصلة املنقوطة ،أما
أسماء الكائنات املثيلة للصنف املزمع إنشاؤها والتي ترد قبل نهاية التعريف (الفاصلة
32
التراكيب واألصناف الوحدة الثانية
املنقوطة) فإنه جزء اختياري ،كما أن منت التعريف يتضمن تعريف ًا لعناصر الصنف
املختلفة (متغيرات ودوال) محددات الوصول لها أيض ًا اختيارية ،لكن ،في حالة عدم
تبيان احملدد ،فإن احملدد يعد تلقائي ًا من نوع يسمى . privateاملثال ( )1التالي ،يوضح
كيفية اإلعالن عن صنف ،عناصره أعداد كسرية:
مثال ()1
class RNum
{public:
void ;)assign(int, int
double ;)(convert
void ;)(invert
void ;)(print
private:
;int a,b
;}
من التعريف السابق يتضح ،أن الصنف سمي RNumوأن ميتلك مجموعة من العناصر
(احلقول والدوال) ،حيث أن الدوال ،)(print ،)(invert ، )(convert ، )(assign
تسمى بالدوال األعضاء أو املناهج Methodsأو الدوال املنتمية Member Function
كونها دوال تنتمي إلى الصنف احملدد ،أما املتغيرات a, bفتسمى باملتغيرات املنتمية
Member Variableأي املنتمية إلى الصنف ،ويتضح أيض ًا أن جميع الدوال املنتمية
عرفت على أنها عامة ،publicبينما املتغيرات املنتمية عرفت على أنها خاصة private
وهذه التعريفات (أي محددات الوصول) لها دالالت معينة سنتعرف عليها الحقاً.
مثال ()2
لتمثيل عقار معني باعتباره صنف ًا فإننا نحدد مجموعة احلقول و مجموعة الدوال
الالزمة ملعاجلة هذا الصنف ،وهنا نوضح كيف ميكن تعريف العقار propertyعلى
أنه صنف:
33
التراكيب واألصناف الوحدة الثانية
class property
{private:
float ;Area
float ;MonthRentVal
float ;Price
public:
float ;)increase_rent(float Incr
float ;)decrease_rent(float Decr
float ;)increase_price(float Incr
float ;)decrease_price(float Decr
float ;)(yearly_rent
float ;)(get_price
float ;)(get_area
float ;)(get_MonthRent
;}
34
التراكيب واألصناف الوحدة الثانية
5. ;)(float yearly_rent
حتسب هذه الدالة قيمة األجرة السنوية للعقار.
6. ;)(float get_price
تسترجع هذه الدالة قيمة املتغير املنتمي .Price
7. ;)(float get_area
تسترجع هذه الدالة قيمة املتغير املنتمي. Area
8. ;)(float get_MonthRent
تسترجع هذه الدالة قيمة املتغير املنتمي .MonthRentVal
مثال ()3
35
التراكيب واألصناف الوحدة الثانية
;“ =cout<<”r
;)(r.print
;cout<<” = “<<r.convert()<<endl
;)(r.invert
;“ = cout<<”1/r
;)(r.print
;cout<<endl
;int p
}
فقد مت إنشاء الكائن rمن الصنف ،RNumوأصبح واضح ًا أن الكائن rأعلن عنه مثل
املتغير العادي ولكن من نوع ،RNumوهذا يسمى نوع معرف من طرف املستخدم .فمن
الواضح أن الكائن rله القدرة على نداء الدوال املنتمية للصنف RNumاألربع،)(assign
.)(print ، )(invert ، )(convertفمثالً ،الدالة املنتمية ،)(printمت نداؤها عبر إحلاق
اسمها باسم مالكها بعد فصلهما بنقطة ، )(r.printوعليه ،فإن الدالة املنتمية يتوجب
نداؤها بهذا الشكل.
36
التراكيب واألصناف الوحدة الثانية
مثال ()4
وميكننا استخدام أي من هذه الكائنات عبر الدوال املنتمية (بعد كتابتها بالطبع)
باستدعائها بالطريقة املناسبة ،وذلك بكتابة اسم الكائن ثم نقطة ( )periodثم اسم الدالة
املراد استدعاؤها مع العوامل املناسبة .فمثال ،ميكننا استدعاء الدالة increase_rent
اخلاصة بالصنف propertyلزيادة األجرة الشهرية اخلاصة بالعقار p1مبقدار 100دينار
كما يلي:
;)p1.increase_rent(100.0
وينتج عن تنفيذ هذه الدالة زيادة املتغير MonthRentValاملنتمي إلى p1مبقدار
.100.0
وميكننا تصور اجلملة السابقة على أنها رسالة messageمرسلة إلى الكائن p1تطلب
فيها منه زيادة املتغير MonthRentValاخلاص به مبقدار 100.0دينار .وإلنقاص األجرة
الشهرية اخلاصة بالكائن p2مبقدار 50.0دينار ًا فإننا نرسل له الرسالة التالية:
;)p2.decrease_rent(50.0
تدريب ()2
عرف ،عزيزي الدارس ،الدالة increase_rentاملنتمية إلى الصنف porperty
التي تستخدم لزيادة األجرة الشهرية ،وعرف الدالة decrease_priceاملنتمية إلى
الصنف porpertyالتي تستخدم إلنقاص األجرة الشهرية.
37
التراكيب واألصناف الوحدة الثانية
3.3اإلعالن عن حقول الصنف ومحددات الوصول لها
()Access Modifiers
عزيزي الدارس ،ال بد أنك الحظت في األمثلة السابقة أننا قمنا بتعريف أصناف
ضمنت في منت التعريف باإلعالن عن حقول مختلفة هي مبثابة متغيرات ودوال ،وسميت
متغيرات منتمية Member Variableودوال منتمية .Member Functionبالطبع ،نحن
لم نكتب هذه الدوال بعد ،وكل ما فعلناه هو أننا وضعنا منوذج ( prototypeأول سطر
في تعريف الدالة) إعالن ًا لإلعالم على أنها دوال منتمية إلى الصنف وبي ّنا خصائصها،
ومبعنى آخر ،لم حتدد وظائفها بعد ،وأنها مخولة مبعاجلة املتغيرات املنتمية للصنف .إن
جملة تعريف الدالة هي ما يسمى عادة ترويسة الدالة ،التي حتتوي على نوع القيمة املعادة
، return typeثم اسم الدالة متبوعة بعواملها (إن وجدت) محاطة بأقواس هاللية.
لقد حان الوقت لتبيان الدالالت اخلاصة مبا يعرف مبحددات الوصول إلى املتغيرات
املنتمية والدوال املنتمية .إن الكلمتني privateو publicفي تعريف الصنف هما مبثابة
محددات الوصول؛ أي أنهما تبينان صالحية الوصول والتعامل مع حقول الصنف.
إن هاتني الكلمتني تقسمان الصنف إلى جزأين :اجلزء اخلاص privateواجلزء العام
public.في اجلزء اخلاص privateنعرف املتغيرات والدوال املنتمية التي ال نريدها أن
تعالج من جانب أي دالة سوى الدوال املنتمية .وهذا بهدف حتقيق مبدأ إخفاء البيانات
.Data Hidingالذي ينص على ضرورة أن يراعي مصمم الصنف اجلديد إمكانية
استخدام هذا الصنف ،من جانب املبرمجني في برامجهم املختلفة ،من خالل الدوال
املعرف بها هذا الصنف؛ وهذا ما يعرف أيض ًا
املنتمية له من دون احلاجة إلى معرفة الكيفية ّ
بالتراكيب التجريدية .abstract data typesولهذا األمر عدة فوائد منها ،تسهيل عملية
البرمجة؛ إذ يستطيع املبرمج أن يركز على مهمته الرئيسة دون االلتفات إلى التفاصيل
الدقيقة اخلاصة بكيفية تعريف الصنف .واألهم من ذلك هو انه إذا اضطررنا إلعادة النظر
في تعريف الصنف ودواله املنتمية لن نضطر إلى تعديل البرامج املستخدمة لهذا الصنف.
على سبيل املثال ال احلصر ،لو قررنا ،بعد كتابة جميع برامجنا التي تستخدم الصنف
،propertyتعديل تعريف هذا الصنف وجعل أجرة العقار أجرة سنوية بدال من شهرية؛
فإننا سنضطر فقط إلى إعادة كتابة بعض الدوال املنتمية (بحيث تعيد األجرة بعد قسمتها
على )12ولن نضطر إلى تعديل البرامج التي تستخدم هذه الدوال.
أما في القسم العام publicفنعرف املتغيرات والدوال املنتمية التي نريدها أن تستخدم
38
التراكيب واألصناف الوحدة الثانية
بشكل مباشر من جانب جميع الدوال في البرنامج ،وليس فقط من جانب الدوال
املنتمية .وتشكل هذه املتغيرات والدوال العامة الواجهة اخلارجية للصنف؛ إذ بدونها
ال ميكن استخدام الصنف وإجراء عمليات مفيدة عليه .وكما بينا سابق ًا فإنه من املمكن
حذف كلمة ، privateألنه عند عدم حتديد نوع القسم ،خاص ًا أو عــاماً ،تعد C ++
تلقائي ًا ذلك القسم خاص ًا. private
ال بد أن نبني الحق ًا أنه من خالل هذا املقرر وعند معاجلة موضوع التوارث سوف جتد
أن هناك محددات وصول أخرى ولها أيض ًا داللتها اخلاصة؛ حيث إن كل صنف بإمكانه
أن يحتوي على أربعة مستويات للوصول إلى حقول الصنف ،وفيما يلي سرد لداللة كل
محدد من هذه احملددات:
•احملدد اخلاص :private
غير متاح التعامل مع هذه املتغيرات املنتمية والدوال املنتمية إ ّ
ال َع ْب َر أعضاء الصنف
املنتمية له (املعرفة بداخله).
•احملدد العام :public
متاح التعامل مع هذه املتغيرات املنتمية والدوال املنتمية َع ْب َر جميع األصناف األخرى.
•احملدد الصديق :Friend
غير متاح التعامل مع هذه املتغيرات املنتمية والدوال املنتمية إال َع ْب َر أعضاء الصنف
املنتمية له ،أو أعضاء الصنف الصديق للصنف املنتمية له.
•احملدد احملمي :protected
غير متاح التعامل مع هذه املتغيرات املنتمية والدوال املنتمية إال َع ْب َر أعضاء الصنف
املنتمية له ،وعند توريث الصنف وراثة عامة فإن هذه املتغيرات املنتمية والدوال املنتمية
يتاح التعامل معها من طرف األصناف املهيمنة.
39
التراكيب واألصناف الوحدة الثانية
.4دوال األصناف
1.4الدوال املنتمية () Member Function
1.1.4تعريف الدالة املنتمية
لقد وضحنا كيفية اإلعالن عن الدوال املنتمية ،وحان الوقت لتوضيح كيفية تعريف
الدوال املنتمية؛ أي تبيان عمل كل منها وذلك عبر كتابة الدوال املنتمية لصنف معني .إن
كتابة دالة منتمية إلى صنف ال تختلف كثير ًا عن طريقة كتابة دالة عادية ليس لها عالقة
بأي صنف ،واالختالف الرئيس يكمن في أنه يجب حتديد الصنف الذي تنتمي إليه الدالة
املنتمية عند كتابتها .بالعودة إلى املثال ( ،)1فقد أعلن في الصنف املسمى RNumعن
مجموعة من الدوال املنتمية ،فمثالً invert ،معلنة ولكن لم تعرف بعد ،والسؤال كيف
نعرفها (نكتبها)؟ ففي املثال ( ،)3عرفت الدالة املنتمية invertعلى الشكل التالي:
)(void RNum::invert
{int ;t=a
; a=b
; b=t
}
الحظ ،عزيزي الدارس ،أن أول جملة في تعريف الدالة وتسمى ،عاد ًة ،ترويسة
الدالة ،احتوت على نوع القيمة املعادة ،return typeوهو في هذه احلالة ،voidثم
اسم الصنف RNumالذي تنتمي إليه الدالة ،وبذلك ،ال جند أي اختالف عن تعريف
الدالة العادية إال بتحديد الصنف الذي تنتمي له هذه الدالة الذي يتم من خالل الرمز ::
وتدعى بعملية تقرير املجال scope resolution operatorثم اسم الدالة متبوعة بعواملها
محاطة بأقواس .يستخدم اسم الصنف متبوع ًا بـ ::للداللة على أن الدالة invertهي
الدالة نفسها التي كتبنا منوذج ًا لها (املعلنة) في تعريف الصنف ،RNumوبدون رمز تقرير
املجال ::لن يعرف املترجم أن الدالة اجلاري تعريفها هي دالة منتمية للصنف ،RNum
مما سوف يتسبب بخطأ .الحظ ،عزيزي الدارس ،أن ترويسة الدالة هي نفسها النموذج
الذي وضعناه لها داخل تعريف الصنف ،لكن ال يحتوي النموذج على اسم الصنف وال
على عملية تقرير املجال.
40
التراكيب واألصناف الوحدة الثانية
أما في املثال ( ،)2فنجد أنه أعلن في الصنف propertyعن دالة منتمية اسمها
increase_rentنعرفها لتقوم هذه الدالة بزيادة قيمة املتغير املنتمي MonthRentVal
مبقدار املعامل Incrثم ترجع لنا قيمة املتغير بعد الزيادة .التعريف يتم على الشكل التالي:
)float property::increase_rent(float Incr
;{ MonthRentVal +=Incr
;return MonthRentVal
}
لكن املتغير MonthRentValخاص بأي كائن؟ تذكر ،عزيزي الدارس ،أن هذه
الدالة تستخدم باعتبارها رسالة ترسل إلى كائن من الصنف propertyكما في:
;)p1.increase_rent(100.0
وعليه ،فإن MonthRentValاخلاص بـ p1هو الذي يستخدم عند تنفيذ الدالة ومن
ثم ،يزداد مبقدار .100.0
وتعرف الدالة اخلاصة بـ decrease_rentبطريقة مشابهة كما يلي:
)float property::decrease_rent(float Rent
){ if(Decr<MonthRentVal
;{MonthRentVal -=Decr
;return MonthRentVal
}
else
;return 0.0
}
تقوم هذه الدالة بالتأكد من أن مقدار النقصان في األجرة الشهرية Decrأقل من األجرة
قبل أن تقوم بطرح القيمة من MonthRentVal؛ ذلك كي ال تصبح أجرة العقار أقل من
صفر .تعيد هذه الدالة قيمة MonthRentValبعد النقصان ،وإذا لم تكن قيمة النقصان
أقل من األجرة فإن الدالة تعيد 0.0لإلشارة إلى أن قيمة MonthRentValلم تنقص شيئاً.
وتعرف الدالة yearly_rentكما يلي:
Float )(property::yearly_rent
;{return 12*MonthRentVal
}
41
التراكيب واألصناف الوحدة الثانية
وحتسب األجرة السنوية للعقار بضرب األجرة الشهرية للعقار بعدد األشهر في
السنة .وتعرف الدالة get_priceالتي تستخدم السترجاع سعر عقار ما كما يلي:
)(float property::get_price
;{ return Price
}
نشاط ()2
عزيزي الدارس ،تالحظ أن األمثلة والتدريبات اخلاصة بالصنف املسمى
قد بينت كيفية تعريف الدوال املنتمية لهذا الصنف ،وعليه، property
واكتب برنامج ًا porperty قم بتجميع مجمل التعريفات اخلاصة بالصنف
متكام ً
ال للتعامل مع هذا الصنف.
تدريب ()3
عرف الدالة increase_priceاملنتمية إلى الصنف propertyالتي تستخدم
لزيادة سعر Priceالعقار.
تدريب ()4
عرف الدالة decrease_priceاملنتمية إلى الصنف porpertyالتي تستخدم
إلنقاص سعر العقار.
42
التراكيب واألصناف الوحدة الثانية
تدريب ()5
عرف الدالة )( get_areaالتي تستخدم السترجاع مساحة areaالعقار.
تدريب ()6
عرف الدالة )(get_MonthRentالتي تسترجع قيمة املتغير املنتمي .MonthRentVal
تدريب ()7
ٍ
كتاب يباع في مكتبة لبيع الكتب .حيث يحتوي عرف الصنف Bookلتمثيل
هذا الصنف على املتغيرات املنتمية التالية:
price .1ميثل سعر الكتاب.
quantity .2ميثل عدد النسخ املتوافرة من ذاك الكتاب.
title .3ميثل اسم الكتاب.
والدوال املنتمية التالية:
.1دالة حلساب السعر الكلي جلميع الكتب.
.2دالة لزيادة سعر الكتاب.
44
التراكيب واألصناف الوحدة الثانية
منوذج prototypeجديد في التعريف
ٍ ال بد أنك الحظت ،عزيزي الدارس ،وجود
اجلديد للصنف propertyوهو
;)property(float sz, float Rent, float prc
إن هذا النموذج خاص بالب َّّناء اخلاص بإعطاء الكائنات من صنف propertyقيم ًا
ابتدائية .الحظ ،عزيزي الدارس ،أن هذه الدالة الب َّناءة تختلف عن بقية الدوال من
حيث:
.1أن اسم الدالة الب َّناءة هو اسم الصنف نفسه أي propertyفي مثالنا.
تعرف نوع ًا للقيمة املرجعة return typeوذلك ألن الب َّناء ال
.2وأن الدالة الب َّناءة ال ّ
نعرف نوع ًا للقيمة املرجعة للدالة الب ّناءة حتى لو
يرجع قيمة .وإنه من اخلطأ أن ّ
كان .void
إن ما يقوم به هذا الب ّناء هو إعطاء املتغيرات قيم ًا ابتدائية مناسبة حسب قيم العوامل.
لقد سبق ذكرنا ،عزيزي الدارس ،أن الب ّناء يستدعى تلقائي ًا عند إنشاء كائن من
الصنف املطلوب وعليه ،يجب تزويده بالعوامل الضرورية ،ولذا فإنه من اخلطأ إنشاء
الكائن p1بوساطة اجلملة:
;property p1
ألنها ال حتدد قيم العوامل الضرورية إلعطاء املتغيرات املنتمية لـ p1قيمها.
وإلنشاء الكائن p1من الصنف ،propertyواستدعاء الب ّناء وإعطائه قيم ًا ابتدائية،
نستخدم اجلملة:
;) property p1=property(150.0,100.0,250
إن هذه اجلملة تنشئ الكائن p1وتستدعي البناء propertyإلعطاء متغيراته املنتمية
Areaو MonthRentValو Priceالقيم 150.0و 100.0و 250.0على الترتيب.
45
التراكيب واألصناف الوحدة الثانية
وميكننا اختصار اجلملة السابقة وكتابة:
;)property p1(150.0,100.0,250
إن إنشاء الكائن p1بوساطة اجلملة:
;property p1
التي ال حتدد عوامل للبناء ،تكون صحيحة فقط في حالة عدم وجود
بناء للصنف propertyأو في حالة وجود بناء ال يأخذ عوامل كالبناء التالي:
)(property::property
; {Area=0.0
;MonthRentVal=0.0
;Price=0.0
}
46
التراكيب واألصناف الوحدة الثانية
تدريب ()8
عزيزي الدارس ،في املثال ( )3مت اإلعالن عن الصنف RNumالذي عناصره
أعداد كسرية وميتلك الدالة املنتمية )( ،assignاملطلوب كتابة برنامج يستخدم
الصنف نفسه مع إضافة بناء لهذا الصنف بد ً
ال من هذه الدالة .)(assign
ولكن لهذه الطريقة أيض ًا ثمن ،حيث إنها تزيد حجم البرنامج الناجت عن عملية
الترجمة وهو ما يدعى بـ البرنامج التنفيذي .executable programولذا ،فإنها قد ال
تكون مناسبة للدوال كبيرة احلجم التي تستدعي عدد ًا كبير ًا من املرات؛ إذ إن ذلك قد
يزيد حجم البرنامج التنفيذي بصورة كبيرة.
47
التراكيب واألصناف الوحدة الثانية
يطلب املبرمج من املترجم استخدام هذه الطريقة «االنتشار السطري» (inline
)expansionعن طريق تعريف الدوال الصغيرة على أنها دوال سطرية ،inline function
وللمترجم احلق في رفض هذا الطلب والتعامل مع هذه الدوال بالطريقة التقليدية خاصة
إذا كانت الدالة كبيرة.
وجند أن الدوال السطرية هي نوع من أنواع الدوال املنتمية للصنف ،وفيما يلي نبني
كيفية التعامل مع الدوال السطرية:
48
التراكيب واألصناف الوحدة الثانية
}
;)(float get_area
;)(float get_MonthRent
;}
الطريقة الثانية
نستطيع ترك تعريف الدوال خارج تعريف الصنف وفي الوقت نفسه تعريفهما على
أنهما دوال سطرية ،وذلك بوضع كلمة inlineكأول كلمة في ترويسة الدالة (أول جملة
في التعريف) .فمثال ،لتعريف الدالة increase_priceكدالة سطرية ،نستطيع كتابتها
خارج تعريف الصنف كما يلي:
)inline float increase_rent(float Incr
;{ Price+=Incr
};return Price
49
التراكيب واألصناف الوحدة الثانية
البد أنك الحظت ،عزيزي الدارس ،أننا عرفنا نوع املتغير iداخل جملة الدوران for
وهذا جائز في لغة .C ++في الواقع ،إن لغة C ++مرنة جدا من حيث ترتيب جمل تعريف
النوع type declarationواجلمل التنفيذية ،املهم في األمر هو أن تعرف نوع املتغير قبل أن
تستخدمه ألول مرة ،وأن تراعي قواعد حتديد مجال املتغيرات.
التعريف الثاني:
)int power(int x
;{ return x*x
}
وعند استدعاء إحدى هذه الدوال فإن مترجم ++Cيستخدم اسم الدالة وكذلك عدد
العوامل ونوعها في حتديد أي التعريفني يستخدم ،فمثال اجلملة:
;)cout<<power(2,3
واضح من عدد العوامل أن التعريف األول لـ powerهو املطلوب ،ولذا فإن النتيجة
تكون عرض القيمة .8أما اجلملة:
;)cout<<power(2
فتستدعي التعريف الثاني لـ power؛ ولذا فإن النتيجة تكون عرض القيمة 4على
شاشة العرض.
إن هذه اخلاصية لـ C ++تدعى بخاصية “حتميل الدوال” Function Overloading
أي حتميلها أكثر من تعريف؛ مما يعطي لغة C ++مرونة إضافية في التعامل مع الدوال.
على سبيل املثال ،نستطيع تعريف أكثر من بناء للصنف classبأعداد وأنوع مختلفة من
العوامل .فمثال ،للصنف porpertyنستطيع تعريف بناء آخر ال يأخذ عوامل ،يعطي
كل متغير منتمي القيمة صفر.
)(property::property
;{Area=0.0
; MonthRentVal=0.0
; Price=0.0
}
بالطبع ،نحتاج إلى وضع منوذج لهذا البناء داخل تعريف الصنف بحيث يصبح
{class property
private:
50
التراكيب واألصناف الوحدة الثانية
float ;Area
float ;MonthRentVal
float ;Price
public:
;)(property
;)property(float sz, float Rent, float prc
;)float increase_rent(float Incr
;)float decrease_rent(float Decr
;)float increase_price(float Incr
)float decrease_price(float Decr
;)(float yearly_rent
;)(float get_price
;)(float get_area
;)(float get_MonthRent
;}
ومن اجلدير بالذكر أنه بوجود هذا البناء تصبح اجلملة:
;property p1
صحيحة ،وذلك ألن البناء اجلديد هو الذي يستخدم لبناء الكائن p1حيث يأخذ كل
متغير منتمي لـ p1القيمة .0.0
وجتدر اإلشارة أيضا إلى أننا نستطيع تعريف العديد من البنائني للصنف ولكن ال
نستطيع أن نكتب أكثر من هدام واحد.
ال يسمح للدوال احململة بأن تختلف فقط في نوع الدالة (نوع القيمة املرجعة) ،بل
يجب أن تختلف في نوع عوامل الدالة أو عددها.
تدريب ()9
عزيزي الدارس ،اكتب برنامج ًا بداخله دالة سمها myFuncولها ثالث
صيغ :الصيغة األولى تعمل على إعادة العدد الصحيح نفسه املرسل لها،
الصيغة الثانية تعيد عدد ًا صحيح ًا ميثل ناجت جمع العددين الصحيحني املرسلني
لها ،أما الصيغة الثالثة فتعيد عدد ًا حقيقي ًا ميثل ناجت ضرب العدد الصحيح األول
بناجت جمع العدد الصحيح الثاني مع العدد احلقيقي الثالث املرسل للدالة.
51
التراكيب واألصناف الوحدة الثانية
3.4العوامل التلقائية Default Arguments
متكننا لغة C ++من تعريف ما يسمى بالعوامل التلقائية للدوال ،حيث تستخدم هذه
العوامل تلقائي ًا في حالة عدم تزويد الدالة بعوامل حقيقية عند استدعائها .لنفرض ،على
سبيل املثال ،أن مقدار إيجار العقار يزداد مبقدار ثابت في معظم األحيان وهو 100.0
دينار ،وعليه ،فإننا نريد أن جنعل القيمة التلقائية للزيادة .100.0ويتم ذلك بتغيير بسيط
على منوذج الدالة increase_rentاملوجود داخل تعريف الصنف بحيث يصبح كما يلي:
;)inline float increase_rent (float Incr=100.0
وال يلزم تغيير أي شيء في طريقة كتابة الدالة نفسها.
وهكذا نستطيع اآلن أن نستدعي الدالة increase rentدون إعطائها قيمة للعامل
،Incrكما في اجلملة:
;)(p1.increase_rent
حيث تقوم هذه اجلملة بزيادة قيمة األجرة الشهرية اخلاصة بالكائن p1مبقدار 100.0
دينار .وبالطبع ،نستطيع استدعاء الدالة نفسها وإعطاءها قيمة للعامل Incrكما في
اجلملة
;)p1.increase_rent(150.0
52
التراكيب واألصناف الوحدة الثانية
البناء .ومن ثم فإن اجلملة:
;)property p2(150.0
تنشئ الكائن p2وتعطي املتغير املنتمي Areaالقيمة 150.0واملتغير املنتمي
MonthRentValالقيمة التلقائية 200.0واملتغير املنتمي Priceالقيمة التلقائية
.10000.0
ومن اجلدير بالذكر أننا ال نستطيع حذف قيمة العامل Rentوتزويد العامل prcبقيمة
محددة .فمثال ،اجلملة:
;)property p3(200.0,30000.0
تنشئ الكائن p3وتعطي املتغير املنتمي Areaالقيمة 200.0واملتغير املنتمي
MonthRentValالقيمة 30000.0واملتغير املنتمي Priceالقيمة التلقائية .10000.0أي
أن القيمة 30000.0استخدمت قيمة للعامل .Rent
تدريب ()10
أعد تعريف الدالة decrease_priceكدالة سطرية باستخدام الطريقتني األولى
والثانية.
تدريب ()11
أعد تعريف الدالة decrease_priceبحيث تأخذ القيمة التلقائية .1000.0
53
التراكيب واألصناف الوحدة الثانية
4.4الدوال املنتمية الثابتة والكائنات الثابتة
)(const member functions & const objects
عزيزي الدارس ،إن هناك عادة برمجية إلنشاء الثوابت ،وذلك إذا لم تكن هناك
حاجة لتغييرها ،وتسمح لغة C ++بإجراء ذلك من خالل استخدام الكلمة املخصصة
لذلك ،constوإن أي متغير يصبح ثابت ًا في حالة اإلعالن عنه أنه من نوع ثابت ،وذلك
بإضافة كلمة constفي بداية جملة اإلعالن كما يأتي على سبيل املثال:
;const float nVal=8.5
;const int mVal=6
;‘ ‘ =const char bVal
والسؤال :هل تسمح لغة C ++بالتعامل مع الدوال املنتمية كدوال ثابتة؟؟ واإلجابة:
نعم ،وعندها ال ميكن ألي دالة أخرى أو كائن يتعامل مع الدالة املنتمية الثابتة من إجراء
أي تغيير عليها ،وعند اإلعالن عن الدالة يتوجب تبيان أنها من النوع الثابت ،حيث إن
ذلك يتم بوضع كلمة constفي ترويسة الدالة وبالتحديد ،بعد األقواس الهاللية اخلاصة
باملعامالت ،وتعريف الصنف Exالتالي ،حيث ميتلك تعريف ًا للدالة السطرية املسمى
fun1وتعريف ًا الدالة السطرية الثابتة املسمى :fun2
class Ex
{ private:
;int x
public:
};void fun1() { x=0
};)(void fun2() const { fun1
;}
اعلم ،عزيزي الدارس ،أنه في حالة وجود دالة منتمية ثابتة ،فإنه ال ميكن أن يجري
عبرها أي تعديل على متغير منتمي أو أن تنادي أي دالة أخرى لم تعرف على أنها دالة منتمية
ثابتة (كما هو موضح في تعريف الصنف السابق املسمى Ex؛ حيث إن الدالة الثابتة fun2
تنادي الدالة غير الثابتة fun1وهذا خطأ) وإال فإنه سوف يحصل خطأ (،)Compiler Error
ونوضح كيفية محاولة التعامل مع متغير منتمي من خالل تعريف الصنف ErrTestالتالي:
class ErrTest
{public:
;int mVal
} ;void setVal() const { mVal=0
;}
54
التراكيب واألصناف الوحدة الثانية
تعمل على إسناد القيمة صفر إلىsetVal أن الدالة املنتمية الثابتة،يتضح من التعريف
. وهذا غير مقبول ويؤدي إلى خطأ،) (تعديل على قيمة متغير منتميmVal املتغير املنتمي
)5( مثال
الذي ميتلك مجموعة من الدوال املنتمية، Date يوضح كيفية اإلعالن عن الصنف
: كما يليGetYear()،GetDay() ،GetMonth() :الثابتة
#include <iostream>
class Date
{ private:
int Month;
int Day;
int Year;
Date() { }
public:
Date(int nMonth, int nDay, int nYear)
{SetDate(nMonth, nDay, nYear); }
void SetDate(int nMonth, int nDay, int nYear)
{Month = nMonth;
Day = nDay;
Year = nYear; }
int GetMonth() const { return Month; }
int GetDay() const { return Day; }
int GetYear() const { return Year; }
};
int main()
{ const Date XDate(6, 28, 2009);
PrintDate(XDate);
int p; cin>>p;
return 0;
}
55
التراكيب واألصناف الوحدة الثانية
.5اإلدخال واإلخراج في لغة C ++
تزودنا لغة C++بالعديد من العمليات operatorsوالدوال اخلاصة باإلدخال
واإلخراج ،باإلضافة إلى تلك املوجودة في لغة Cاألصلية التي نستطيع أيضا استخدامها
في لغة .C++
1.5اإلدخال Input
يستخدم الكائن ( cinويقرأ )see inإلدخال قيم للمتغيرات عن طريق لوحة املفاتيح،
فمث ً
ال اجلمل:
;float x
;int y
;cin>>x>>y
تعرف املتغير xمن النوع floatواملتغير yمن النوع ،intواجلملة الثالثة تقرأ قيم ًا
لهما .وعند إدخال القيم لـ xو yيجب أن يفصل بينهما فراغ واحد على األقل .وميكن
أن تدخل كل قيمة على سطر مستقل.
بالنسبة لقيمة xيجب أن حتتوي على الفاصلة العشرية وإال تفترض ++Cوجود
الفاصلة على أقصى ميني الرقم.
الكائن cinهو من الصنف istreamوهو أيض ًا معرف داخل امللف (املكتبة)
iostream.hوعلينا وضع جملة التضمني:
>#include<iostream.h
في بداية أي برنامج يستخدم الكائن .cin
تدعى العملية >> عملية االستخراج extractionأي األخذ من جدول املدخالت
.input stream
نشاط ()3
برنامج كاملٍ الختبار الصنف Bookوجميع
ٍ قم ،عزيزي الدارس ،بكتابة
دواله املنتمية وتنفيذه.
56
التراكيب واألصناف الوحدة الثانية
تدريب ()12
بناء يقوم
أضف ،عزيزي الدارس ،للصنف Bookالذي كتب في التدريب ً 7
بقراءة قيم جلميع متغيراته املنتمية و دالة أخرى لطباعة قيم هذه املتغيرات.
57
التراكيب واألصناف الوحدة الثانية
2.5اإلخراج Output
يستخدم الكائن coutويقرأ ( )see outلعرض النتائج على شاشة العرض .فمثال،
اجلملة
;”cout<<”Hello world
تعرض الرسالة Hello worldعلى الشاشة .وترمز >> إلى عملية معرفة تسمى
عملية الوضع (أي الوضع على الكائن coutالسلسلة الرمزية (“)”Hello world
.ومبا أن الكائن coutميثل الشاشة فإن نتيجة تنفيذ اجلملة السابقة هو ظهور السلسلة
الرمزية Hello worldعلى الشاشة.
ونستطيع استخدام سلسلة الهروب ( ’escape sequence) ‘\nلالنتقال إلى
سطر جديد new lineعلى الشاشة .قارن ،عزيزي الدارس ،بني اجلملتني التاليتني:
;”cout<<”Hello “<<”world
;”cout<<”Hello \n”<<”world
إن نتيجة تنفيذ اجلملة األولى هو ظهور الرسالة Hello worldعلى سطر واحد على
الشاشة .أما اجلملة الثانية فينتج عنها ظهور الكلمة Helloعلى سطر و الكلمة world
على سطر آخر.
يدعى الرمز \ برمز الهروب escapeوذلك ألنه يسمح لـ C++بترجمة الرمز
الالحق بطريقة مختلفة عن العادة .الحظ ،عزيزي الدارس ،أن نتيجة تنفيذ اجلملة:
;» cout<<»Hello n»<<»world
أي دون كتابة الرمز \ قبل احلرف nسينتج عنه عرض:
Hello n world
أي لم يترجم احلرف nعلى أنه رمز السطر اجلديد بل على أنه احلرف .n
وهنالك العديد من سالسل الهروب التي تستخدم لتهيئة املخرجات output
formattingمنها:
جتعل احلاسوب يصدر صوتا منبها alert \a
جتعل احلاسوب يقفز عدد ًا من املسافات تارك ًا إياها فارغة. \t
تستخدم لعرض الرمز « داخل سلسلة رمزية . stringعلى سبيل \»
املثال ،لعرض الرسالة »hi»Aliنكتب اجلملة:
;« »\cout<<»hi\»Ali
58
التراكيب واألصناف الوحدة الثانية
\’ تستخدم لعرض الرمز ‹ داخل سلسلة رمزية .stringعلى سبيل املثال لعرض
الرسالة Don’tنكتب اجلملة:
;”cout<<”Don\’t
نشاط ()4
قم ،عزيزي الدارس ،بكتابة برنامج صغير وتنفيذه لتجربة كل من سالسل
الهروب السابقة (باستخدام احلاسوب).
.6اخلالصة
تعد األصناف مصنع الكائنات ،حيث تستخدم إلنشاء كائنات حتتوي على متغيرات ّ
ودوال منتمية .وتختلف األصناف عن التراكيب بشكل رئيس في أن لها دوال منتمية.
تستخدم هذه الدوال املنتمية ملعاجلة األجزاء اخلاصة من الكائنات .وال يحق للدوال
األخرى معاجلة هذه األجزاء وذلك بهدف حتقيق مبدأ إخفاء البيانات .وهنالك نوعان
قيم ابتدائية
خاصان من الدوال املنتمية يسمى النوع األول «البناؤون» ،ويستخدم إلعطاء ً
للكائنات عند إنشائها .أما النوع الثاني من الدوال املنتمية الذي يدعى «الهدامون»
فيستخدم عند انتهاء احلاجة إلى كائن معني ألغراض مختلفة .تتم عملية إدخال البيانات
بوساطة الكائن cinوالعملية >> بينما تتم عملية إخراج النتائج باستخدام الكائن cout
والعملية <<.
59
التراكيب واألصناف الوحدة الثانية
ميكن في لغة C++أن نكتب أكثر من دالة حتت االسم نفسه .وال يسبب ذلك أي
لبس ألن عدد العوامل ونوعها ،باإلضافة إلى اسم الدالة ،يستخدم لتحديد التعريف
الواجب استخدامه .كذلك ميكن تعريف الدوال الصغيرة كدوال سطرية مما ميكن مترجم
لغة C++من استبدال جمل استدعاء هذه الدوال باجلمل املعرفة لها ،مما يوفر في كثير من
األحيان بعضا من وقت التنفيذ .كما ميكن أن نعرف قيم ًا تلقائية لعوامل الدوال تستخدم
بشكل تلقائي عند استدعاء هذه الدوال دون حتديد العوامل املطلوبة.
.8إجابات التدريبات
تدريب ()1
{ struct Person
;int age
;long ss
;float weight
;]char name[25
;} family_member
{ )(int main
;struct Person ali
;Person sami
;ali.age = 22
;sami.age = 35
}
60
التراكيب واألصناف الوحدة الثانية
)2( تدريب
float property::increase_rent(float rt)
{ rent +=rt;
return rent;}
float property::decrease_rent(float rt)
{ rent -=rt;
return rent;}
)3( تدريب
float property::increase_price(float amt)
{ Price +=amt;
return Price;}
)4( تدريب
float property::decrease_price(float amt)
{ Price -=amt;
return Price;}
)5( تدريب
float property::get_area()
{ return Area;}
)6( تدريب
float property::get_MonthRent()
{ return MonthRentVal;}
)7( تدريب
class Book{
double price;
int quantity;
char title[20];
public:
61
التراكيب واألصناف الوحدة الثانية
double total_price();
double increase_price(double incr);
};
double Book::total_price()
{return price*quantity;}
double increase_price(double incr)
{price+=incr;}
)8( تدريب
#include <iostream.h>
class RNum
{public:
RNum (int n, int d) {a=n; b=d;}
double convert();
void invert();
void print();
private:
int a,b;
};
main()
{ RNum x(5, 8);
cout<<»x= «;
x.print();
cout<<» = «<<x.convert()<<endl;
x.invert();
cout<<»1/x = «;//<<
x.print();
cout<<endl;
int p;
cin>>p;
}
double RNum::convert()
62
التراكيب واألصناف الوحدة الثانية
{ return double(a)/b;
}
Void RNum::invert()
{ int t=a;
a=b;
b=t;
}
Void RNum::print()
{ cout<<a<<›/›<<b;
}
)9( تدريب
#include <iostream>
int myFunc(int i)
{ return i; }
63
التراكيب واألصناف الوحدة الثانية
)10( تدريب
الطريقة األولى
class property{
protected:
float Price;
float Monthly_Rent;
public:
property();
float increase_rent(float amt)
{ Monthly_Rent +=amt;
return Monthly_Rent;}
float decrease_rent(float amt)
{ Monthly_Rent -=amt;
return Monthly_Rent;}
float increase_price(float amt)
{ Price+=amt;
return Price;}
float decrease_price(float amt)
{ Price -=amt;
return Price;}
};
:الطريقة الثانية
inline float property::increase_price(float amt)
{ Price+=amt;
return Price;}
)11( تدريب
داخل تعريف الصنفdecrease_price إن ما يجب تعديله هو فقط منوذج الدالة
بحيث يصبح،property
class property{
private:
float Area;
64
التراكيب واألصناف الوحدة الثانية
float MonthRentVal;
float Price;
public:
float increase_rent(float Incr);
float decrease_rent(float Decr);
float increase_price(float Incr);
float decrease_price(float Decr=1000.0);
float yearly_rent();
float get_price();
float get_area();
float get_MonthRent();
};
)12( تدريب
Book::Book()
{ cout<<”Enter the title of the book”;
cin>>title;
cout<<”Enter the price of the book”;
cin>>price;
cout<<”Enter the number of books”;
cin>>quantity;
}
void Book::print_book()
{ cout<<”the title of the book”;
cout<<title;
cout<<”Enter the price of the book”;
cout<<price;
cout<<”Enter the number of books”;
cout<<quantity;
}
65
التراكيب واألصناف الوحدة الثانية
.9مسرد املصطلحات
إعادة حتميل الدوال :Function Overloadingأي حتميل الدالة املعاد -
حتميلها أكثر من تعريف.
البناء :Constructorهو دالة منتمية للصنف تأخذ اسم الصنف نفسه -
الذي يتبع له وتعلن بدون قيمة معادة وتستدعى تلقائي ًا عند إنشاء كائن من
صنفها.
التراكيب :Structureمجموعة أو حزمة من احلقول fieldsاملتعلقة بكائن -
objectمعني يجمعها اسم تركيب محدد.
الكائن :Objectهو املثيل عن صنفه. -
الصنف :Classهو مبثابة القالب (النموذج ،التصميم) الذي على أساسه -
نستطيع استخراج أعضاء وكائنات تشترك في الصفات والسلوكيات.
محددات الوصول :Access Modifiersآلية لتبيان صالحية الوصول -
والتعامل مع حقول الصنف.
احملدد اخلاص :Privateال يتيح التعامل مع املتغيرات املنتمية والدوال -
املنتمية إال من خالل أعضاء الصنف املنتمية له (املعرفة بداخله).
احملدد العام :Publicيتيح التعامل مع املتغيرات املنتمية والدوال املنتمية -
من خالل جميع األصناف األخرى.
احملدد الصديق :Friendال يتيح التعامل مع املتغيرات املنتمية والدوال -
املنتمية إال من خالل أعضاء الصنف املنتمية له ،أو أعضاء الصنف الصديق
للصنف املنتمية له.
احملدد احملمي :Protectedال يسمح بالتعامل مع املتغيرات املنتمية -
والدوال املنتمية إال من خالل أعضاء الصنف املنتمية له ،وعند توريث
الصنف وراثة عامة فإن هذه املتغيرات املنتمية والدوال املنتمية يتاح التعامل
معها من قبل األصناف املهيمنة.
الهدام :Destructorويستخدم الهدام عند انتهاء احلاجة إلى الكائن -
املنشأ.
66
التراكيب واألصناف الوحدة الثانية
املراجع.10
1. Bronson, Gary, “Program Development and Design Using C++”,
Third Edition. PWS Publishing Company, 2005.
2. Cannon, Scott, “Understanding Programming: An Introduction Us-
ing C++”. Brooks/Cole Publishing Co, 2001.
3. Nell Dale, Chip Weems, “Programming and Problem Solving with
C++, Fourth Edition”. Jones and Bartlett Publishers, 2004.
4. Savitch, Walter, “Problem Solving with C++, 7/E”. Addison-Wesley
Publishing Company, Inc 2008.
67
التراكيب واألصناف الوحدة الثانية
68
التراكيب واألصناف الوحدة الثانية
الوحدة الثالثة
المصفوفات واألصناف
Arrays and Classes
70
املصفوفات واألصناف الوحدة الثالثة
محتويات الوحدة
الصفحة امل ــوض ـ ـ ـ ــوع
73 .1املقدمة
73 1.1متهيد
73 2.1أهداف الوحدة
73 3.1أقسام الوحدة
74 4.1القراءات املساعدة
74 5.1ما حتتاج إليه لدراسة الوحدة
75 .2مصفوفات من الكائنات Arrays of Objects
83 .3الدوال ومصفوفات الكائنات
83 1.3ترتيب مصفوفة من الكائنات
88 2.3البحث الثنائي Binary Search
137 .8اخلالصة
138 .9حملة عن الوحدة الدراسية الرابعة
138 .10إجابات التدريبات
147 .11مسرد املصطلحات
148 .12املراجع
71
املصفوفات واألصناف الوحدة الثالثة
72
املصفوفات واألصناف الوحدة الثالثة
.1املقدمة
1.1متهيد
ع��زي��زي ال����دارس ،إن معظم التطبيقات العملية تتطلب اس��ت��خ��دام ق��وائ��م من
الكائنات ،list of objectsوغالب ًا ما تنفذ هذه القوائم باستخدام مصفوفات من
الكائنات Arrays of objectsأو باستخدام قوائم متصلة من الكائنات linked lists
.of objectsفي هذه الوحدة ،سنناقش كيفية إنشاء مصفوفات وقوائم متصلة من
الكائنات عبر أمثلة عملية وتدريبات مختلفة.
2.1أهداف الوحدة
ينتظر منك ،عزيزي الدارس ،بعد فراغك من دراسة هذه الوحدة أن تكون قادرا
على أن:
تعرف وتستخدم التراكيب .structures
ّ .1
.2تستخدم مصفوفات من التراكيب .arrays of structures
.3تعرف وتستخدم األصناف .classes
.4متيز بني التراكيب واألصناف.
.5تنشئ الكائنات وتستخدمها بطريقة صحيحة.
.6تكتب الدوال املنتمية وتستدعيها بطريقة صحيحة.
.7تكتب الدوال السطرية.
.8حتمل الدوال أكثر من تعريف واحد.
.9تدخل البيانات وتخرجها بوساطة cinو .cout
3.1أقسام الوحدة
تقسم هذه الوحدة إلى ستة أقسام رئيسة حيث يبحث القسم األول في كيفية
تعريف مصفوفة من الكائنات وه��و بذلك يحقق الهدف األول .بينما يبحث
القسم الثاني في كيفية مترير املصفوفات باعتبارها عوامل إلى دوال ،كما يناقش
عدة تطبيقات عملية الستخدام املصفوفات ،ويحقق هذا القسم الهدفني الثاني
والثالث .أما القسم الثالث فيناقش كيفية احتواء مصفوفة من الكائنات في صنف
واحد ،ويحقق هذا القسم الهدف الثالث .وأما القسم الرابع فإنه ميهد الطريق
73
املصفوفات واألصناف الوحدة الثالثة
لبقية أقسام الوحدة؛ إذ يناقش كيفية استخدام الكائنات عبر متغيرات مؤشرة،
ويحقق األهداف :الرابع و اخلامس والسادس .ويطرح القسم اخلامس كيفية
حجز الذاكرة بطريقة ديناميكية ،وهو بذلك يحقق الهدف الرابع .ويبحث
القسم ال��س��ادس في موضوع القوائم املتصلة وكيفية تشكيلها من كائنات،
ويحقق هذا القسم الهدف السادس.
4.1القراءات املساعدة
عزيزي الدارس ،لقد حاولنا تقدمي الكثير من املفاهيم املهمة في هذه الوحدة بشكل
مبسط .ولكن كي تتعلم لغة ++Cبشكل جيد جد ًا ستجد أنه من املفيد أن تدرس أكبر
عدد ممكن من األمثلة والتمارين .لذا ننصحك بالرجوع إلى أكبر عدد ممكن من كتب
لغة .C++وفيما يلي جتد قائمة من هذه الكتب:
1. Cannon, Scott, Understanding Programming: An Introduction Us-
ing C++, West Publishing Company, 1997.
2. Savitch, Walter, Problem Solving with C++, Addison-Wesley Pub-
lishing Company, Inc 1996.
3. Bronson, Gary, Program Development and Design Using C++,
PWS Publishing Company, 1997.
74
املصفوفات واألصناف الوحدة الثالثة
.2مصفوفات من الكائنات Arrays of Objects
كثير ًا ما تستخدم مصفوفات من الكائنات لتمثيل قائمة listمن األشياء املتجانسة (من النوع
نفسه) .فمثال ،نستخدم مصفوفة من الكائنات لتمثيل قائمة من الكتب أو الطلبة أو املوظفني
وما إلى ذلك من قوائم .فلتمثيل قائمة من الطلبة studentsاملسجلني في شعبة معينة من مادة
نعرف الصنف studentكما يلي: ما ،علينا أو ً
ال أن ّ
{class student
;long stno
;int csno
;]char StName[20
public:
;)(void initialize
};long get_stno() {return stno
};char* get_stname(){return StName
;}
يتضح من تعريف الصنف studentأن لكل طالب املتغيرات املنتمية التالية:
،stno .1يرمز إلى رقم الطالب.
،csno .2يرمز إلى عدد املواد التي أنهى الطالب دراستها.
،StName .3ميثل اسم الطالب (مصفوفة رمزية).
76
املصفوفات واألصناف الوحدة الثالثة
املتغيرات املنتمية فتبقى دون قيم ابتدائية) فتصبح اجلملة
;]student std[3
خاطئة ،وذلك ألنها ال تزود ذلك البناء بالعوامل الالزمة.
هنالك طريقتان صحيحتان لتعريف املصفوفة بوجود هذا البناء .إما أن نكتب
;}student std[3]={96010,97030,98060
وإما أن نكتب
;})student std[3]={student(96010),student(97030),student(98060
وكلتا اجلملتني تعطي الكائناتstd[0] :و ] std[1و ]std[2قيم ًا ابتدائية عن طريق استدعاء
)student(96010و )student(97030و ) student(98060على الترتيب .الحظ ،عزيزي
الدارس ،أن اجلملة األولى ما هي إال اختصار للجملة الثانية.
وبالطبع ،نستطيع أن نكتب بنائني آخرين ،فمثالً ،البناء
)student::student(long s,char* na,int cn
;{stno=s
;)strcpy(StName,na
};csno=cn
يستخدم إلعطاء كل متغير ينتمي إلى الصنف studentقيمة ابتدائية .الحظ ،عزيزي
الدارس ،استخدامنا للدالة strcpyاخلاصة بنسخ السالسل الرمزية إلعطاء املتغير StName
قيمة ابتدائية ،وعليه ،علينا أن نضع جملة التضمني (االحتواء)
> #include<string.hفي بداية البرنامج.
واجلملة التالية تبني كيفية استخدام هذا البناء في تعريف مصفوفة وإعطاء كل من عناصرها
قيمة ابتدائية.
;})student stu[2]={student(96100,»Ali»,5),student(97001,»Ahmad»,10
تعرف هذه اجلملة املصفوفة stuوالتي تتكون من عنصرين [stu [0و [stu [1حيث يعطى
العنصر (الكائن) األول قيمة ابتدائية عن طريق استدعاء (.student (96100, «Ali»,5
ويعطى العنصر الثاني قيمة ابتدائية عن طريق استدعاء ( .student ( 97001,«Ahmad»,10
تذكر ،عزيزي الدارس ،أنه ال بد من وضع مناذج prototypesلهذه الدوال البناءة داخل
تعريف الصنف studentوذلك ألنها دوال منتمية.
وتستخدم الكائنات املصفوفة بشكل فردي (أي عنصر ًا عنصراً) عبر الدوال املنتمية املعرفة
للصنف .studentفمثالً ،اجلملة
;)(st[5].initialize
77
املصفوفات واألصناف الوحدة الثالثة
تستدعي الدالة )( initializeاخلاصة بالصنف studentعلى الكائن السادس .أي أن هذه
الدالة ستقوم بقراءة قيم للمتغيرات املنتمية اخلاصة بالكائن السادس .ولقراءة قيم جلميع عناصر
املصفوفة املائة نستدعي الدالة )( initializeلكل من هذه الكائنات داخل جملة دوران كما يلي
)for(int I=0;I<100;I++
;)( st[I].initialize
وبالطبع ،نستطيع قراءة بيانات لـ nمن الطلبة ،حيث nعدد من الطلبة يدخله املستخدم
عند تنفيذ البرنامج ،كما يلي
;int n
;”)cout<<”Enter Number of Students (<=100
;cin>>n
)for(int I=0;I<n;I++
;)( st[I].initialize
ولطباعة أرقام الطلبة stnoنكتب
)for(int I=0;I<n;I++
;)( cout<<st[I].get_stno
الحظ ،عزيزي الدارس ،أن اجلملة )(st[0].get_stnoتسترجع رقم الطالب األول أي
قيمة املتغير املنتمي stnoاخلاص بالكائن ] ،st[0وأن اجلملة )( st[1].get_stnoتسترجع رقم
الطالب الثاني.
نشاط ()1
قم ،عزيزي الدارس ،باستخدام احلاسوب لتنفيذ تعريف الصنف studentوالدوال
املنتمية له (دون بنائني) ،وكتابة برنامج بسيط يعرف مصفوفة من الطلبة ومن ثم ،يقوم
بقراءة بيانات خاصة بـهم و طباعتها بطريقة مناسبة.
املصفوفات باعتبارها متغيرات منتمية
لقد رأينا ،عزيزي الدارس ،كيف ميكن أن حتتوي املصفوفات على كائنات ،والسؤال
اآلن :هل ميكن أن يحدث العكس؛ أي أن يحتوي كائن ما على مصفوفة باعتبارها متغير ًا
78
املصفوفات واألصناف الوحدة الثالثة
منتمياً؟ واجلواب ،بالطبع نعم .وقد سبق أن رأينا في تعريف الصنف studentمتغيرا منتميا هو
في الواقع مصفوفة من العناصر وذلك املتغير هو StNameالذي هو في حقيقة األمر مصفوفة
من األحرف.
وسنضيف اآلن إلى تعريف الصنف studentمصفوفة أخرى ،grades ،كمتغير منتم ميثل
عالمات الطالب في املواد التي أنهى دراستها .وسنضيف دالتني منتميتني ملعاجلة هذه املصفوفة
،average() .1تستخدم هذه الدالة حلساب معدل عالمات طالب ما (كائن).
،get_grade(int i) .2تستخدم هذه الدالة السترجاع العالمة iلطالب معني.
وتستخدم هذه الدالة حلساب معدل طالب معني فمثالً ،اجلملة التالية تستدعي)(average
حلساب معدل الطالب اخلامس في املصفوفة ،stحيث تخزن القيمة املسترجعة في املتغير .x
79
املصفوفات واألصناف الوحدة الثالثة
;)(double x=st[4].average
وتعرف الدالة )( get_gradeكما يلي:
)double student::get_grade(int i
{
)if (i<csno&& i>=0
;]return grades[i
else
;return -1
}
الحظ ،عزيزي الدارس ،أن هذه الدالة تقوم بالتأكد من أن رقم العالمة املطلوبة املمثلة
بالعامل iتقع بني 0و ) .(csno-1فإن حتقق هذا الشرط تسترجع لنا العالمة املطلوبة وإال فإنها
تعيد لنا القيمة 1-للداللة على أن رقم العالمة املطلوبة غير صحيح .فمثالً ،لطباعة العالمة
السادسة للطالب الثالث في املصفوفة stنستخدم اجلملة
;)cout<<st[2].get_grade(5
وبالطبع ،علينا تعديل الدالة )( initializeبحيث تقرأ أيضا عالمات الطالب وتخزنها في
املصفوفة املنتمية .gradesبحيث يصبح كما يلي
)(void student::initialize
{
;»cout<<»enter student›s number
;cin>>stno
;« cout<<»enter student›s name
;cin>>StName
;»cout<<»enter no. of courses taken by the student
;cin>>csno
)for(int i=0;i<csno;i++
;{cout<<»enter grade no.»<<i+1
;]cin>>grades[i
}
}
80
املصفوفات واألصناف الوحدة الثالثة
لقراءة العدد املطلوبfor أننا أضفنا إلى هذه الدالة جملة دوران، عزيزي الدارس،الحظ
.من العالمات
:واملثال التالي يوضح كيفية إدخال اسم وعالمات دارسني وإيجاد معدل كل منهما
#include<string.h>
#include<iostream.h>
#include<stdio.h>
class student{
long stno; int csno;
double grades[100];
char StName[20];
public:
double average();
void initialize();
long get_stno() {return stno;}
char* get_stname(){return StName;}
double get_grade(int i);
};
double student::average()
{ double sum=0.0;
for(int i=0;i<csno;i++)
sum=sum+grades[i];
return sum/csno;
}
double student::get_grade(int i)
{
if (i<csno&& i>=0)
return grades[i];
else
return -1;
}
void student::initialize()
{
cout<<”enter student’s number :”;
81
املصفوفات واألصناف الوحدة الثالثة
;cin>>stno
;”cout<<”enter student’s name :
;cin>>StName
;”cout<<”enter no. of courses taken by the student :
;cin>>csno
)for(int i=0;i<csno;i++
;){cout<<”enter grade no.”<<(i+1
;]cin>>grades[i
}
}
)(main
;]{ student stu[2
)for(int i=0;i<2;i++
;)(stu[i].initialize
)for( int i=0;i<2;i++
;cout<<stu[i].get_stname()<<’\t’<<stu[i].average()<<endl
}
تدريب ()1
اكتب ،عزيزي الدارس ،برنامج ًا )( mainيعرف مصفوفة من الطلبة ،ثم يقوم بقراءة
البيانات اخلاصة بهؤالء الطلبة ثم طباعة اسم كل منهم ومعدل عالماته بشكل مناسب.
تدريب ()2
اكتب ،عزيزي الدارس ،دالة منتمية للصنف studentالسترجاع أعلى عالمة
لطالب ما .واكتب برنامج ًا )( mainالستخدام هذه الدالة.
82
املصفوفات واألصناف الوحدة الثالثة
.3الدوال ومصفوفات الكائنات
واآلن ،كيف ميكننا مترير مصفوفة من الكائنات arrays of objectsلدالة معينة
لترتيبها ترتيب ًا تصاعدياً ،على سبيل املثال؟ ميكن مترير املصفوفة إلى دالة معينة عبر العنوان
addressاخلاص بأول عنصر في املصفوفة واملمثل باسم املصفوفة مجردا من أي مؤشر.
وهذا ينطبق متام ًا على املصفوفات املكونة من كائنات .arrays of objectsعلى سبيل
املثال ،الدالة التالية جتد لنا معدل معدالت الطلبة في الشعبة (املصفوفة) كاملة (الحظ أن
هذا يختلف عن معدل عالمات جميع الطلبة في الشعبة).
)double SectionAv(student* ar, int n
;{ double sum=0
)for(int i=0;i<n;i++
;)(sum+=ar[i].average
;return sum/n
}
من املهم أن تالحظ ،عزيزي الدارس ،ما يلي:
.1أن هذه الدالة غير منتمية للصنف studentوعليه ،يجب عليها أن تتعامل
مع الكائنات من الصنف studentمن خالل الدوال املعرفة عليها مثل الدالة
)(.average
.2أن العامل arميثل عنوان addressلكائن من نوع student؛ إذ إننا سنستخدمه
لتمرير عنوان أول عنصر في املصفوفة.
.3عندما نستدعي هذه الدالة فإننا نرسل اسم املصفوفة مجرد ًا من أي مؤشر وعدد
الطلبة في املصفوفة .فمثالً ،إليجاد معدل معدالت الطلبة في املصفوفة stuالتي
حتتوي على 40طالب ًا فإننا نستدعي الدالة SectionAvكما في اجلملة
)double x=SectionAv(stu,40
83
املصفوفات واألصناف الوحدة الثالثة
•الفرز االنتقائي Selection Sort
سنستخدم في مثالنا األول خوارزمية الفرز االنتقائي ،Selection Sortوهي خوارزمية
بسيطة جد ًا تقوم على فكرة إيجاد أقل قيمة ثم استبدالها بالقيمة املوجودة في بداية املصفوفة ،ثم
تكرار العملية إليجاد ثاني أصغر قيمة في املصفوفة ثم استبدالها بالقيمة الثانية في املصفوفة .وهكذا
نكرر اخلطوات السابقة حتى نضع n-1من العناصر في مكانها الصحيح ،عندها تتوقف اخلوارزمية
(الحظ ،عزيزي الدارس ،أن العنصر رقم nيكون أيض ًا في املكان الصحيح في هذه احلالة).
في املرة األولى التي فيها نقوم بالبحث عن أقل قيمة ،نبدأ من العنصر األول في املصفوفة.
ولكي نضمن أننا سنجد ثاني أقل قيمة في املصفوفة في املرة التالية فإننا نبدأ البحث ابتداء من
العنصر الثاني في املصفوفة ،وفي املرة الثالثة نبدأ البحث ابتداء من العنصر الثالث وهكذا…
)void sort(student* st, int n
){ for(int i=0;i<n-1;i++
{ // find the ith minimum starting the search from the ith
// location
;)(long min=st[i].get_stno
;int pos=i
)for(int j=i+1; j<n;j++
)if (st[j].get_stno()<min
;)({min=st[j].get_stno
};pos=j
// swap the minimum with the ith element
;]student a=st[pos
;]st[pos]=st[i
;st[i]=a
} //for i
}
إن الدالة sortحتتوي على جملتي دوران forاألولى تتكرر n-1من املرات وتتكرر داخلها
عمليتان رئيستان:
.1عملية إيجاد أقل قيمة ،ويبدأ البحث في كل مرة من املوقع iالذي يزداد مبقدار
1في كل دورة ،وحتديد مكان (موقع) تلك القيمة حيث يستخدم املتغير pos
لالحتفاظ مبكان أقل قيمة.
84
املصفوفات واألصناف الوحدة الثالثة
.2ثم يتم استبدال الطالب صاحب أقل رقم (أي[ )st [posمع العنصر .]st] i
وتتم عملية إيجاد الطالب صاحب أقل رقم ومكانه كما يلي:
نفترض أن الطالب ]st] iهو صاحب أقل رقم
;)(long min=st[i].get_stno
وعليه ،فإن موقع posالطالب صاحب أقل قيمة هو iأي أن
;int pos=i
واآلن ،علينا مقارنة أقل قيمة لرقم الطالب minبأرقام باقي الطلبة ،وفي كل مرة جند طالب ًا
برقم أقل من minجنعل minتساوي هذه القيمة ونحتفظ مبكان الطالب ذي أقل رقم باستخدام
املتغير .posوجملة الدوران الداخلية تقوم بهذه املهمة
)for(int j=i+1; j<n;j++
)if (st[j].get_stno()<min
;)( {min=st[j].get_stno
}; pos=j
أما عملية تبديل موقع الطالب ذي أقل رقم ] st[posمع العنصر ]st[iفتتم من خالل اجلمل
;]student a=st[pos
;]st[pos]=st[i
;st[i]=a
واملثال التالي يوضح كيفية استخدام االقتران sortلترتيب العناصر في مصفوفة من النوع
.student
>#include<string.h
>#include<iostream.h
>#include<stdio.h
{class student
;long stno
;int csno
;]double grades[100
;]char StName[20
public:
;)void sort(student *st, int n
85
املصفوفات واألصناف الوحدة الثالثة
void initialize();
long get_stno() {return stno;}
char* get_stname(){return StName;}
double get_grade(int i);
};
double student::get_grade(int i)
{
if (i<csno&& i>=0)
return grades[i];
else
return -1;
}
void student::initialize()
{
cout<<”enter student’s number :”;
cin>>stno;
cout<<”enter student’s name :”;
cin>>StName;
cout<<”enter no. of courses taken by the student :”;
cin>>csno;
for(int i=0;i<csno;i++)
{cout<<”enter grade no.”<<(i+1)<<” :”;
cin>>grades[i];
}
}
void sort(student* st, int n)
{ for(int i=0;i<n-1;i++)
{ // find the ith minimum starting the search from the ith
// location
long min=st[i].get_stno();
int pos=i;
for(int j=i+1; j<n;j++)
if (st[j].get_stno()<min)
86
املصفوفات واألصناف الوحدة الثالثة
{min=st[j].get_stno();
pos=j;}
// swap the minimum with the ith element
student a=st[pos];
st[pos]=st[i];
st[i]=a; } //for i
}
main()
{ student stu[50];
int n;
cout<<”Enter no. of students :”;
cin>> n;
for(int i=0;i<n;i++ )
stu[i].initialize();
sort(stu,n);
cout<<” The student List after sorting : “ <<endl;
for(int k=0; k<n;k++)
cout<<stu[k].get_stno() <<endl;
}
)2( نشاط
بنفسك (على الورق) اخلوارزمية أعاله لترتيب قائمة، عزيزي الدارس،تتبع
:من الطلبة على افتراض أن أرقامهم هي
.5 ، 15 ، 1 ، 7 ، 10
87
املصفوفات واألصناف الوحدة الثالثة
تدريب ()3
اكتب ،عزيزي الدارس ،دالة لترتيب مصفوفة من الطلبة حسب الترتيب األبجدي
ألسماء الطلبة .الحظ ،أنك تستطيع استخدام الدالة strcmpلتقارن بني السالسل
ضمن برنامجك اجلملة >.#include<string.h الرمزية على أن ُت ّ
تدريب ()4
عدل البرنامج أعاله بحيث يقوم بترتيب املصفوفة بشكل تنازلي بدال من تصاعدي.
89
املصفوفات واألصناف الوحدة الثالثة
وذلك كي تستمر عملية البحث في املرحلة التالية في النصف الثاني من اجلزء الذي نبحث به.
وإال فإن قيمة hiتصبح mid-1وذلك كي تستمر عملية البحث في النصف األول من اجلزء
الذي نبحث به.
وتتوقف عملية البحث في حالتني:
.1في حالة إيجاد الطالب الذي نبحث عنه ،وفي هذه احلالة تعيد الدالة قيمة .mid
.2في حالة انتهاء جملة دوران whileألن قيمة loأصبحت أكبر من قيمة ،hiوهذا
ممكن أن يحصل فقط إذا لم جند العنصر الذي نبحث عنه داخل املصفوفة .وفي هذه
احلالة فإن الدالة تعيد القيمة اخلاصة .1-
واملثال التالي ،عزيزي الدارس ،يوضح استخدام خوارزمية البحث الثنائي للبحث
عن العدد 6في املصفوفة التالية,102 ,78 ,46 ,25 ,19 ,18 ,6 ,5 ,-1{ :
.}114والتي هي مرتبة تصاعديا من اليسار إلى اليمني.
(في اجلولة األولى العدد الواقع في املنتصف )6 < 19
-1 5 6 18 19 25 46 78 102 114
(في اجلولة الثانية العدد الواقع في املنتصف )6 > 5
-1 5 6 18 19 25 46 78 102 114
(في اجلولة الثالثة العدد الواقع في املنتصف )6 = 6
-1 5 6 18 19 25 46 78 102 114
وميكن أن نكتب البرنامج التالي ،عزيزي الدارس ،الذي يستخدم خوارزمية البحث
الثنائي للبحث فيما إذا كان رقم الدارس موجود ًا أم ال ,وفي حالة وجود رقم الدارس
يطبع لنا البرنامج موقع الرقم في املصفوفة املرتبة تصاعدياً:
>#include<string.h
>#include<iostream.h
>#include<stdio.h
{class student
;long stno
;int csno
;]double grades[100
;]char StName[20
public:
;)void sort(student *st, int n
;)int bsearch(student* st,int n, long sn
90
املصفوفات واألصناف الوحدة الثالثة
void initialize();
long get_stno() {return stno;}
char* get_stname(){return StName;}
};
void student::initialize()
{
cout<<”enter student’s number :”;
cin>>stno;
cout<<”enter student’s name :”;
cin>>StName;
}
void sort(student* st, int n)
{ for(int i=0;i<n-1;i++)
{ // find the ith minimum starting the search from the ith
// location
long min=st[i].get_stno();
int pos=i;
for(int j=i+1; j<n;j++)
if (st[j].get_stno()<min)
{min=st[j].get_stno();
pos=j;}
// swap the minimum with the ith element
student a=st[pos];
st[pos]=st[i];
st[i]=a; } //for i
}
// binary search
int bsearch(student* st,int n, long sn){
int lo=0;
int hi=n-1;
while (lo <= hi)
{ int mid =(lo+hi) /2;
long r=st[mid].get_stno();
if (r== sn)
return mid;
91
املصفوفات واألصناف الوحدة الثالثة
else
)if (sn>r
;lo=mid+1
else
;hi = mid-1
} //while
;return -1
}
)(main
;]{ student stu[50
;int n,sn, st
;”cout<<”Enter no. of students :
;cin>> n
) for(int i=0;i<n;i++
;)(stu[i].initialize
;)sort(stu,n
;”cout<<”Enter the student number :
;cin>>sn
;)st = bsearch(stu, n, sn
;cout<<st
}
وباالطالع على النتيجة ،يتضح أنه مت إدخال معلومات عن ثالثة دارسني وطلب
البحث عن الدارس رقم .33وكانت النتيجة أن حدد البرنامج موقع الدارس في
املصفوفة املرتبة تصاعدي ًا وهو .1
92
املصفوفات واألصناف الوحدة الثالثة
نشاط ()3
تتبع ،عزيزي الدارس ،بنفسك (على الورق ودون استخدام احلاسوب)
الدالة bsearchللبحث عن الطالب ذي الرقم 13في قائمة من الطلبة حتمل
األرقام التالية:
20 ، 17 ، 15 ، 14 ، 13 ، 12 ، 10 ، 7 ، 5 ، 2
نشاط ()4
تتبع ،عزيزي الدارس ،بنفسك (على الورق ودون استخدام احلاسوب) الدالة
bsearchللبحث عن الطالب ذي الرقم 6في قائمة من الطلبة حتمل األرقام
التالية:
20 ، 17 ، 15 ، 14 ، 13 ، 12 ، 10 ، 7 ، 5 ، 2
الحظ أن هذا الطالب غير موجود في القائمة وعليه فإن اخلوارزمية تتوقف عندما
تصبح قيمة loأكبر من قيمة .hi
تدريب ()5
اكتب ،عزيزي الدارس ،دالة تستخدم خوارزمية البحث الثنائي للبحث عن طالب
ذي اسم معني في مصفوفة من الطلبة مرتبة ترتيب ًا تصاعدي ًا حسب أسماء الطلبة
تدريب ()6
عدل الدالة السابقة (التي كتبت في التدريب )5بحيث تعمل على مصفوفة
من الطلبة مرتبة ترتيب ًا تنازلياً.
93
املصفوفات واألصناف الوحدة الثالثة
.4احتواء مصفوفة من الكائنات في صنف
وعاء لكائنات objectsمن صنف معني ً لقد رأينا كيف ميكن أن نستخدم املصفوفة
( studentفي مثالنا أعاله) وننفذ بعض العمليات مثل عملية الترتيب والبحث ،وفي هذا
القسم سنرى كيف ميكن أن نعرف هذا الوعاء باعتباره صنف ًا classسندعوه sectionأي شعبة.
أي أن الصنف اجلديد سيحتوي على مصفوفة من الكائنات باإلضافة إلى الدوال الالزمة ملعاجلة
هذه املصفوفة .وعلى سبيل املثال ،على ذلك سنعرف الصنف ،sectionحيث ميثل كل كائن
objectمن هذا الصنف شعبة من الطلبة في مادة ما.
{class section
;]student sec[50
;int size
;)int bsearch(long stno
public:
};section(){size=0
;)void StAdd(student s
;)int StDelete(long stno
;)(void Stlist
;)void StRetrieve(long stno
;}
94
املصفوفات واألصناف الوحدة الثالثة
القسم اخلاص privateمن تعريف الصنف مما يجعل استدعاءها ممكن ًا فقط عبر بقية
الدوال املنتمية ،فال نستطيع استدعاءها مباشرة عبر )( mainعلى سبيل املثال .الحظ
أيضاً ،عزيزي الدارس ،أن عوامل هذه الدالة قد اختلفت عن عوامل bsearchالتي
كتبت في القسم السابق؛ إذ ال نحتاج لتمرير املصفوفة نفسها وال حجم املصفوفة،
ألن هذه الدالة منتمية ،وهي من ثم ،تستطيع معاجلة secو sizeمباشرة.
// binary search
)int section::bsearch(long sn
{
;int lo=0
;int hi=size-1
)while (lo <= hi
;{ int mid =(lo+hi) /2
;)(long r=sec[mid].get_stno
)if (r== sn
;return mid
else
)if (sn>r
;lo=mid+1
else
;hi = mid
} //while
;return -1
}
،StAdd(student s) .3تقوم هذه الدالة بإضافة طالب جديد ،s ،إلى املصفوفة sec
في املوقع املناسب بحيث تبقى قائمة الطلبة مرتبة تصاعدي ًا حسب أرقام الطلبة.
وتقوم هذه الدالة بإجراء أربع خطوات رئيسة هي:
•إيجاد موقع إضافة العنصر اجلديد ،ويتم ذلك من خالل جملة دوران .while
•عملية إزاحة shiftللعناصر ،وذلك إلفراغ موقع اإلضافة ويتم ذلك من خالل
جملة الدوران ،forالتي تبدأ بآخر عنصر وتنقله إلى أول موقع شاغر في
املصفوفة ،ثم تنتقل إلى العنصر قبل األخير وتنقله إلى املوقع السابق للعنصر
95
املصفوفات واألصناف الوحدة الثالثة
ال في موقع اإلضافة .والشكل األخير وهكذا إلى أن نحرك العنصر املوجود أص ً
1يوضح هذه العملية:
•عملية إضافة العنصر في املوقع املخصص .pos
•ثم عملية تعديل حجم املصفوفة sizeبزيادة 1إلى القيمة السابقة.
،StDelete(long stno) .4تقوم هذه الدالة بشطب طالب معني ذي الرقم stnoاملعطى
باعتباره عام ً
ال للدالة .تستخدم هذه الدالة bsearchلتحديد موقع العنصر املراد
ال الطالبشطبه .وقبل عملية الشطب الفعلية نتأكد من أن bsearchقد وجد فع ً
املراد حذفه (تذكر بأن bsearchيرجع القيمة 1-إذا لم يجد العنصر املطلوب).
وتتم عملية احلذف بإزاحة العناصر الواقعة في املصفوفة بعد العنصر املراد حذفه
96
املصفوفات واألصناف الوحدة الثالثة
موقع ًا واحد ًا إلى اليسار والشكل 2يوضح هذه العملية .وتعدل قيمة sizeبطرح
.1تعيد هذه الدالة قيمة صحيحة أكبر من 1-لإلشارة إلى أن عملية الشطب متت
بنجاح .أما إذا لم تتم عملية الشطب بنجاح (ألن الطالب لم يكن موجود ًا أصالً)
فإن القيمة اخلالصة 1-تعاد إلى الدالة املستدعية.
Stlist()، .5تقوم هذه الدالة بطباعة قائمة بأسماء الطلبة املوجودين في الشعبة ،باإلضافة
إلى بعض املعلومات عن كل منهم.
)(void section::Stlist
){ for(int i=0;i<size;i++
« »<<)(cout<<sec[i].get_stname()<<» «<<sec[i].get_stno
;<<sec[i].average()<<endl
}
الحظ ،عزيزي الدارس ،أن طباعة endlفي جملة coutتتسبب في االنتقال إلى
سطر جديد ،أي أن عملها هو عمل ’ ‘\nنفسه.
97
املصفوفات واألصناف الوحدة الثالثة
تقوم هذه الدالة بطباعة بعض املعلومات اخلاصة بالطالب،StRetrieve(long stno) .6
لتحديد موقع الطالب فيbsearch وتستخدم هذه الدالة.stno صاحب الرقم
.املصفوفة
void section::StRetrieve(long stno)
{int i=bsearch(stno);
if(i>-1)
{
cout<<”Student infromation \n \t”;
cout<<sec[i].get_stname()<<” “<<sec[i].get_stno()<<” “
<<sec[i].average()<<endl;
}
else
cout<<”Student no “<<stno<<” was not found \n”;
}
99
املصفوفات واألصناف الوحدة الثالثة
public:
void sort(student *st, int n);
int bsearch(student* st,int n, long sn);
double average();
void initialize();
long get_stno() {return stno;}
char* get_stname(){return StName;}
};
void student::initialize()
{
cout<<”enter student’s number :”;
cin>>stno;
cout<<”enter student’s name :”;
cin>>StName;
cout<<”enter no. of courses taken by the student :”;
cin>>csno;
for(int i=0;i<csno;i++)
{cout<<”enter grade no.”<<(i+1);
cin>>grades[i];
}
}
double student::average()
{ double sum=0.0;
for(int i=0;i<csno;i++)
sum=sum+grades[i];
return sum/csno;
}
// binary search
int bsearch(student* st,int n, long sn){
int lo=0;
int hi=n-1;
while (lo <= hi)
{ int mid =(lo+hi) /2;
long r=st[mid].get_stno();
if (r== sn)
return mid;
else
if (sn>r)
lo=mid+1;
else
hi = mid-1;
} //while
return -1;
}
// Class Section
// ---------------------------------------------------------------
class section{
student sec[50];
int size;
int bsearch(long stno);
public:
101
املصفوفات واألصناف الوحدة الثالثة
section(){size=0;}
void StAdd(student s);
int StDelete(long stno);
void Stlist();
void StRetrieve(long stno);
};
// binary search
int section::bsearch(long sn) { int lo=0;
int hi=size-1;
while (lo <= hi)
{ int mid =(lo+hi) /2;
long r=sec[mid].get_stno();
if (r== sn)
return mid;
else
if (sn>r)
lo=mid+1;
else
hi = mid;
} //while
return -1;
}
void section::StAdd(student s) {int pos=0; // search for the proper inser-
tion position
while(s.get_stno() > sec[pos].get_stno() && pos<size) pos++;
// shift element up one position
for(int i=size-1;i>=pos;i--)
sec[i+1]=sec[i];
// insert element
sec[pos]=s;
// modify size
size++; }
102
املصفوفات واألصناف الوحدة الثالثة
int section::StDelete(long stno)
{int i=bsearch(stno);
if(i>-1)
{for(int j=i;j<size-1;j++)
sec[j]=sec[j+1];
size--;
}
return i;
}
void section::Stlist()
{ for(int i=0;i<size;i++)
cout<<sec[i].get_stname()<<» «<<sec[i].get_stno()<<» «
<<sec[i].average()<<endl; }
void section::StRetrieve(long stno)
{int i=bsearch(stno);
if(i>-1)
{
cout<<»Student infromation \n \t»;
cout<<sec[i].get_stname()<<» «<<sec[i].get_stno()<<» «
<<sec[i].average()<<endl;
}
else
cout<<»Student no «<<stno<<» was not found \n»;
}
// ---------------------------------------------------------------
main()
{ section cs100;
int choice;
do
{cout<<»\n\n \t Enter 1 to add a new student \n»;
cout<<»\t Enter 2 to delete a student \n»;
cout<<»\t Enter 3 to some information about a student \n «;
103
املصفوفات واألصناف الوحدة الثالثة
cout<<»\t Enter 4 to get a list of all students \n»;
cout<<»\t Enter 5 to Exit \n»;
cin>>choice;
switch(choice){
case 1: // student add
student s;
s.initialize();
cs100.StAdd(s);
break;
case 2: // delete a student
long sn;
cout<<»Enter student number «;
cin>>sn;
cs100.StDelete(sn);
break;
case 3: // retrieve some data about a student
cout<<»Enter student number «;
cin>>sn;
cs100.StRetrieve(sn);
break;
case 4: // list information about all students
cout<<»Students information \n\n»;
cs100.Stlist();
break;
case 5: cout<<»End of program \n”;
break;
default: cout<<”invalid choice ... Try again “;
}}
while(choice!=5);
}
وبعد تنفيذ البرنامج سوف يعرض لنا قائمة من اخليارات التي ميكن عبرها أن نضيف
معلومات عن دارس أو االستفسار عن معلومات دارس أو حذف معلومات دارس أو
104
املصفوفات واألصناف الوحدة الثالثة
غيرها من األوامر املتوافرة كما هو موضح في النتيجة التالية (ميكنك تنفيذ نشاط ()5
بكتابة البرنامج السابق):
نشاط ()5
قم ،عزيزي الدارس ،باستخدام احلاسوب لتنفيذ البرنامج أعاله باإلضافة إلى
تعريف الصنف studentوالصنف sectionودوالهم املنتمية .تذكر ،أن تعريف
الصنف studentيجب أن يأتي قبل تعريف الصنف .section
تدريب ()7
أضف ،عزيزي الدارس ،الدالة sortAvإلى الصنف sectionباعتبارها دالة منتمية
جديدة تقوم بترتيب (فرز) مصفوفة الطلبة ترتيب ًا تنازلي ًا حسب معدل عالمات الطلبة ،ثم
طباعة قائمة باسم الطالب ومعدل عالماته.
105
املصفوفات واألصناف الوحدة الثالثة
.5املؤشرات واألصناف Pointers and Classes
تعلم ،عزيزي الدارس ،أن اجلملة
;int *p
تعرف املتغير pعلى أنه مؤشر pointerإلى موقع في الذاكرةmemory location
مخصص لألرقام الصحيحة ،مما يعني أننا نستطيع تخزين عنوان addressمتغير صحيح في
املتغير .pعلى سبيل املثال ،اجلمل
;int k=10
;p=&k
تعرف املتغير kعلى أنه من النوع .intواجلملة الثانية تخزن عنوان املتغير kفي املتغير املؤشر
.pوالشكل ( )3يوضح بالرسم هذا األمر ،مما يسمح لنا بالتعامل مع املتغير kعبر اسمه ،k ،أو
عبر عنوانه املخزن في املتغير املؤشر .pإذ إن *pتعني املتغير املشار إليه من طرف املتغير املؤشر .p
10
K
P
الشكل ( :)3املتغير Pيحمل عنوان املتغير Kأي أنه يشير إليه.
ونستطيع استخدام املؤشرات مع التراكيب واألصناف بالطريقة نفسها .على سبيل املثال،
لنعرف التركيب Bookكما يلي:
{struct Book
;] char author[20
;] char title[20
; int pages
; float price
;}
106
املصفوفات واألصناف الوحدة الثالثة
واآلن نستطيع تعريف املتغير املؤشر qعلى أنه مؤشر لتراكيب structuresمن نوع Book
كما يلي:
;Book *q
مما يعني أننا نستطيع تخزين عنوان لتركيب من نوع Bookداخله .لنعرف التركيب
BK1
;Book BK1
واآلن نستطيع تخزين عنوان BK1في qكما يلي
;q=&BK1
وعليه ،فإننا نستطيع التعامل مع التركيب BK1عبر اسمه أو عبر التعبير .*q
تستطيع ،عزيزي الدارس ،أن تعتبر *qلقبا aliasآخر لـ .BK1فمثالً ،نستطيع تخزين
القيمة 15.5في احلقل (أو املتغير املنتمي) priceاخلاص بالتركيب BK1باستخدام اجلملة
;BK1.price=15.5
أو من خالل اجلملة
;(*q).price=15.5
إذ إن كلتا اجلملتني تقومان بالعمل نفسه.
من املهم أن تدرك ،عزيزي الدارس ،أن األقواس في التعبير (*q).priceضرورية ألن
عملية النقطة لها أولوية (أسبقية) priorityأعلى من أولوية العملية*.
ولتبسيط التعبير (*q).priceنستطيع أن نستخدم ما يسمى بعملية السهم arrow
operator؛ إذ إن q->priceمكافئة متاما لـ (*q).price؛ أي أننا نستطيع أن نكتب اجلملة
التالية بدي ً
ال للجملتني السابقتني
;q->price=15.5
الحظ أن هذه اجلملة تعرف املتغير p1على أنه مؤشر للنوع studentوتعطيه عنوان الكائن
107
املصفوفات واألصناف الوحدة الثالثة
St1باعتباره قيمة ابتدائية .وعلينا مراعاة كون املتغيرات املنتمية للصنف studentمتغيرات
خاصة (معرفة في القسم اخلاص) ،لذا فإننا ال نستطيع التعامل معها عبر املتغير املؤشر إال داخل
الدوال املنتمية .على سبيل املثال ،ال نستطيع أن نستخدم اجلملة التالية داخل main
;(*p1).stno=123
والشيء نفسه ينطبق على اجلملة
;p1->stno=333
ولكننا نستطيع أن نستدعي دالة منتمية للصنف studentبوساطة املتغير املؤشر على أن
تكون تلك الدالة معرفة في القسم العام publicمن الصنف .فمثال ،نستطيع استدعاء الدالة
averageحلساب معدل عالمات الكائن St1من خالل املؤشر p1كما يلي:
;)((*p1).average
أو باستخدام عملية السهم كما يلي
;)(p1->average
ونستطيع استدعاء الدالة )( get_stnameعبر املتغير املؤشر p1لطباعة اسم الطالب كما
يلي
;)(cout<<(*p1).get_stname
أو
;)(cout<<p1->get_stname
108
املصفوفات واألصناف الوحدة الثالثة
.6حجز الذاكرة بطريقة ديناميكية
Dynamic Memory Allocation
هنالك طريقتان حلجز الذاكرة لكائن ما :الطريقة الستاتيكية والطريقة الديناميكية.
تقضي الطريقة األولى بأن يقوم مترجم لغة C++بحجز الذاكرة ،عند ترجمة البرنامج،
جلميع املتغيرات املستخدمة في البرنامج وتبقى هذه الذاكرة محجوزة للبرنامج طوال
فترة التنفيذ حتى وإن لم تستخدم ،ولهذا تسمى هذه الطريقة بالطريقة الستاتيكية .وقد
استخدمنا هذه الطريقة في جميع برامج هذه الوحدة لغاية اآلن.
أما الطريقة الديناميكية فتسمح لنا بحجز الذاكرة التي نحتاج عند احلاجة إليها في أثناء
التنفيذ .وعند انتهاء احلاجة إليها ،نستطيع حتريرها بحيث تستخدم ألغراض أخرى.
تتم عملية احلجز في لغة C++عبر الدالة newوتتم عملية التحرير عبر الدالة .delete
على سبيل املثال ،حلجز موقع لعدد صحيح نستخدم الدالة newكما يلي
;int *p
;p=new int
تقوم اجلملة األولى بتعريف pباعتباره متغير ًا مؤشر ًا لقيمة صحيحة .وتقوم اجلملة
الثانية بحجز موقع في الذاكرة لعدد صحيح وتخزن عنوان هذا املوقع في املتغير املؤشر .p
والشكل ( )4يوضح هذا األمر .ومن املمكن اختصار هاتني اجلملتني بجملة واحدة هي
;int *p=new int
*P
P
الشكل ( P :)4هو املتغير املؤشر بينما * Pهو املوقع املشار إليه.
والطريقة الوحيدة للتعامل مع هذا املوقع احملجوز هي عبر املتغير املؤشر .pإذ إن
التعبير *pيعود على هذا املوقع احملجوز .لندرس اجلمل التالية
109
املصفوفات واألصناف الوحدة الثالثة
;*p=10
;*p=*p+5
;cout<<*p
اجلملة األولى تقوم بتخزين العدد 10في املوقع *pواجلملة الثانية تقوم بزيادة قيمة
*pبـ 5واجلملة الثالثة تعرض قيمة *pعلى الشاشة وهي طبع ًا القيمة .15
وبطريقة مشابهة ،نستطيع حجز ذاكرة لكائن من صنف ما .فمثالً ،حلجز ذاكرة لكائن من
الصنف studentنستخدم اجلملة
;student *q=new student
وهذه اجلملة في الواقع تقوم بعمل اجلملتني
;student *q
;q=new student
حيث تعرف املتغير qعلى أنه مؤشر لكائن من الصنف studentوحتجز موقع ًا لكائن
من هذا الصنف وتخزن عنوانه في املتغير .qوهذه اجلملة صحيحة فقط في حالة عدم
وجود بناء للصنف studentأو في حالة وجود بناء ال يحتاج إلى عوامل .أما في حالة
وجود بناء يأخذ عوامل فال بد من تزويد هذا البناء بقيم لهذه العوامل عند حجز ذاكرة
الكائن.
على سبيل املثال ،في حالة وجود البناء
)student::student(long s,char* na,int cn
;{stno=s
;)strcpy(StName,na
};csno=cn
مما يؤدي إلى إعطاء املتغيرات املنتمية للكائن احملجوز stnoو StNameو cnالقيم
96100و ” “Ahmad Aliو 10على الترتيب.
وبالطبع ،نستطيع استخدام هذا الكائن من خالل املتغير املؤشر qوالدوال املعرفة
على الصنف studentكما في اجلمل
;)(cout<<(*q).get_stname
110
املصفوفات واألصناف الوحدة الثالثة
;)(cout<<q->get_stno
وعند انتهاء احلاجة للكائن احملجوز نستطيع حترير الذاكرة املخصصة له باستخدام الدالة
deleteمما يعطي الفرصة لنظام التشغيل الستخدام هذه الذاكرة ألمور أخرى .على سبيل
املثال ،اجلملة
;delete q
حترر الذاكرة احملجوزة للكائن الذي يشير إليه املتغير املؤشر .ومن اجلدير بالذكر أنه في
حالة وجود هدام destructorللصنف studentفإن هذا الهدام يستدعى تلقائياً.
ومن اجلدير بالذكر أن الكائن الذي يحجز بطريقة ديناميكية يبقى في الذاكرة إلى أن نستخدم
الدالة deleteلتحرير الذاكرة اخلاصة به أو إلى أن ينتهي البرنامج.
لندرس ،عزيزي الدارس ،املثال التالي الذي يستخدم املؤشرات ,حيث يقوم البرنامج
باإلعالن عن صنف املسافة Distanceكما يلي:
class Distance //English Distance class
{
private:
;int feet
;float inches
public:
void getdist() //get length from user
{
;cout << «\nEnter feet: «; cin >> feet
;cout << «Enter inches: «; cin >> inches
}
void showdist() //display distance
} ;»\‹ << { cout << feet << «\›-» << inches
;}
وكما نرى من املثال ،فإن الصنف مسافة يحتوي الدوال املنتمية التي تقوم بإدخال وحدات
املسافة بالقدم والبوصة وطباعتها .وميكنا كتابة الدالة الرئيسية التي تقوم بتعريف الصنف
واستدعاء الدوال وتنفيذها .وكما تالحظ فإنه مت تعريف distptrمؤشر ًا كما يلي:
;Distance* distptr مؤشر من نوع الصنف // Distance
;distptr = new Distance //points to new Distance object
111
املصفوفات واألصناف الوحدة الثالثة
: كما يليDistance ومت استدعاء الدوال من نوع مؤشر للصنف
distptr->getdist(); //access object members
distptr->showdist(); // with -> operator
:ًواملثال التالي ميثل البرنامج كامال
class Distance //English Distance class
{
private:
int feet;
float inches;
public:
void getdist() //get length from user
{
cout << “\nEnter feet: “; cin >> feet;
cout << “Enter inches: “; cin >> inches;
}
void showdist() //display distance
{ cout << feet << “\’-” << inches << ‘\”’; }
};
int main()
{
Distance dist; //define a named Distance object
dist.getdist(); //access object members
dist.showdist(); // with dot operator
Distance* distptr; //pointer to Distance
distptr = new Distance; //points to new Distance object
distptr->getdist(); //access object members
distptr->showdist(); // with -> operator
cout << endl;
return 0;
}
class CSquare
{
public:
double Side;
CSquare() { Side=0.00;}
CSquare(double side) {Side=side;}
~CSquare() { }
int main()
113
املصفوفات واألصناف الوحدة الثالثة
{
CSquare *sqr[4];
114
املصفوفات واألصناف الوحدة الثالثة
• حجز املصفوفات بطريقة ديناميكية Dynamic Allocations of Arrays
إن حجز املصفوفات بطريقة ستاتيكية كما فعلنا لغاية اآلن يعني أن هذه املصفوفات
حتجز طوال وقت تنفيذ البرنامج حتى وإن لم تستخدم بشكل كامل ،مما قد يزيد من حجم
الذاكرة املطلوبة لتنفيذ البرنامج بشكل كبير .ومما يزيد في حجم املشكلة أننا ال نعرف
دائماً ،عند كتابة البرنامج ،حجم املصفوفة الفعلي الذي سنحتاجه عند تنفيذ البرنامج،
ولذلك فإننا قد نضطر إلى حجز مصفوفة كبيرة جد ًا حتسب ًا ألسوأ الظروف .فمثالً ،في
مثالنا اخلاص بتعريف الصنف ،sectionعرفنا املصفوفة secبحجم 50عنصراً.
وعند تنفيذ البرنامج قد ال يسجل 50طالب ًا في هذه الشعبة ومع ذلك ،ستبقى املصفوفة
محجوزة للبرنامج بشكل كامل طوال فترة تنفيذه.
واحلل يكمن في أن حتجز ذاكرة املصفوفة بطريقة ديناميكية باستخدام الدالة newوبحجم
مبدئي صغير ،وعند احلاجة إلى مصفوفة بحجم أكبر (ألن طالب ًا جديد ًا سجل في املادة ،على
سبيل املثال) نستطيع أن نحجز مصفوفة أكبر ونقل عناصر املصفوفة القدمية إليها ومن ثم ،حترير
الذاكرة اخلاصة باملصفوفة القدمية.
ولكن ،قبل ذلك ،دعنا نرى ،عزيزي الدارس ،كيف ميكن أن نحجز مصفوفة بطريقة
ديناميكية .وسندرس ذلك من خالل املثال البسيط التالي
;float *p
;]p=new float[50
تعرف اجلملة األولى املتغير املؤشر pعلى أنه مؤشر للنوع .floatوتقوم اجلملة الثانية
بحجز مصفوفة من نوع floatبحجم 50عنصراً .وتقوم أيضا بتخزين عنوان أول عنصر في
املتغير املؤشر .pوبالطبع ،نستطيع اختصار اجلملتني السابقتني في جملة واحدة هي
;]float *p=new float[50
واآلن نستطيع استخدام هذه املصفوفة اجلديدة عبر pكأي مصفوفة أخرى .فمثالً ،نستطيع
قراءة قيم لعناصر هذه املصفوفة كما يلي
)for(i=0;i<50;i++
;] cin>>p[i
ونستطيع كتابة عناصر هذه املصفوفة كل على سطر مستقل كما يلي
)for(i=0;i<50;i++
; cout<<p[i]<<endl
115
املصفوفات واألصناف الوحدة الثالثة
وعند انتهاء احلاجة لهذه املصفوفة نستطيع حترير الذاكرة احملجوزة لها بوساطة الدالة
deleteكما يلي
;delete p
نشاط ()6
استخدم ،عزيزي الدارس ،احلاسوب لتنفيذ برنامج صغير حلجز مصفوفة من نوع
intبطريقة ديناميكية ،ومن ثم ،قراءة عناصر هذه املصفوفة وطباعتها بشكل مناسب.
116
املصفوفات واألصناف الوحدة الثالثة
الحظ ،عزيزي الدارس ،االختالفات التالية في التعريف اجلديد لـ :section
املتغير املنتمي secليس مصفوفة هذه املرة ،بل إنه متغير مؤشر للصنف .student .1
ألننا سنستخدمه في حجز املصفوفة بطريقة ديناميكية لوضع عنوان هذه املصفوفة به.
هنالك متغير منتم جديد ،MaxSize ،في التعريف اجلديد ،وميثل هذا املتغير .2
حجم الذاكرة احملجوزة .الحظ ،عزيزي الدارس ،أن هذا املتغير يختلف عن املتغير
sizeالذي ميثل عدد الطلبة (الكائنات) الفعلي في املصفوفة (أي الذين أضفناهم
إلى املصفوفة) .وكما سنرى فإن بناء الصنف sectionيعطي هذا املتغير MaxSize
القيمة االبتدائية 20على أن تزداد هذه القيمة مبقدار الضعف عند احلاجة.
إن بناء الصنف sectionاجلديد يختلف كثير ًا عن السابق .فهو يقوم بحجز مصفوفة .3
من نوع studentوبحجم 20عنصر ًا ويخزن عنوان أول عنصر في هذه املصفوفة في
املتغير .secكما يعطي املتغيرات املنتمية MaxSizeو sizeالقيم 20و 0على الترتيب.
هنالك أيضا دالة منتمية جديدة )( expandتستدعى عند احلاجة إلى توسعة املصفوفة .4
احملجوزة .وتستدعى هذه الدالة من جانب الدالة StAddعند محاولة إضافة عنصر
جديد واكتشاف أن املصفوفة احملجوزة ال تتسع للمزيد من العناصر .تقوم الدالة
expandبحجز مصفوفة من النوع studentولكن بحجم مقداره ،2*MaxSizeثم
تقوم بنسخ عناصر املصفوفة القدمية إلى املصفوفة اجلديدة .بعدئذ ،تقوم بتحرير الذاكرة
احملجوزة للمصفوفة القدمية ،ثم جتعل املتغير secيشير إلى املصفوفة اجلديدة ،وتعدل
قيمة MaxSizeبحيث تصبح ضعف القيمة السابقة .ويتم ذلك بلغة C++كما يلي:
)(void section::expand
;]{student *p=new student[2*MaxSize
)for(int i=0;i<size;i++
;]p[i]=sec[i
;delete sec
;sec=p
;MaxSize *=2
}
.5كما نحتاج إلى تعديل الدالة StAddبحيث تستدعي الدالة expandعند احلاجة؛ أي
إذا كان عدد الطلبة الفعلي في املصفوفة “زائد واحد” أكبر من حجم املصفوفة الكلي
) .(MaxSizeبحيث تصبح كما يلي:
117
املصفوفات واألصناف الوحدة الثالثة
)void section::StAdd(student s
;{int pos=0
// expand array if necessary
;)(if(size+1>MaxSize) expand
// search for the proper insertion position
)while(s.get_stno() > sec[pos].get_stno() && pos<size
;pos++
// shift element up one position
)for(int i=size-1;i>=pos;i--
;]sec[i+1]=sec[i
;sec[pos]=s
;size++
}
من املهم أن تالحظ ،عزيزي الدارس ،أن ال تغيير ينبغي أن يحصل على بقية الدوال
املنتمية.
نشاط ()7
استخدم ،عزيزي الدارس ،احلاسوب لتنفيذ التعريف اجلديد لـ sectionواختباره
مع بقية الدوال باإلضافة للبرنامج mainالسابق (الذي يعرض قائمة للمستخدم
لالختيار منها إضافة طالب أو حذفه … إلخ).
تدريب ()9
ملاذا عرفت الدالة expandفي القسم اخلاص من الصنف section؟
تدريب ()10
عرف ،عزيزي الدارس ،الدالة املنتمية shrinkالتي تصغر حجم املصفوفة احملجوزة
عندما يقل عدد الطلبة في املصفوفة sizeبحيث يصبح أقل من نصف .MaxSize
118
املصفوفات واألصناف الوحدة الثالثة
تدريب ()11
قم ،عزيزي الدارس ،بتعديل الدالة StDeleteبحيث تقوم باستدعاء الدالة
shrinkعندما يصبح عدد الطلبة في املصفوفة أقل من نصف احلجم الكلي للمصفوفة.
}
.4الدالة StAddأيض ًا بحاجة لبعض التعديالت ،فهي تستقبل اآلن عنوان ًا لكائن
(بوساطة العامل )sوليس الكائن نفسه .بالطبع ،الكائن نفسه يجب أن يحجز
من طرف الدالة املستدعية مثل ،mainعلى سبيل املثال .وهذا سبب تغيير تعريف
العامل sفي ترويسة الدالة .الحظ أيض ًا أن طريقة استدعاء الدالة )( get_stnoقد
ال من sو ] sec[posيحتويان اآلن على تغيرت في جملة الدوران whileألن ك ً
عناوين الكائنات .فمثال ،الستدعاء الدالة )( get_stnoللكائن صاحب العنوان
املخزن في sنستخدم اجلملة )(.(*s).get_stno
)void section::StAdd(student* s
;{int pos=0
// expand array if necessary
;)(if(size+1>MaxSize) expand
// search for the proper insertion position
)while((*s).get_stno() > (*sec[pos]).get_stno() && pos<size
120
املصفوفات واألصناف الوحدة الثالثة
pos++;
// shift element up one position
for(int i=size-1;i>=pos;i--)
sec[i+1]=sec[i];
sec[pos]=s;
size++;
}
// binary search
int section::bsearch(long sn)
{
int lo=0;
int hi=size-1;
while (lo <= hi)
{ int mid =(lo+hi) /2;
long r=(*sec[mid]).get_stno();
if (r== sn)
return mid;
else
if (sn>r)
lo=mid+1;
else
hi = mid;
} //while
return -1;
}
بحيث حترر الذاكرة احملجوزة للطالبStDelete ونحتاج لعمل تعديل بسيط للدالة.6
الذي تشطبه بحيث تصبح
int section::StDelete(long stno)
{int i=bsearch(stno);
delete sec[i];
if(i>-1)
{for(int j=i;j<size-1;j++)
sec[j]=sec[j+1];
size--;
}
return i;
}
بحيث تقوم بحجز الذاكرة اخلاصة بالطالبmain ونحتاج أيض ًا لتعديل الدالة.7
switch إن ما نحتاج لتعديله هو فقط احلالة األولى من جملة.اجلديد عند اإلضافة
بحيث تصبح
switch(choice){
case 1: // student add
student *p=new student;
p->initialize();
cs100.StAdd(p);
break;
:
:
122
املصفوفات واألصناف الوحدة الثالثة
نشاط ()8
استخدم ،عزيزي الدارس ،احلاسوب لتنفيذ التعريف اجلديد لـ sectionواختباره
مع بقية الدوال باإلضافة للبرنامج mainالسابق (الذي يعرض قائمة للمستخدم
لالختيار منها إضافة طالب أو حذفه … إلخ).
تدريب ()12
ادرس ،عزيزي الدارس ،الصنف التالي جيداً ،الشخص ،personثم استخدم فكرة
حجز املصفوفات بطريقة ديناميكية إلدخال معلومات عن الشخص متثل اسمه وطباعة األسماء:
class person //class of persons
{
protected:
string name; //person’s name
public:
void setName() //set the name
} ;{ cout << “Enter name: “; cin >> name
void printName() //display the name
} ;{ cout << endl << name
string getName() //return the name
} ;{ return name
;}
اكتب الدالة الرئيسة والداالت الالزمة لطباعة األسماء مرتبة تصاعدي ًا بعد فرزها،
بحيث تكون النتائج كما يلي:
123
املصفوفات واألصناف الوحدة الثالثة
Linked Lists .7القوائم املتصلة
Linkedأنها مجموعة من العقد nodesاملتجانسة (من linkedListsعلىاملتصلة list �������القائمة
������� .7تعرف
تليها ،باستثناء النوع نفسه) مرتبة بشكل خطي بحيث حتمل كل عقدة عنوان العقدة التي
���� ������� ������� nodes ������ ��� ������� ����� ��� linked list
آخر عقدة؛ إذ حتمل القيمة اخلاصة NULLلإلشارة إلى انتهاء القائمة املتصلة .ومتثل كل
��������� )�� ����� ����( ����� ���� ��� ���� ���� �� ���� ����� ������ �����
الشكل ( :)5يظهر في الشكل قائمة متصلة من أربع عقد ،حيث حتمل كل عقدة عنوان العقدة
����� )����� ���� �� ���� ��� ���� ���� �� ����� ����� ����� �� ���� :(5
التي تليها .وميثل ذلك بالرسم باستخدام السهم .بينما حتمل العقدة األخيرة القيمة اخلاصة
������ ���� ����������� ������ ������ ���� ����� .����� �������� ������ ��� ����� .
������ .������ ���� �������� ������ ����� NULL
LLUNومتثل بالرسم باستخدام اخلط املائل.
����� ������� ������� ����� ����� ��������� �� ���� �� ������ ���� ����
ال للمصفوفات في كثير من األحيان وذلك لعدة ال مفض ًوتشكل القوائم املتصلة بدي ً
����� ����:
منها��� ���� �������� ����� ����� ����� � ���� �� (����) ���� �����: أسباب �����
�� .1
للبيانات كما هي .1إن عملية إضافة عنصر (عقدة) أو شطبه ال تتطلب عملية إزاحة
����� ��� ������� ��������� )���� ������ ������� ��� StDelete � StAdd
السابقة).
����� ����� �������� ������ ������� ������� �� ������� ������� ������ ���� .2
������� ������ ������ ��� ��� ������ ����� ������� ��� ������ ������ ����� ������
استخدام الذاكرة بطريقة اقتصادية بحيث نحجز املتصلة من
.delete ��������القوائم
������ � new ��������� متكننا
.2كذلك
الذاكرة اخلاصة بالعقد فقط عند احلاجة إليها ونحررها فور انتهاء احلاجة إليها بطريقة
.delete
������� ��� �������� ���������� ����: newو
������� الدوال
������� باستخدام���
ديناميكية���� �����
���
�������� �������� ������� �� �� � �� ���� ��� ������ �� :��������� �������� .1
باملصفوفات منها:
مقارنتها ����� ����
��������.������عند
للقوائم���املتصلة
املساوئ�� ��� بعض
������� ��� هنالك����� �����ذلك،
����� �� ومع
القائمة املتصلة
استعراض�������
من�� ���� ������ال بد
������ عنصر ما
إلى�� ���� ������للوصول
����������� التتابعية :إذ
املعاجلة��������
��������� .1
���قارن هذااملطلوب.
العنصر� ����� ��������� إلى
��� ����� ) إلى أن نصل القائمة
������� ��� بداية
������ �� عنصر ًا عنصر ًا
�������� من ������ ��
باملصفوفات؛ إذ إننا نستطيع معاجلة أي عنصر مبعرفة اخلاصة
����� �������. املباشرة
�������� باملعاجلة ���
����� �����(
ترتيبه في املصفوفة مما ميكننا من استخدام خوارزميات بحث فعالة (ال تتطلب
.������� ������ ��� ��������� ���� �� ���� ������� ������� ��������� ��� ����� .2
124
املصفوفات واألصناف الوحدة الثالثة
تعد املصفوفات أقل تعقيد ًا
.2تتطلب بعض البرمجيات املعقدة للتعامل معها إذ ّ
للبرمجة.
كما سبق أن ذكرنا أن كل عقدة في القائمة املتصلة حتمل عنوان العقدة التي تأتي بعدها في
القائمة باستثناء آخر عقدة في القائمة حيث حتمل قيمة املؤشر اخلاصة NULLوهي قيمة مكافئة
للصفر في لغة .C++تستخدم هذه القيمة عالمة لإلشارة إلى نهاية القائمة املتصلة .هذا يعني
أن تعريف أي عقدة سواء كان تركيب ًا structureأو صنف ًا classالبد من أن يحتوي على متغير
منتم قادر على تخزين عنوان العقدة التالية ،سندعو هذا املتغير nextفي أمثلتنا .فمث ً
ال إلنشاء
قائمة متصلة من الطلبة ،حيث متثل كل عقدة طالب ًا معيناً ،منثل العقدة باعتبارها صنف ًا student
كما يلي:
{class student
;long stno
;int csno
;]char StName[20
;student* next
public:
;)(void initialize
};long get_stno() {return stno
};char* get_stname(){return StName
;}
الحظ ،عزيزي الدارس ،وجود املتغير املنتمي nextفي تعريف studentوأنه من النوع
* ،studentأي أنه مؤشر للنوع studentمما يعني أننا سنستخدمه لتخزين عنوان العقدة (أو العنصر
التالي) .ومن اجلدير بالذكر أننا نحتفظ بعنوان أول عقدة في القائمة املتصلة في متغير مؤشر كي
نستطيع أن نستعرض traverseالقائمة عنصر ًا عنصراً .وعادة ما يدعى هذا املتغير Headأو First
وتستطيع ،بالطبع ،تسميته أي شيء آخر إن شئت .ومن الضروري احملافظة على قيمة هذا املتغير
من التغيير اخلاطئ ،إذ إنه سبيلنا الوحيد للتعامل مع القائمة املتصلة.
كما سبق أن ذكرنا أن كل عقدة في القائمة املتصلة حتمل عنوان العقدة التي تأتي بعدها في
القائمة باستثناء آخر عقدة في القائمة حيث حتمل قيمة املؤشر اخلاصة NULLوهي قيمة مكافئة
للصفر .تستخدم هذه القيمة عالمة لإلشارة إلى نهاية القائمة املتصلة.
هذا يعني أن تعريف أي عقدة سواء كان تركيب ًا structureأو صنف ًا classالبد من أن
125
املصفوفات واألصناف الوحدة الثالثة
يحتوي على متغير (حقل) منتم قادر على تخزين عنوان العقدة التالية ،وسندعو هذا املتغير
nextفي أمثلتنا.
ال على القوائموسنعرف قائم ًة متصلة من الطلبة أي من صنف studentباعتبارها مثا ً
املتصلة .أي أن العقدة في القائمة ستحتوي على كائن من نوع studentباإلضافة إلى عنوان
العقدة التالية .وفيما يلي تعريف :node
{struct node
;student data
;node* next
;}
حيث يحتوي تعريف هذا الصنف على متغير منتم واحد هو headويستخدم
لالحتفاظ بعنوان أول عقدة في القائمة املتصلة ،ولذا فهو من النوع* .nodeوهنالك
أيضا أربع دوال منتمية هي:
.1البناء :LinkedListكل ما يعمله هو جعل headمساوي ًا لقيمة املؤشر اخلاصة
NULLلإلشارة إلى أن القائمة فارغة في البداية.
:ListAdd .2تقوم بإضافة طالب جديد إلى القائمة املتصلة ،وفي املوقع املناسب
126
املصفوفات واألصناف الوحدة الثالثة
بحيث تبقى القائمة مرتبة تصاعدي ًا حسب رقم الطالب.
:ListDelete .3تقوم بحذف الطالب ذي الرقم stnoمن القائمة املتصلة.
:ListTraverse .4تقوم هذه الدالة بزيارة كل عقدة في القائمة على الترتيب وتعرض
بعض املعلومات عن الطالب املخزن في تلك العقدة.
127
املصفوفات واألصناف الوحدة الثالثة
الحظ أن جملة الدوران whileتتوقف في حالتني:
.1إذا أصبحت قيمة Qمساوية NULL
.2أو في حالة عدم حتقق الشرط )(s.get_stno()>(*Q).data.get_stno
في احلالة األولى ،نكون قد وصلنا إلى نهاية القائمة دون أن جند عقدة حتقق الشرط
)(s.get_stno()<=(*Q).data.get_stno
مما يعني أن رقم الطالب اجلديد أكبر من رقم أي طالب في القائمة ،وعليه فإن
العقدة اجلديدة ستضاف في نهاية القائمة.
أما احلالة الثانية فتعني أننا وجدنا عقدة في القائمة رقم الطالب فيها أكبر من رقم
الطالب اجلديد مما يعني أن العقدة اجلديدة ستضاف قبلها.
•ثم تبدأ عملية ربط العقدة اجلديدة بالقائمة ،وهنالك حالة خاصة جتب مراعاتها وهي
في حالة إضافة العقدة اجلديدة في بداية القائمة املتصلة ،عندها نضطر لتغيير قيمة
headبحيث يصبح عنوان العقدة اجلديدة .ولكن كيف نعرف أن إضافة العقدة
اجلديدة سيكون في بداية القائمة؟ واجلواب يكمن في قيمة املتغير prevعند نهاية
عملية البحث؛ فإذا كان ما زال مساوي ًا لقيمته االبتدائية ،وهي ,NULLفهذا يعني
أن العقدة اجلديدة ستضاف في بداية القائمة .وعليه ،فإن اجلملتني التاليتني تنفذان
; ptr->next=head
;head=ptr
تقوم األولى بتخزين عنوان أول عقدة في القائمة ،قبل اإلضافة ،في احلقل next
اخلاص بالعقدة اجلديدة .وتقوم اجلملة الثانية بجعل قيمة headمساوية لعنوان العقدة
اجلديدة ،أي أنها تصبح أول عقدة في القائمة .والشكل ( )6يبني بالرسم مثا ً
ال على
هذه احلالة؛ إذ حتمل العقدة اجلديدة رقم طالب 5وهو أصغر من أرقام الطلبة في القائمة
املتصلة .وعليه ،يجب إضافتها في بداية القائمة .ميثل الشكل ( A )6قيم املتغيرات
املؤشرة عند انتهاء عملية البحث عن موقع اإلضافة .بينما ميثل الشكل ( B )6حالة
القائمة املتصلة بعد اإلضافة.
128
املصفوفات واألصناف الوحدة الثالثة
الشكل ( :)6يبني الشكل Aقيم املتغيرات عند انتهاء عملية البحث عن موقع عقدة جديدة حتمل الرقم
،5بينما يوضح الشكل Bحالة القائمة املتصلة بعد عملية اإلضافة (الحظ أن daehأصبح يشير إلى
العقدة اجلديدة).
أما في حالة إضافة العقدة اجلديدة في أي موقع آخر (غير بداية القائمة) فإن اجلملتني
التاليتني تنفذان
;ptr->next=prev->next
;prev->next=ptr
تقوم اجلملة األولى بربط العقدة اجلديدة بالعقدة التي يجب أن تأتي بعدها وهي
العقدة التي كانت تلي ( prevقبل اإلضافة) ومن ثم ،فإن عنوان هذه العقدة موجود في
احلقل nextاخلاص بالعقدة املشار إليها بـ .prevوتقوم اجلملة الثانية بربط العقدة املشار
إليها بـ prevبالعقدة اجلديدة بأن نخزن عنوان العقدة اجلديدة ptrفي احلقل nextاخلاص
ال على هذه احلالة حيث يبني كيفية بالعقدة املشار إليها بـ .prevيوضح الشكل ( )7مثا ً
129
املصفوفات واألصناف الوحدة الثالثة
إضافة طالب جديد يحمل الرقم .16إذ يبني الشكل ( A :)7قيم املتغيرات عند انتهاء
عملية البحث ،بينما يوضح الشكل ( B )7القائمة املتصلة بعد انتهاء عملية اإلضافة.
الشكل( : )7يبني الشكل Aقيم املتغيرات عند انتهاء عملية البحث عن موقع عنصر يحمل الرقم .61
بينما يبني الشكل Bالقائمة املتصلة بعد إضافة العنصر اجلديد الذي يحمل الرقم .61
130
املصفوفات واألصناف الوحدة الثالثة
;node* prev=NULL
;node* Q=head
))(while(Q!=NULL && s.get_stno()>(*Q).data.get_stno
;{prev=Q
;Q=Q->next
}
if(prev==NULL) // insertion at front
;{ ptr->next=head
;head=ptr
}
else //insertion in the middle
{
;ptr->next=prev->next
;prev->next=ptr
}
} //else
} //ListAdd
نشاط ()9
قم ،عزيزي الدارس ،بتتبع خطوات عمل ListAddللتأكد من أنه يعمل بطريقة
صحيحة عند إضافة عقدة في نهاية القائمة املتصلة.
131
املصفوفات واألصناف الوحدة الثالثة
املتغير pالستعراض العقد عقدة عقدة حتى تنتهي القائمة بدون أن جند العقدة املطلوبة
(أي عندما تصبح قيمة pمساوية لـ ،)NULLأو عندما جند العنصر املطلوب .وعندها
فإن Pتشير إلى هذه العقدة .وتتم عملية البحث كما يلي:
;node* prev=NULL
;node* p=head
)while(p && p->data.get_stno()!=stno
;{ prev=p
;p=p->next
}
تأمل جيداً ،عزيزي الدارس ،الشرط في جملة الدوران ،whileإن هذا الشرط يعتمد
يعد falseلذا فإن هذا الشرط مكافئ متام ًا للشرط
على فكرة أن NULLمكافئة للصفر الذي ّ
)while(p!=NULL && p->data.get_stno()!=stno
:
:
أي أن جملة الدوران تتوقف إذا أصبحت قيمة pمساوية لـ NULLأو إذا حصل أن
p->data.get_stno()=stno
أي أننا وجدنا العقدة التي نبحث عنها .وفي هذه احلالة ،فإن pتشير إلى هذه العقدة
التي نود حذفها .وطاملا أن هذين الشرطني غير متحققني فإن جملة whileتقدم prevو
pبحيث يشيران إلى العنصرين الالحقني لهما.
.2بعد انتهاء جملة الدوران ،whileيجب أن نعرف ملاذا انتهت :إذا انتهت ألن قيمة
pأصبحت NULLفإن هذا يعني أن العقدة املطلوبة غير موجودة في القائمة،
عندها ترجع ListDeleteالقيمة صفر ًا لإلشارة إلى أن عملية احلذف لم تتم .وهذا
يتم بواسطة اجلملة
;if(!p) return 0
الحظ ،عزيزي الدارس ،أن هذه اجلملة مكافئة متاما لـ
;if(p==NULL) return 0
.3إذا لم يتحقق الشرط السابق فهذا يعني أن جملة الدوران whileانتهت ألننا
وجدنا العقدة املطلوبة ،وفي هذه احلالة ،فإن pتشير إلى هذه العقدة .يبقى اآلن أن
132
املصفوفات واألصناف الوحدة الثالثة
نتحقق من موقع العقدة التي نود شطبها :هل هي في بداية القائمة أم ال؟ إذا كانت العقدة
في القائمة فإن شطب هذه العقدة يجب أن يغير قيمة headبحيث يشير إلى العنصر الثاني
في القائمة .وإذا لم تكن العقدة في بداية القائمة فإننا يجب أن جنعل العقدة السابقة تشير
إلى العقدة التي تلي .pوهذا يتم عبر اجلملة
if(!prev) // i.e. prev==NULL
;head=head->next
else
;prev->next=p->next
يوضح الشكل ( )8ما يحدث عند حذف العقدة املوجودة في بداية القائمة (التي
حتمل الرقم ،)10بينما يوضح الشكل ( )9ما يحدث عند حذف عقدة موجودة منتصف
القائمة (العقدة التي حتمل الرقم .)17
.4يبقى أن نحرر الذاكرة املخصصة للعقدة التي نود حذفها ،وهذا يتم عبر اجلملة
;delete p
وأن نعيد القيمة 1لإلشارة إلى أن عملية احلذف متت بنجاح.
الشكل ( :)8يوضح الشكل Aقيم املتغيرات عند انتهاء البحث عن العقدة التي حتمل الرقم ،10بينما
يوضح الشكل Bحالة القائمة املتصلة بعد حذف تلك العقدة.
الشكل ( :)9يبني الشكل Aقيم املتغيرات عند انتهاء البحث عن العقدة التي حتمل الرقم ،17بينما يبني
الشكل Bحالة القائمة املتصلة بعد عملية حذف تلك العقدة لكن قبل حترير الذاكرة اخلاصة بها.
134
املصفوفات واألصناف الوحدة الثالثة
استعراض القائمة املتصلة ListTraverse
تقوم الدالة ListTraverseبزيارة كل عقدة في القائمة وطباعة بعض املعلومات اخلاصة
بالطالب.
)(void LinkedList::ListTraverse
;{node*p=head
)while(p
{
;cout<<(*p).data.get_stno()<<” “<<(*p).data.get_stname()endl
;p=p->next
}
}
نشاط ()11
قم ،عزيزي الدارس ،باستخدام احلاسوب لتنفيذ الصنف LinkedListودوالها
املنتمية .واكتب الدالة mainبحيث تعرض مجموعة من اخليارات للمستخدم إلضافة
طالب أو حذفه ،أو الستعراض القائمة املتصلة.
تدريب ()13
أضف ،عزيزي الدارس ،للصنف LinkedListالدالة املنتمية
)node* LinkedList::Search(long stno
التي تقوم بالبحث عن طالب ذي رقم معني واسترجاع عنوان العقدة اخلاصة به.
وإن لم يكن موجود ًا فإنها تعيد القيمة اخلاصة .NULL
تدريب ()14
أضف ،عزيزي الدارس ،للصنف LinkedListالدالة املنتمية
)(int LinkedList::Count
بعد العقد املوجودة داخل القائمة وإعادة ذلك العدد.
التي تقوم ّ
135
املصفوفات واألصناف الوحدة الثالثة
)15( تدريب
برنامج ًا يستخدم املؤشرات لتعريف قائمة متصلة، عزيزي الدارس،اكتب
.حتتوي على معلومات عن رقم الدارس كما هي موضحة في الشكل
136
املصفوفات واألصناف الوحدة الثالثة
.8اخلالصة
هنالك طريقتان رئيستان لتمثيل قائمة من الكائنات:
باستخدام املصفوفات :حيث ميثل كل عنصر من املصفوفة كائن ًا من صنف ما .وميكن .1
احتواء هذه املصفوفة من الكائنات مع الدوال الالزمة ملعاجلتها في صنف واحد.
.2باستخدام القوائم املتصلة :تعرف القائمة املتصلة على أنها مجموعة من العقد،
حيث حتمل كل عقدة ،باستثناء آخر عقدة ،عنوان العقدة التي تليها .ومتثل كل
عقدة إما تركيب ًا وإما كائن ًا من صنف ما.
137
املصفوفات واألصناف الوحدة الثالثة
حملة عن الوحدة الدراسية الرابعة.9
مترير: سنناقش ثالثة مواضيع مهمة هي، عزيزي الدارس،في الوحدة القادمة
ونرجو منك إعطاء تلك.العوامل باإلشارة والدوال الصديقة والعمليات واملجموعات
.الوحدة كل الوقت واالهتمام الذي تستحق
إجابات التدريبات.10
)1( تدريب
main()
{ student stu[50];
int n;
cout<<”Enter no. of students”;
cin>> n;
for(int i=0;i<n;i++)
stu[i].initialize();
for( i=0;i<n;i++)
cout<<stu[i].get_stname()<<’\t’<<stu[i].average()<<endl;
}
)2( تدريب
class student{
long stno;
int csno;
double grades[100];
char StName[20];
public:
double average();
void initialize();
long get_stno() {return stno;}
char* get_stname(){return StName;}
138
املصفوفات واألصناف الوحدة الثالثة
double get_grade(int i);
double max_grade();
};
double student::max_grade()
{ double max=grades[0];
for(int i=1;i<csno;i++)
if(grades[i]>max) max=grades[i];
return max;
}
main()
{ student stu[50];
int n;
cout<<”Enter no. of students”;
cin>> n;
for(int i=0;i<n;i++)
stu[i].initialize();
for( i=0;i<n;i++)
cout<<stu[i].get_stname()<<’\t’<<stu[i].average()<<’\t’<<stu[i].
max_grade()<<endl;
)3( تدريب
#include<string.h>
void sort(student* st, int m)
{ for(int i=0;i<m-1;i++)
{ int pos=i;
char* min=st[i].get_stname();
for(int j=i+1; j<m;j++)
if (strcmp(st[j].get_stname(),min)<0)
{min=st[j].get_stname();
pos=j;}
139
املصفوفات واألصناف الوحدة الثالثة
student a=st[pos];
st[pos]=st[i];
st[i]=a;
}
}
)4( تدريب
. 0 > في جملة املقارنة بـ0 < استبدل،فقط
)5( تدريب
// binary search
int bsearch(student* st,int n, char* sn){
int lo=0;
int hi=n-1;
while (lo < hi)
{ int mid =(lo+hi) /2;
char r[20];
strcpy(r,st[mid].get_stname());
if (strcmp(r,sn)==0)
return mid+1;
else
if (strcmp(sn,r)>0)
lo=mid+1;
else
hi = mid-1;
} //while
return 0;
}
)6( تدريب
استبدل جملة،فقط
if (strcmp(sn,r)>0)
بـif (strcmp(sn,r)<0)
140
املصفوفات واألصناف الوحدة الثالثة
)7( تدريب
void section::sortAv()
{ for(int i=0;i<size-1;i++)
{ int pos=i;
long min=sec[i].average();
for(int j=i+1; j<size;j++)
if (sec[j].average()<min)
{min=sec[j].average();
pos=j;}
student a=sec[pos];
sec[pos]=sec[i];
sec[i]=a;
}
for(i=0;i<size;i++)
cout<<sec[i].get_stname()<<›\t›<<sec[i].average();
}
)8( تدريب
:النتيجة كما يلي
Squares Characteristics
Square 1
Side: 24.55
Perimeter: 98.2
Area: 602.702
Square 2
Side: 15.08
Perimeter: 60.32
Area: 227.406
Square 3
Side: 8.212
Perimeter: 32.848
Area: 67.4369
Square 4
141
املصفوفات واألصناف الوحدة الثالثة
Side: 202.24
Perimeter: 808.96
Area: 40901
)9( تدريب
. من خالل الدوال املنتمية، فقط، يستخدمexpand ألن
)10( تدريب
void section::shrink()
{student *p=new student[MaxSize/2];
for(int i=0;i<size;i++)
p[i]=sec[i];
delete sec;
sec=p;
MaxSize /=2;
}
)11( تدريب
int section::StDelete(long stno)
{int i=bsearch(stno);
if(i>-1)
{for(int j=i;j<size-1;j++)
sec[j]=sec[j+1];
size--;
if (size<MaxSize/2) shrink();
}
return i;
}
142
املصفوفات واألصناف الوحدة الثالثة
)12( تدريب
// sorts person objects using array of pointers
#include <iostream>
#include <string> //for string class
////////////////////////////////////////////////////////////////
class person //class of persons
{
protected:
string name; //person›s name
public:
void setName() //set the name
{ cout << «Enter name: «; cin >> name; }
void printName() //display the name
{ cout << endl << name; }
string getName() //return the name
{ return name; }
};
////////////////////////////////////////////////////////////////
int main()
{
void bsort(person**, int); //prototype
person* persPtr[100]; //array of pointers to persons
int n = 0; //number of persons in array
char choice; //input char
do { //put persons in array
persPtr[n] = new person; //make new object
persPtr[n]->setName(); //set person›s name
n++; //count new person
cout << «Enter another (y/n)? «; //enter another
cin >> choice; // person?
}
while( choice==›y› ); //quit on ‹n›
cout << «\nUnsorted list:»;
for(int j=0; j<n; j++) //print unsorted list
143
املصفوفات واألصناف الوحدة الثالثة
{ persPtr[j]->printName(); }
bsort(persPtr, n); //sort pointers
cout << «\nSorted list:»;
for(j=0; j<n; j++) //print sorted list
{ persPtr[j]->printName(); }
cout << endl;
return 0;
} //end main()
//--------------------------------------------------------------
void bsort(person** pp, int n) //sort pointers to persons
{
void order(person**, person**); //prototype
int j, k; //indexes to array
for(j=0; j<n-1; j++) //outer loop
for(k=j+1; k<n; k++) //inner loop starts at outer
order(pp+j, pp+k); //order the pointer contents
}
//--------------------------------------------------------------
void order(person** pp1, person** pp2) //orders two pointers
{ //if 1st larger than 2nd,
if( (*pp1)->getName() > (*pp2)->getName() )
{
person* tempptr = *pp1; //swap the pointers
*pp1 = *pp2;
*pp2 = tempptr;
}
}
)13( تدريب
node* LinkedList::Search(long stno)
{node* p=head;
while(p)
if(p->data.get_stno()==stno) return p;
144
املصفوفات واألصناف الوحدة الثالثة
else
p=p->next;
return NULL;
}
)14( تدريب
int LinkedList::Count()
{ node* p=head;
int C=0;
while(p)
{ C+=1;
p=p->next;
}
return C;
}
)15( تدريب
#include <iostream>
///////////////////////////////////////////////////////////////
struct link //one element of list
{
int data; //data item
link* next; //pointer to next link
};
////////////////////////////////////////////////////////////////
class linklist //a list of links
{
private:
link* first; //pointer to first link
public:
linklist() //no-argument constructor
{ first = NULL; } //no first link
145
املصفوفات واألصناف الوحدة الثالثة
void additem(int d); //add data item (one link)
void display(); //display all links
};
//--------------------------------------------------------------
void linklist::additem(int d) //add data item
{
link* newlink = new link; //make a new link
newlink->data = d; //give it data
newlink->next = first; //it points to next link
first = newlink; //now first points to this
}
//--------------------------------------------------------------
void linklist::display() //display all links
{
link* current = first; //set ptr to first link
while( current != NULL ) //quit on last link
{
cout << current->data << endl; //print data
current = current->next; //move to next link
}
}
////////////////////////////////////////////////////////////////
int main()
{
linklist li; //make linked list
li.additem(25); //add four items to list
li.additem(36);
li.additem(49);
li.additem(64);
li.display(); //display entire list
return 0;
}
146
املصفوفات واألصناف الوحدة الثالثة
.11مسرد املصطلحات
حجز الذاكرة بالطريقة الديناميكية :Dynamic Memory Allocationتسمح -
هذه الطريقة للمبرمج بحجز الذاكرة التي يحتاج إليها في أثناء التنفيذ .وعند انتهاء
احلاجة إليها يستطيع املبرمج من حتريرها بحيث تستخدم ألغ��راض أخ��رى .تتم
عملية احلجز في لغة C++من خالل الدالة newوتتم عملية التحرير من خالل
الدالة .delete
حجز الذاكرة الطريقة الستاتيكية :ٍStatic Memory Allocationيقوم مترجما -
اللغة (مثل مترجم لغة )C++بحجز ال��ذاك��رة ،عند ترجمة البرنامج ،جلميع
املتغيرات املستخدمة في البرنامج وتبقى هذه الذاكرة محجوزة للبرنامج طيلة فترة
التنفيذ حتى وإن لم تستخدم.
الذيل :Trailer Nodeعنصر خاص يقع في نهاية بعض صيغ القوائم املتصلة. -
وقد يستخدمه املبرمج لتخزين بعض املعلومات اخلاصة عن القائمة.
الرأس :Header Nodeعنصر خاص يقع في بداية بعض صيغ القوائم املتصلة. -
وتخزن فيه بعض املعلومات العامة أو اخلاصة عن القائمة ،وقد يبقى خالي ًا من
القيم.
عقد :Nodesالعقدة هي مبثابة معلومات تخص عنصر ما (ك��ائ��ن) من عناصر -
القائمة املتصلة.
الفرز :Sortingيطلق على عملية ترتيب مجموعة من الكائنات وذل��ك وفق -
ترتيب معني (تنازلي أو تصاعدي) .وفي حالة كون العناصر من النوع الكائنات يتم
الفرز حسب عنصر منتم أو أكثر من العناصر املنتمية والتي يطلق عليها اسم املفتاح
(.)Key
القائمة املتصلة :linked listعلى أنها مجموعة من العقد nodesاملتجانسة (من -
النوع نفسه) مرتبة بشكل خطي بحيث حتمل كل عقدة عنوان العقدة التي تليها،
باستثناء آخ��ر عقدة إذ حتمل القيمة اخلاصة NULLل�لإش��ارة إل��ى انتهاء القائمة
املتصلة.
املعاجلة التتابعية :Sequential Processingاستعراض القائمة املتصلة عنصرا -
عنصرا من بداية القائمة إلى أن نصل إلى العنصر املطلوب.
مصفوفة م��ن الكائنات :Arrays of Objectsكثيرا م��ا تستخدم مصفوفة من -
الكائنات لتمثيل قائمة listمن األشياء املتجانسة (من نفس النوع كائنات).
147
املصفوفات واألصناف الوحدة الثالثة
املراجع.12
148
املصفوفات واألصناف الوحدة الثالثة
الوحدة الرابعة
الدوال الصديقة والعمليات
Friend Functions and Operators
150
الدوال الصديقة والعمليات الوحدة الرابعة
محتويات الوحدة
الصفحة امل ــوض ـ ـ ـ ــوع
153 .1املقدمة
153 1.1متهيد
153 2.1أهداف الوحدة
153 3.1أقسام الوحدة
154 4.1القراءات املساعدة
154 5.1ما حتتاج إليه لدراسة الوحدة
155 .2طرق مترير العوامل للدوال Parameter Passing to Functions
155 1.2مترير العوامل بالقيمة Parameter Passing by Value
157 2.2مترير العوامل باإلشارة Parameter Passing by Reference
.3الدوال الصديقة واألصناف الصديقة 162 Friend Functions and Classes
162 1.3الدوال الصديقة Friend Functions
168 2.3األصناف الصديقة Friend Classes
171 .4العمليات Operators
171 1.4إعادة حتميل العمليات Operators Overloading
174 2.4أشكال إعادة التحميل للعمليات املختلفة
178 3.4الكلمة املفتاحية ()this
181 4.4العناصر الثابتة ()static
183 .5صنف املجموعات Class Set
189 .6اخلالصة
189 .7حملة عن الوحدة الدراسية اخلامسة
190 .8إجابات التدريبات
204 .9مسرد املصطلحات
205 .10املراجع
151
الدوال الصديقة والعمليات الوحدة الرابعة
152
الدوال الصديقة والعمليات الوحدة الرابعة
.1املقدمة
1.1متهيد
عزيزي الدارس ،سندرس في هذه الوحدة ،الدوال واألصناف الصديقة،
وكيفية تعريفها واستخدامها .ثم سندرس كيفية تعريف العمليات مثل ( +و *
و ) -حيث جنعلها تتصرف كما نريد حسب نوعية العوامل .وفي القسم األخير
سوف نتحدث عن املجموعات.
2.1أهداف الوحدة
ينتظر منك ،عزيزي الدارس ،بعد فراغك من دراسة هذه الوحدة أن تكون
قادر ًا على أن:
.1تكتب وتستخدم الدوال التي تستقبل العوامل باإلشارة Reference Parameters.
.2متيز بني الدوال املنتمية والدوال الصديقة وتعرف متى تستخدم ك ً
ال منها.
.3تكتب الدوال الصديقة وتستدعيها بشكل صحيح.
.4تعرف األصناف الصديقة.
.5تكتب تعريفات جديدة للعمليات املختلفة.
.6متيز بني العمليات املنتمية والصديقة.
.7تعرف وتستخدم thisو staticفي برامج لغة . C++
.8تعرف وتستخدم صنف املجموعات . Class Set
3.1أقسام الوحدة
تتكون هذه الوحدة من أربعة أقسام رئيسة يناقش القسم األول منها موضوع
طرق مترير العوامل للدوال وهو بذلك يحقق الهدف األول .بينما يناقش القسم
الثاني موضوع الدوال واألصناف الصديقة ويحقق هذا القسم األهداف :الثاني
والثالث والرابع .أما القسم الثالث فيناقش موضوع العمليات املنتمية والصديقة
وهو بذلك يحقق األه��داف اخلامس والسادس والسابع .يعرض القسم الرابع
صنف املجموعات في لغة C++ويحقق الهدف الثامن.
153
الدوال الصديقة والعمليات الوحدة الرابعة
متغيرات مؤشرة ،ويحقق األهداف :الرابع و اخلامس والسادس .ويطرح
القسم اخلامس كيفية حجز الذاكرة بطريقة ديناميكية ،وهو بذلك يحقق الهدف
الرابع .ويبحث القسم السادس في موضوع القوائم املتصلة وكيفية تشكيلها من
كائنات ،ويحقق هذا القسم الهدف السادس.
4.1القراءات املساعدة
لقد حاولنا تقدمي الكثير من املفاهيم املهمة في هذه الوحدة بشكل مبسط.
ولكن كي تتعلم لغة C++بشكل جيد جد ًا ستجد أنه من املفيد أن تدرس أكبر
عدد ممكن من األمثلة والتمارين .لذا ننصحك بالرجوع إلى أكبر عدد ممكن من
كتب لغة . C ++وفيما يلي جتد قائمة من هذه الكتب:
1. Bronson, Gary, “Program Development and Design Using C++”,
PWS Publishing Company, 1997.
2. Cannon, Scott, “Understanding Programming: An Introduction Us-
ing C++”, West Publishing Company, 1997.
3. Deitel H. M. and Deitel P J, “C++ How to Program”, 6/e, 7/e, PREN-
TICE HALL 2008, 2009.
4. Moo B. E., “C++ Primer”, 4th Edition, Addison-Wesley, 2005.
5. Savitch W. “Absolute C++”, 2nd Edition (Savitch Series), Addison-
Wesley, 2005.
6. 6. Savitch, Walter, “Problem Solving with C++”, Addison-Wesley
Publishing Company, Inc 1996.
154
الدوال الصديقة والعمليات الوحدة الرابعة
.2طرق مترير العوامل للدوال
Parameter Passing to Functions
1.2مترير العوامل بالقيمة Parameter Passing by Value
تعلم ،عزيزي الدارس( ،من مقرر معاجلة البيانات) أن لغة Cمترر قيم العوامل إلى الدوال
ومن ثم ،فإن أي تغيير جتريه الدالة على القيم ال يؤدي إلى تغيير قيم العوامل احلقيقية .فمثال،
الدالة التالية:
)void swap1(int X, int Y
;{ int T=X
;X=Y
;Y=T
}
التي يبدو أنها تبدل قيمة Xمع قيمة Yوهما ما يدعيان بالعوامل الرسمية Formal
Argumentsلوجودهما في ترويسة الدالة) إال أن هذا صحيح فقط داخل الدالة نفسها،
وال يرافق هذا التغيير تغيير على العوامل احلقيقية actual argumentsاملوجودة في جملة
االستدعاء (التي أرسلت للدالة) .ولتوضيح ذلك لندرس نتيجة استدعاء الدالة أعاله
عبر املثال التالي:
)(main
;{int A=10
;int B=20
;cout<<”A=”<<A<<” B=”<<B<<endl
;)swap1(A,B
;cout<<”A=”<<A<<” B=”<<B<<endl
}
تستدعي هذه الدالة swap1لتبديل قيم العوامل احلقيقية Aو Bوتقوم بطباعة قيم Aو B
قبل استدعاء swap1وبعده .إن نتيجة تنفيذ هذا البرنامج هي طباعة:
A=10 B=20
A=10 B=20
أي أن قيم Aو ، Bوهي العوامل احلقيقية ،لم تبدل بعد استدعاء الدالة ،swap1
155
الدوال الصديقة والعمليات الوحدة الرابعة
ألن الدالة swap1تستقبل قيم العوامل Aو Bوليس عناوينها .إن القيم 10و 20في
مثالنا أعاله تخزن في مواقع جديدة في الذاكرة تختلف عن مواقع املتغيرات Aو Bولذا
فإن أي تغيير على العوامل الرسمية Xو Yال يقابله تغيير على قيم العوامل احلقيقية
AوB.
نشاط ()1
قم ،عزيزي الدارس ،بتنفيذ املثال أعاله والحظ النتيجة ،ثم قم بإضافة جملة في
نهاية الدالة swap1لطباعة قيم Xو ، Yماذا تستنتج؟
ال من أن نرسل قيمة العوامل احلقيقية Aو Bيجب وحلل املشكلة أعاله فإننا بد ً
أن نرسل للدالة عنوانهما حتى تستطيع هذه الدالة تغيير القيم املخزنة في هذه املواقع.
وبالطبع يجب إعادة كتابة swap1بحيث يتمكن من استخدام هذه العناوين لتغيير قيم
Aو . Bكما في : swap2
)void swap2(int* X,int* Y
; { int T=*X
; *X=*Y
;*Y=T
}
إن عوامل swap2الرسمية Xو Yهي مؤشرات ملتغيرات صحيحة ،ولذا فإن
swap2يتعامل مع املواقع التي نود تغييرها من خالل األلقاب *Xو . *Yوبالطبع،
عند استدعاء الدالة swapيجب أن نبعث لها عناوين املتغيرات التي نود تبديل قيمها
(العوامل احلقيقية) كما في املثال التالي
)(main
;{int A=10
;int B=20
;cout<<”A=”<<A<<” B=”<<B<<endl
;)swap2(&A,&B
;cout<<”A=”<<A<<” B=”<<B<<endl
}
156
الدوال الصديقة والعمليات الوحدة الرابعة
الحظ ،عزيزي الدارس ،أننا أرسلنا عناوين املتغيرين )Aو Bأي &Aو (&B
عندما استدعينا الدالة swap2.وبالطبع ،تقوم اآلن الدالة swap2بتغيير قيم Aو B
كما يجب حيث تكون النتائج كما يلي
A=10 B=20
A=20 B=10
نشاط ()2
قم ،عزيزي الدارس ،بتنفيذ املثال أعاله والحظ النتيجة.
الحظ وجود العملية & قبل كل من العوامل الرسمية لإلشارة إلى أن العوامل احلقيقية
ستمرر باإلشارة ) (by referenceوليس بالقيمة ) (by valueمما يعني أن أي تغيير على
قيم العوامل الرسمية سيقابله تغيير على قيم العوامل احلقيقية .من املهم أن تالحظ،
عزيزي الدارس ،أننا استخدمنا أسماء العوامل Xو Yداخل الدالة بطريقة مباشرة بدون
أن نسبقها بـ * إذ يتم عمل ذلك تلقائيا من جانب مترجم C.وأيضا عند استدعاء الدالة
swap3فإننا نرسل العوامل احلقيقية بدون استخدام العملية &كما يتضح في املثال التالي
)(main
;{int A=10
;int B=20
;cout<<”A=”<<A<<” B=”<<B<<endl
157
الدوال الصديقة والعمليات الوحدة الرابعة
swap3(A,B);
cout<<”A=”<<A<<” B=”<<B<<endl;
}
واجلدول التالي يلخص األمثلة السابقة ويبني تعريف االقتران وطريقة االستدعاء
.والنتيجة
void swap1(int X, int Y) void swap2(int* void swap3(int &X, int &Y)
{ int T=X; X,int* Y) { int T=X;
X=Y; { int T=*X; X=Y;
Y=T; *X=*Y; Y=X;
} *Y=T; }
}
)3( نشاط
. بتنفيذ املثال أعاله والحظ النتيجة، عزيزي الدارس،قم
158
الدوال الصديقة والعمليات الوحدة الرابعة
ومن اجلدير بالذكر أننا ال نستطيع مترير املصفوفات باإلشارة إمنا يجب أن منررها
بوساطة عنوان أول عنصر كما هو موضح في املثال التالي:
{ )][void f(int A
;A[0] = 5
}
{ )(int main
;]int B[10
;B[0] = 2
;)f(B
cout << B[0] << endl; // the output is 5
}
بالرغم من ان معامالت االقتران fتظهر أنها مترر بالقيمة (لعدم وجود إشارة &),
وحيث إن املتغير هو مصفوفة فإنه يتم متريره باإلشارة .لذا سيقوم البرنامج بطباعة قيمة
5بدال من .2
ومن املمكن أيض ًا أن ترجع الدالة قيمة باإلشارة كما في مثالنا التالي:
)int& max(int A, int B
){if (A>B
; return A
else
; return B
}
الحظ ،عزيزي الدارس ،أن نوع القيمة املرجعة هو & intأي إشارة referenceملتغير
صحيح .تقوم الدالة maxبتحديد العامل صاحب القيمة األكبر وإعادة إشارة إليه .ولذا
فإننا نستطيع أن نستدعي الدالة maxكما يلي:
;int A=10
;int B=100
;max(A,B)=5
159
الدوال الصديقة والعمليات الوحدة الرابعة
في املتغير صاحب أكبر قيمة وهو5 إن نتيجة تنفيذ هذه اجلملة هي تخزين القيمة
.B املتغير
)1( تدريب
: وأجب عن األسئلة التالية،ًادرس البرامج التالية جيدا
بني نتيجة التنفيذ كل برنامج على حدة؟- 1
ما نوع مترير املتغيرات في كل مثال؟- 2
. أعد كتابة البرنامج األول بطريق مترير املتغيرات بالقيمة- 3
160
الدوال الصديقة والعمليات الوحدة الرابعة
تدريب ()2
ادرس البرامج التالية جيد َا ثم اختر رم��ز اإلجابة الصحيحة حني نستدعي االقتران
)(Print
) void Mystery( int & a, int & b, int c
{
;a = b + c
;b = 0
;c = 0
}
)(void Print
{
;int x = 0, y = 1, z = 2
;)Mystery(x, y, z
;cout << x << « « << y << « « << z
;)Mystery(x, y, z
;cout << x << « « << y << « « << z << endl
}
161
الدوال الصديقة والعمليات الوحدة الرابعة
.3الدوال الصديقة واألصناف الصديقة
Friend Functions and Classes
1.3الدوال الصديقة Friend Functions
لقد أشرنا سابقا ،عزيزي الدارس ،إلى أن الدوال املنتمية للصنف هي فقط التي يحق لها
معاجلة األجزاء اخلاصة للصنف (املعرفة في اجلزء اخلاص privateمن الصنف) .في الواقع،
هنالك نوع آخر من الدوال يستطيع معاجلة األجزاء اخلاصة للصنف ،وهذه الدوال تدعى
الدوال الصديقة .وتعرف الدالة على أنها دالة صديقة لصنف ما بوضع منوذج prototypeلهذه
الدالة داخل تعريف الصنف مسبوق ًا بالكلمة . friendفمثالً ،لتعريف الدالة averageالتي
جتد معدل عالمات طالب ما كدالة صديقة للصنف studentنضع النموذج:
;)friend double average(student S
داخل تعريف الصنف studentفيصبح كما يلي:
{class student
;long stno
;int csno
;]double grades[100
;]char StName[20
public:
;)friend double average(student s
;)(void initialize
};long get_stno() {return stno
};char* get_stname(){return StName
;}
162
الدوال الصديقة والعمليات الوحدة الرابعة
تكتب الدالة الصديقة مثل أي دالة أخرى ليس لها عالقة بالصنف .فمثالً ،تعرف
الدالة averageكما يلي:
نشاط ()4
عرف ،عزيزي ال��دارس ،الدالة املنتمية averageوق��ارن بني تعريف الدالة
الصديقة averageاملعرفة في هذا القسم وتعريف الدالة املنتمية averageالتي
عرفتها ،وقارن أيض ًا بني طريقتي استدعائهما وقم بتدوين مالحظاتك.
وال شك في أنك تتساءل ،عزيزي الدارس ،متى نفضل استخدام الدوال املنتمية،
ومتى نفضل استخدام الدوال الصديقة؟
بشكل عام ،يفضل دائم ًا استخدام الدوال املنتمية إال في احلاالت التالية:
1 .1إذا كانت الدالة حتتاج إلى عاملني أو أكثر ،خصوصا إذا انتمى كل منهما إلى
صنف classمختلف ،في هذه احلالة ،يفضل أن تعرف الدالة باعتبارها دالة
163
الدوال الصديقة والعمليات الوحدة الرابعة
صديقة لكل األصناف التي تعالج كائناتها .فمثالً ،تصور أننا نود أن نكتب
دالة ما تنقل طالبا ما من شعبة sectionإلى شعبة أخرى ،الحظ أننا نحتاج في
ال أخر ميثل الطالب ،في هذه هذه احلالة إلى عاملني ميثل كل منهما شعبة وعام ً
احلالة ،يفضل كتابة هذه الدالة باعتبارها دالة صديقة للصنف. section
2 .2ذا أردنا حتميل الدالة أكثر من تعريف بحيث تتحدد الدالة التي نود استخدامها من
خالل عدد العوامل ونوعها .فمثالً ،لو كان لدينا أكثر من صنف من العقارات،
مثل شقة وعمارة ومنزل مستقل ،وأردنا كتابة الدالة )area(pالتي حتسب املساحة
للعقار pحسب نوعه ،في هذه احلالة ،قد نفضل أن نعرف ثالث دوال صديقة،
واحدة لكل صنف بحيث تتحدد الدالة التي ستستخدم من خالل نوع العامل
املمرر إليها في جملة االستدعاء.
3 .3في املثال التالي ،أعل ّنا عن الدالة () Resetالتي تأخذ معامل له الكينونة من
الصنف املركم ،Accumulatorوجتعل قيمة m _nValueتساوي .0من املنطقي
أن الدالة () Resetلن تكون قادرة على الوصول الى املتغيرات املنتمية اخلاصة
بالصنف املركم ،ألن () Resetليس دالة منتمية للصنف املركم .على أية حال،
وألن الصنف املركم أعلن أن الدالة () Resetصديق للصنف ،فإن الدالة الصديقة
() Resetميكنها الوصول إلى املتغيرات املنتمية اخلاصة في الصنف املركم.
class Accumulator
{
private:
;int m_nValue
public:
} ;Accumulator() { m_nValue = 0
} ;void Add(int nValue) { m_nValue += nValue
ويجب أن تالحظ ،عزيزي الدارس ،أننا يجب أن منرر الكينونة املركم للدالة ()
.Resetوسبب ذلك ان الدالة)( Resetليست دالة منتمية للصنف املركم ،وال ميلك
الكينونة من الصنف مركم ًا للعمل معها ،ما لم أعط تلك الكينونة من الصنف املركم.
وبالرغم من أن هذا املثال واضح جداً ،إال أننا سوف نشرح مثا ً
ال آخر .في هذا املثال،
نعلن عن الدالة () IsEqualباعتبارها صديق ًا للصنف القيمة .Value classمن املثال
ميكنك أن ترى أن الدالة الصديقة () IsEqualتأخذ كينونتني للصنــف القيمـة Value
classكمعامالن للدالة هما cValue1و .cValue2وألن الدالة ()IsEqualصديقة
للصنف القيمة ،فإن الدالة ميكنها أن تعالج املتغيرات اخلاصة بكينونات الصنف القيمة.
في هذه احلالة ،تستعمل الدالة لتعمل مقارنة بني كينونتني من نوع الصنف القيمة Value
، classويرجع لنا صح trueإذا كانت الكينونات متساوية .
class Value
{
private:
;int m_nValue
public:
} ;Value(int nValue) { m_nValue = nValue
;)friend boolean IsEqual(const Value &cValue1, const Value &cValue2
;}
الدالة ميكن أن تكون صديقة ألكثر من صنف واحد في الوقت نفسه .على سبيل
املثال ،ادرس املثال التالي:
class Temperature
{
165
الدوال الصديقة والعمليات الوحدة الرابعة
private:
int m_nTemp;
public:
Temperature(int nTemp) { m_nTemp = nTemp; }
class Humidity
{
private:
int m_nHumidity;
public:
Humidity(int nHumidity) { m_nHumidity = nHumidity; }
class Humidity
{
private:
int m_nHumidity;
public:
Humidity(int nHumidity) { m_nHumidity = nHumidity; }
167
الدوال الصديقة والعمليات الوحدة الرابعة
تدريب ()3
اكتب ،عزيزي الدارس ،الدالة transferالتي تقوم بنقل طالب من شعبة ما إلى
شعبة أخ��رى بحيث تكون دال��ة صديقة للصنف شعبة sectionاملعرف في الوحدة
السابقة.
168
الدوال الصديقة والعمليات الوحدة الرابعة
class Storage
{
private:
int m_nValue;
double m_dValue;
public:
Storage(int nValue, double dValue)
{
m_nValue = nValue;
m_dValue = dValue;
}
// Make the Display class a friend of Storage
friend class Display;
};
class Display
{
private:
bool m_bDisplayIntFirst;
public:
Display(bool bDisplayIntFirst) { m_bDisplayIntFirst = bDisplayInt-
First; }
;)cDisplay.DisplayItem(cStorage
;return 0
}
هناك بعض املالحظات املتعلقة باألصناف الصديقة ،إن عالقة األصناف الصديقة
ليست تبادلية وال عالقة تعدية؛ أي أن كون الصنف العرض Displayصديق للصنف
املخزن ،Storageال يعني أن الصنف املخزن Storageأيضا صديق للصنف العرض
.Displayفإذا أردت أن جتعل صنفني صديقني لبعضها بعضاً ،فيجب على االثنني أن
يعلنا على أن الصنف اآلخر صديق .أخيرا ،إذا كان الصنف Aصديق ، Bو Bصديق
،Cفهذا ال يعني أن الصنف Aصديق للصنف . C
نشاط ()5
قم ،عزيزي ال��دارس ،باستخدام احلاسوب إلع��ادة تنفيذ الصنف LinkedList
املعرف في الوحدة السابقة مستخدما التعريف اجلديد لـ . nodeفي الواقع لن تضطر،
عزيزي الدارس ،لتغيير أي شيء باستثناء تعريف . node
170
الدوال الصديقة والعمليات الوحدة الرابعة
Operators .4العمليات
هناك العديد من العمليات املعرفة في لغة C++التي متلك خاصية التحويل التلقائي؛
أي بصور مباشرة بني أنواع البيانات املعرفة في لغة .++ Cهذه التحويالت تسمح للمبرمج
بكتابة مجموعة من التعليمات والعمليات التي حتتوي على مجموعة مخلوطة من أنواع
البيانات في لغة C++.
لغة C++جتعلنا نستطيع إعادة تعريف ملعنى العمليات عند تطبيقها على الكينونات
املختلفة لألصناف .من خالل هذا املفهوم (إعادة التعريف )Overloadingمتكنا لغة
C++من إعادة تعريف العمليات لتعمل مع الكينونات املشتقة من األصناف املختلفة.
في هذا القسم سوف نناقش هذا املفهوم وإعطاء أمثلة على ذلك.
171
الدوال الصديقة والعمليات الوحدة الرابعة
ً بحيث تستخدم إلضافة طالب جديد للشعبة يجب أوsection
:ال أن نضع النموذج التالي
void operator+(student s);
: بحيث يصبح كما يليsection داخل تعريف الصنف
class section{
student sec[50];
int size;
int bsearch(long stno);
public:
section(){size=0;}
void operator+(student s);
void StAdd(student s);
int StDelete(long stno);
void Stlist();
void StRetrieve(long stno);
};
وهو يقوم أيض ًا بإضافةStAdd الحظ أن منوذج العملية ال يختلف عن منوذج الدالة
.طالب للشعبة
: ال يختلف إال في االسمoperator+ كما أن تعريف العملية
void section::operator+(student s)
{int pos=0;
// search for the proper insertion position
while(s.get_stno() > sec[pos].get_stno() && pos<size)
pos++;
// shift element up one position
for(int i=size-1;i>=pos;i--)
sec[i+1]=sec[i];
sec[pos]=s;
size++;
}
إلضافة،ً فمثال،أما بالنسبة الستدعاء العملية فيمكننا استدعاءها كأي دالة منتمية
: كما يلي+ ميكننا أن نستدعي العمليةcs100 للشعبةSالطالب
172
الدوال الصديقة والعمليات الوحدة الرابعة
;)cs100.operator+(S
أما الطريقة األفضل الستدعاء العملية فهي أن نستخدمها كأي عملية جمع أخرى
كما يلي:
;cs100 + S
وبالطبع فإن مترجم لغة C++يعرف بأن هذه العملية ليست عملية جمع عادية بسبب نوع
العوامل cs100و Sولذا ،فإنه يقوم باستدعاء العملية +التي قمنا نحن بتعريفها.
الحظ ،عزيزي الدارس ،أننا عرفنا عامال واحد ًا للعملية operator+هو student
Sبالرغم من أن للعملية عاملني هما الشعبة cs100و الطالب Sفي مثالنا ،وذلك ألن
هذه العملية عملية منتمية للصنف sectionومن ثم ،فإن العامل األول معرف تلقائي ًا
من الصنف ،studentأما العامل student Sالذي يظهر في تعريف ترويسة العملية فهو
العامل الثاني وليس األول.
تدريب ()4
اكتب ،عزيزي الدارس ،العملية – كعملية منتمية للصنف sectionبحيث
تقوم بحذف طالب من الشعبة.
العمليات التي ال يسمح لنا بإعادة تعريفها العمليات املسموح إعادة تعريفها
عملية تقرير املجال :: + - * / % ^
عملية Sizeof Sizeof & | ~ ! , =
اختيار األعضاء مباشرة *. < > =< => ++ --
عملية النقطة . << >> == =! && ||
اخلاصة بالتعابير الشرطية :؟ =+ =- =/ =% =^ =&
=| =* =<< =>> ][ )(
>- *>- new new delete delete
][ ][
في بعض العمليات ،ال تسمح لغة C ++لنا بإعادة تعريفها واجلدول التالي يوضح
لنا هذه العمليات والعمليات املسموح إعادة تعريفها:
كما ال ميكن تغيير اخلصائص التالية للعمليات عند إعادة تعريفها وهي:
173
الدوال الصديقة والعمليات الوحدة الرابعة
1 .1عدد العوامل :فإذا كانت العملية في لغة C++تأخذ عاملني فال ميكن تعريفها
بحيث تأخذ عام ً
ال واحداً ،على سبيل املثال.
2 .2أسبقية (أو أولوية priorityالعملية) :فال ميكن إعادة تعريف العملية +
وإعطاؤها أولوية أعلى من تلك اخلاصة بالعملية * .
3 .3رتيب التنفيذ :associativityفإذا كان ترتيب تنفيذ العملية من اليسار إلى
اليمني فال ميكن تغيير هذا الترتيب وجعله من اليمني إلى اليسار.
على افتراض أن قيم املتغيرات املنتمية لـ hour : Tو minو secهي 12و 23و 30على
الترتيب.
الحظ ،عزيزي الدارس ،أن للعملية << عاملني األول هو coutوالثاني كائن من الصنف
. time
في الواقع أن coutهو كائن من الصنف ostreamكما أن cinكائن من الصنف
.istreamإن العملية << معرفة كعملية صديقة للصنف ostreamكما أن العملية >>
معرفة كعملية صديقة للصنف .istreamوهذه العمليات معرفة بحيث تسمح للعامل
الثاني أن يكون عدد ًا صحيح ًا أو حقيقي ًا أو سلسلة رمزية… إلخ .وهذه التعاريف
174
الدوال الصديقة والعمليات الوحدة الرابعة
موجودة في املكتبة .iostreamوما نريد فعله هو إعطاء العملية << تعريف ًا جديد ًا يسمح
للعامل الثاني بأن يكون من الصنف ،timeلذا ،فإننا سنعرفها كعملية صديقة للصنف
.timeولذا فإننا سنضيف النموذج:
;)friend osrteam& operator<<(ostream& c,time t
بحيث يصبح للصنف : time
>#include<iostream.h
{class time
; int hour
; int min
; int sec
public:
; time(int h, int m, int s){hour=h
;min=m
};sec=s
;) friend osrteam& operator<<(ostream& c,time t
;}
175
الدوال الصديقة والعمليات الوحدة الرابعة
إن العامل احلقيقي األول coutميرر للعملية << عبر العامل الرسمي Cوأن
العامل احلقيقي الثاني ttميرر للعملية << عبر العامل الرسمي الثاني . t
من املهم أن تالحظ ،عزيزي الدارس ،أننا عرفنا للعملية << عاملني ألنها عملية
صديقة وليست منتمية ،إذ لو كانت منتمية الكتفينا بتعريف العامل الثاني على اعتبار
يعد تلقائيا من الصنف الذي تنتمي إليه هذه العملية .الحظ أيض ًا أن
أن العامل األول ّ
هذه العملية ترجع قيمة العامل ( Cلذلك فإن نوع القيمة املرجعة هو )& ostreamحتى
نتمكن من استخدامها في سلسلة من العمليات كما في اجلملة:
;cout<<tt<<ss<<t2
حيث ttو ssو t2كائنات من الصنف . time
يبقى أن جنيب عن السؤال :متى يفضل استخدام العمليات املنتمية ،ومتى يفضل
استخدام العمليات الصديقة؟
يفضل استخدام العمليات املنتمية إذا كان للعملية عامالن وكانت العملية تغير من
قيمة العامل األول ،في هذه احلالة يفضل أن تعرف العملية كعملية منتمية لصنف العامل
األول .أما العملية التي ال تغيير في قيم عواملها has no side effectفيفضل أن تعرف
كعوامل صديقة.
تدريب ()5
عرف ،عزيزي ال��دارس ،العملية >> كعملية صديقة للصنف timeبحيث
تقوم بقراءة الوقت بسؤال املستخدم عن الساعة ،ثم الدقيقة ،ثم الثانية.
تدريب ()6
ادرس الصنف التالي ،Dateكما يظهر من اإلعالن عن الصنف ،Dateإنها
تستخدم مفهوم إعادة التحميل للعمليات الزيادة ++إلضافة 1يوم إلى الشهر.
كما نرى من الصنف Dateأن الصنف يحتوي على:
إعادة حتميل لعملية إدخال البيانات<<&operator
البناء )…(Date
176
الدوال الصديقة والعمليات الوحدة الرابعة
إلعطاء القيم املبدئية للتاريخsetDate الدالة املنتمية
operator++() إعادة حتميل لعملية
+= إعادة حتميل لعملية
leapYear() دالة لفحص السنة الكبيسة
endOfMonth() دالة منتمية لفحص ما إذا كان اليوم هو آخر يوم في الشهر
على الصنف التالي واكتب الدوال والعمليات املنتمية املعلن، عزيزي الدارس،اعتمد
. واكتب البرنامج الرئيس الختبار عمل البرنامجDate عنها في الصنف
// Date class definition with overloaded increment operators.
#include <iostream>
using std::ostream;
class Date
{
friend ostream &operator<<( ostream &, const Date & );
public:
Date( int m = 1, int d = 1, int y = 1900 ); // default constructor
void setDate( int, int, int ); // set month, day, year
Date &operator++(); // prefix increment operator
Date operator++( int ); // postfix increment operator
const Date &operator+=( int ); // add days, modify object
bool leapYear( int ) const; // is date in a leap year?
bool endOfMonth( int ) const; // is date at the end of month?
private:
int month;
int day;
int year;
static const int days[]; // array of days per month
void helpIncrement(); // utility function for incrementing date
}; // end class Date
177
الدوال الصديقة والعمليات الوحدة الرابعة
3.4الكلمة املفتاحية ()this
في كلّ مرة يتم فيها استدعاء الدوال املنتمية ،ميرر بصورة اتوماتيكية مؤشر يدعى
،thisإلى الكينونة التي مت استدعاؤها .هذا املؤشر يدعى thisويعد معام ً
ال ضمني ًا جلميع
الدوال املنتمية .لذا ،في داخل أي دوال منتمية ،فإن املؤشر thisقد يستعمل لإلشارة
إلى الكينونة املستدعاة.
اخلاصة مباشرة في الصنف.ّ وكما تعرف فإن الدالة املنتمية ميكن أن تصل للبيانات
على سبيل املثال ،ادرس هذا الصنف،
{ class cl
;int i
;} void f() { ...
// ...
;}
في داخل الدالة )( fميكن ان نستخدم اجلملة التالية إلسناد القيمة عشرة إلى املتغيرات او
البيانات املنتمية في الصنف.
;i = 10
في احلقيقة ،اجلملة السابقة هي اختصار للجملة التالية:
;this->i = 10
لندرس املثال التقليدي التالي للتعرف على آلية عمل هذا املؤشر:
>#include <iostream
{ class cl
;int i
public:
void load_i(int val) { this->i = val; } // same as i = val
int get_i() { return this->i; } // same as return i
;}
)(int main
{
;cl o
;)o.load_i(100
;)(cout << o.get_i
;return 0
}
178
الدوال الصديقة والعمليات الوحدة الرابعة
. 100 نتيجة تنفيذ هذا البرامج انه سيقوم بإظهار القيمة
وسبب ذلك أن الدوال الصديقة ليست دوال،this الدوال الصديقة ال متلك هذا املؤشر
وعلينا أن نتذكر أن الدوال املنتمية هي الدوال الوحيدة التي ال متلك املؤشر.منتمية لألصناف
.this هذا
في إرجاع القيم من الدوال املنتمية واملعامالتthis ميكنك أيض ًا أن تستخدم املؤشر
: املثال التالي يوضح ذلك.املستنسخة
#include <iostream>
class alpha
{
private:
int data;
public:
alpha() //no-arg constructor
{}
alpha(int d) //one-arg constructor
{ data = d; }
void display() //display data
{ cout << data; }
alpha& operator = (alpha& a) //overloaded = operator
{
data = a.data; //not done automatically
cout << «\nAssignment operator invoked»;
return *this; //return copy of this alpha
}
};
int main()
{
alpha a1(37);
alpha a2, a3;
a3 = a2 = a1; //invoke overloaded =, twice
cout << «\na2=»; a2.display(); //display a2
cout << «\na3=»; a3.display(); //display a3
179
الدوال الصديقة والعمليات الوحدة الرابعة
;cout << endl
;return 0
}
وميكن أن نالحظ األمور التالية:
في املثال ،مت استخدام اإلعالن
alpha& operator = (alpha& a) //overloaded = operator
الذي يقوم بإرجاع القيمة من خالل مرجع بد ً
ال من اإلعالن
)alpha operator = (alpha& a
الذي يقوم بإرجاع قيمة صريحة .ويحتوي السطر األخير في االقتران اجلملة التالية:
;return *this //return copy of this alpha
التي تقوم بإرجاع محتويات الكائن املشار إليه باملؤشر ,thisأي محتويات الكائن احلالي.
تدريب ()7
ادرس البرنامج التالي جيد ًا ثم بني ما هي نتيجة تنفيذ البرنامج التالي:
>#include <iostream
{class what
private:
;int alpha
public:
()void tester
{
;this->alpha = 11; //same as alpha = 11
;cout << this->alpha; //same as cout << alpha
}
;}
{()int main
;what w
;)(w.tester
;cout << endl
;return 0
}
180
الدوال الصديقة والعمليات الوحدة الرابعة
4.4العناصر الثابتة ()static
ذكرنا أن الهدف األساسي من مترير املعامالت للدوال في صورة مرجع (أي باإلشارة)
هو القدرة على تعديل هذه املعامالت من خالل الدوال ،إال أن هناك عدد ًا من األسباب
األخرى التي تدعونا إلى ذلك ،منها الكفاءة .efficiencyعلى سبيل املثال ،تكون بعض
املعامالت كبيرة احلجم ،ومن ثم ،يتم مترير مثل هذه املعامالت كمرجع حتى يتم مترير
عنوانها للدوال بد ًال من نسخها بالكامل مما يعمل ،بالطبع ،على زيادة كفاءة البرنامج.
ولكن نفترض أننا نرغب في مترير إحدى املعامالت كمرجع لزيادة الكفاءة للبرنامج مع عدم
متكني الدوال في العبث في قيمة هذا املعامل ،ميكن في هذه احلالة استخدام الكلمة املفتاحية
constقبل املعامل؛ املثال التالي يوضح ذلك:
void aFunc(int& a, const int& b); //declaration
)(int main
{
;int alpha = 7
;int beta = 11
;)aFunc(alpha, beta
;return 0
}
//--------------------------------------------------------------
void aFunc(int& a, const int& b) //definition
{
a = 107; //OK
b = 111; //error: can›t modify constant argument
}
فلكي نضمن عدم قدرة الدالة )(aFuncعلى تعديل قيمة املعامل betaأو تغييرها ،قمنا
باستخدام الكلمة املفتاحية constمع املعامل أثناء اإلعالن عن الدالة وأثناء تعريفها كما هو
موضح أدناه:
;)Void aFunc(int & alpha, const int & beta
لذا يعترض املترجم على أي محاولة لتعديل قيمة املعامل الثاني للدالة.
تستخدم الكلمة املفتاحية constمع الدوال املنتمية في األصناف إذا أردنا ضمان عدم
تعديل تلك الدوال املنتمية ألي من عناصر البيانات املنتمية املعرفة في تلك األصناف.
181
الدوال الصديقة والعمليات الوحدة الرابعة
لتوضيح ذلك ,دعنا نتابع املثال التالي:
//demonstrates const member functions
class ConstFuncClass
{
private:
;int age
public:
)(void normalFunc //non-const member function
{ age = 55; } //OK
void conFunc() const //const member function
{ age = 55; } //error: can›t modify a member
;}
تدريب ()8
اكتب برنامج ًا بلغة C++مستخدم ًا فكرة الدالة الثابتة constلتعريف صنف اسمه
Distanceيحتوي على عناصر البيانات املنتمية التالية :قدم feetمن نوع عدد صحيح
وبوصات inchesمن نوع عدد حقيقي .يقوم البرنامج بإدخال قراءتني بالقدم والبوصة
وإيجاد مجموعهما وطباعة ذلك.
182
الدوال الصديقة والعمليات الوحدة الرابعة
Class Set .5صنف املجموعات
سنختتم هذه الوحدة ،عزيزي الدارس ،بتعريف املجموعة كصنف class setونعرف
العديد من العمليات التي تستخدم عليها.
تعرف املجموعة بأنها عدد من األشياء غير املرتبة املأخوذة بدون تكرار من مدى
Universeمحدد .ويحدد املدى بأصغر قيمة وأكبر قيمة ميكن أن تنتميا إلى املجموعة.
وهنالك العديد من العمليات التي تستخدم على املجموعات أهمها:
.1إنشاء املجموعة :وسنعرف بناء constructorيقوم بهذه العملية.
.2إضافة عنصر للمجموعة :وسنعرف العملية +لتقوم بذلك.
.3إزالة عنصر من املجموعة :وسنعرف العملية – لتقوم بهذه املهمة.
.4تقاطع املجموعات :set intersectionحيث يتكون حاصل تقاطع مجموعتني
من جميع العناصر املشتركة بني املجموعتني (أي التي تظهر في املجموعتني).
وسنعرف العملية * لتنفيذ هذه العملية.
.5احتاد املجموعات :set unionحيث يتكون حاصل احتاد مجموعتني من جميع
العناصر التي تظهر في أي من هذه املجموعات .وسنعرف العملية +لتنفيذ هذه
العملية .الحظ أننا بذلك نكون قد أضفنا تعريفني للعملية . +
.6االنتماء إلى املجموعة :حيث ّ
يعد عنصر ًا ما منتمي ًا للمجموعة إذا ظهر في هذه
املجموعة .وسنكتب الدالة INلتنفيذ هذه العملية.
.7تعيني املجموعات :تقوم هذه العملية بجعل مجموعة ما مساوية ملجموعة
أخرى .وسنكتب العملية = لتنفيذ ذلك.
.8التأكد من أن املجموعة خالية :تستخدم لفحص ما إذا كانت املجموعة خالية أم
ال .وسنستخدم الدالة SetEmptyلعمل ذلك.
.9فرق املجموعات :set differenceتستخدم هذه العملية إليجاد العناصر
املوجودة في مجموعة وليست موجودة في مجموعة أخرى.
.10عرض عناصر املجموعة على الشاشة :سنعرف العملية << لعمل ذلك.
سنفترض هنا أن مدى املجموعة مدى محدود؛ مبعنى أننا نستطيع أن نعدد جميع
عناصره .ألننا سنستخدم مصفوفة ذات بعد واحد لتمثيل أي مجموعة حيث إن كل
عنصر في املدى ميثل مبوقع في هذه املصفوفة .فإذا كان ذلك العنصر منتمي ًا إلى املجموعة
183
الدوال الصديقة والعمليات الوحدة الرابعة
نضع القيمة 1في املوقع املخصص له ،وإن لم يكن منتمي ًا نضع القيمة 0في املوقع
املخصص له .فمثالً ،لنفرض أن مدى املجموعة هو أسماء أيام األسبوع السبعة ،لتمثيل
هذه املجموعة نستخدم مصفوفة من سبعة مواقع ،موقع لكل عنصر في املدى .إذا كان
يوم السبت عنصر ًا في املجموعة فإننا نخزن القيمة 1في املوقع األول املخصص له وإذا
لم يكن فإننا نخزن القيمة 0في املوقع املخصص له .ثم نأتي للموقع الثاني في املصفوفة
و املخصص ليوم األحد ونضع فيه القيمة 1إذا كان يوم األحد عنصر ًا في املجموعة و 0
إذا لم يكن وهكذا.
الحظ ،عزيزي الدارس ،أن هذا الصنف يحتوي على متغير منتم واحد هو weekdays
184
الدوال الصديقة والعمليات الوحدة الرابعة
هو ،في احلقيقة ،مصفوفة من سبعة مواقع كل موقع مخصص ليوم من أيام األسبوع .كما
يحتوي على العديد من مناذج الدوال والعمليات املنتمية والصديقة .وفيما يلي سنناقش ك ً
ال من
هذه الدوال والعمليات:
البناء : Set
)Set::Set(){for(int i=0;i<7;i++
};weekdays[i]=0
يقوم هذا البناء بإنشاء مجموعة فارغة بوضع القيمة 0في كل موقع من مواقع
املصفوفة.
تقوم هذه العملية بإضافة عنصر جديد للمجموعة ،وذلك بتخزين الرقم 1في املكان
املخصص له .ميثل العامل iرقم اليوم الذي نود إضافته ( ،)7..1ولكون عناصر املصفوفة
تبدأ من 0وليس بواحد فاملوقع املخصص لهذا اليوم هو املوقع . i-1لقد فضلنا أن نعرف هذه
العملية كعملية منتمية ألنها تغير قيمة العامل األول وهو كائن من الصنف . Set
ونستطيع استخدام هذه العملية إلنشاء مجموعة حتتوي على أيام السبت واألحد
واجلمعة كما يلي
;Set S1
;S1+1
;S1+2
;S1+7
تدريب ()9
اكتب ،عزيزي الدارس ،العملية -بحيث تشطب عنصر ًا من املجموعة . Set
185
الدوال الصديقة والعمليات الوحدة الرابعة
تقاطع املجموعات * : operator
)Set operator*(Set S, Set T
;{Set R
)for(int i=0;i<7;i++
;]R.weekdays[i]=S.weekdays[i] && T.weekdays[i
;return R
}
تقوم هذه العملية بإنشاء املجموعة احمللية Rلتخزين العناصر املشتركة في املجموعة
Sواملجموعة Tثم إعادة هذه املجموعة .وقد فضلنا تعريف هذه العملية كعملية صديقة
للصنف Setألنها تأخذ عاملني من الصنف Setوال تغير قيمة أي منهما .وبالطبع
نستطيع إيجاد العناصر املشتركة في املجموعة S1واملجموعة S2باستخدام هذه العملية
كما يلي:
;S1*S2
الحظ ،عزيزي الدارس ،أن قيمتي العاملني Sو Tميرران إلى هذه العملية
)(parameter passing by valueمما قد يجعل احلاسوب يستغرق وقتا أطول في عملية
نسخ هذه القيم عند متريرها ألن كال من Sو Tحتتوي على مصفوفة .والختصار هذا
الوقت ،نفضل مترير العوامل باإلشارة .by referenceفي هذه احلالة ،فقط ،سيمرر
عنوان العوامل مما يعني وقت تنفيذ أقل .وملنع هذه العملية من أن تغير (باخلطأ) قيم هذه
العوامل فإننا منررها كعوامل ثابتة ،وذلك بأن نسبقها بالكلمة constفي ترويسة العملية
فتصبح العملية كما يلي:
)Set operator*(const Set& S, const Set& T
;{Set R
)for(int i=0;i<7;i++
;]R.weekdays[i]=S.weekdays[i] && T.weekdays[i
;return R
}
بالطبع ،علينا اآلن تغيير منوذج هذه العملية داخل تعريف الصنف Setبحيث يصبح:
)friend Set operator*(const Set& S, const Set& T
186
الدوال الصديقة والعمليات الوحدة الرابعة
تدريب ()10
ملاذا لم نرجع ناجت العملية * باإلشارة؟ أي ملاذا لم يكن نوع القيمة املرجعة & Set؟
تدريب ()11
اكتب ،عزيزي الدارس ،العملية +التي هي حاصل احتاد مجموعتني.
االنتماء للمجموعة:
)int Set::IN(int i
;]{if(i>0&&i<7) return weekdays[i-1
else return -1; // to indicate an error in the range
}
تقوم هذه الدالة بإرجاع 1إذا كان رقم اليوم iمنتمي ًا للمجموعة ،وإن لم يكن فترجع القيمة . 0
وإذا حصل خطأ وكان رقم اليوم قيمة غير مقبولة فترجع هذه الدالة القيمة اخلاصة 1-للداللة على ذلك.
تعيني املجموعات:
)void Set::operator=(Set S
){for(int i=0;i<7;i++
;]weekdays[i]=S.weekdays[i
}
الحظ ،عزيزي الدارس ،أن هذه العملية عملية منتمية للصنف Setولذا فإن العامل
Sاملعرف هو العامل الثاني وليس األول .وتستخدم هذه العملية جلعل املجموعة Rمساوية
للمجموعة Tكما يلي:
;R=T
تدريب ()12
اكتب ،عزيزي الدارس ،الدالة SetEmptyالتي ترجع القيمة 1إذا كانت املجموعة
فارغة وإال فإنها ترجع القيمة . 0
187
الدوال الصديقة والعمليات الوحدة الرابعة
تدريب ()13
اكتب ،عزيزي الدارس ،العملية –التي تقوم بإيجاد حاصل طرح مجموعتني.
تدريب ()14
قم ،عزيزي الدارس ،بكتابة العملية << بحيث تقوم بعرض قيمة مجموعة
ما على الشاشة.
تدريب ()15
اكتب برنامج ًا بلغة c++يعلن صنف ًا عن نوع البيانات مجموعة SETالتي ال يزيد
عدد عناصرها على 100عنصر من النوع العدد صحيح .ويحتوي هذا الصنف على
العمليات التالية:
MaxCardيدل على عدد العناصر التي حتتويها املجموعة
EmptySetميحو محتويات املجموعة بإضافة القيمة صفر إلى املتغير card
Memberاختبار عضوية عنصر إلى املجموعة
AddElemإلضافة عنصر جديد إلى املجموعة في حالة أن العنصر موجود أو ال
يوجد مكان overflowال يحدث شيء ( ال يضيف العنصر)
RmvElemحلذف عنصر موجود في املجموعة
Copyنسخ مجموعة إلى أخرى
Equalيفحص ما إذا كانت املجموعات متساوية
Intersectإليجاد التقاطع بني مجموعتني .والعناصر املوجودة في املجموعتني.
على سبيل املثال ،تقاطع املجموعتني } {3,5,6و } {3,6 ,7هو}{3,6
Unionاحتاد مجموعتني
Printيطبع املجموعة
elemsميثل عناصر املجموعة
cardمتغير ميثل عدد عناصر املجموعة.
188
الدوال الصديقة والعمليات الوحدة الرابعة
نشاط ()6
ق��م ،عزيزي ال���دارس ،باستخدام احلاسوب لتنفيذ الصنف Setوكتابة برنامج
الستخدام الدوال والعمليات اخلاصة به.
.6اخلالصة
نستطيع مترير عوامل ال���دوال والعمليات ب��اإلش��ارة ،ونعني بذلك مترير عناوين
ال من قيمها؛ مما يعطي الدالة أو العملية القدرة على تغيير هذه العوامل احلقيقية ب��د ً
العوامل .ونستخدم هذه الطريقة في بعض األحيان حتى وإن لم تكن الدالة أو العملية
لتغير قيم العوامل وذلك لتقليل الوقت الالزم لتمرير املصفوفات ،على سبيل املثال .إذ
ال من عنوانها فسيستغرق ذلك وقت ًا بسبب حجمها. لو أردنا مترير قيمتها بد ً
هنالك نوع آخر من الدوال ،باإلضافة إلى الدوال املنتمية ،يستطيع معاجلة األجزاء
اخلاصة بصنف ما وهي الدوال الصديقة .وتعرف دالة ما كدالة صديقة للصنف بوضع
منوذج لتلك الدالة مسبوق ًا بالكلمة friendداخل تعريف الصنف .ونستطيع تعريف
صنف ما كصنف صديق لصنف آخر مما يجعل تلقائيا جميع ال��دوال املنتمية للصنف
األول دوال صديقة للصنف الثاني .ويتم ذلك بوضع جملة حتوي اسم الصنف األول
مسبوق ًا بالكلمة friendداخل تعريف الصنف الثاني.
متكننا لغة C++من تعريف الكثير من العمليات مثل +و – و * إلخ بالطريقة
املناسبة لنا .وال يختلف تعريف العملية عن تعريف الدالة إال باالسم ،حيث يتكون اسم
العملية من رمزها مسبوق ًا بالكلمة .operatorوميكننا تعريف العملية إما كعملية منتمية
وإما كعملية صديقة.
189
الدوال الصديقة والعمليات الوحدة الرابعة
إجابات التدريبات.8
)1( تدريب
-1
البرنامج الثاني البرنامج األول
3+6=9
The number to be doubled is 3
parameter passing by في كال املثالني يعد التمرير من نوع مترير العوامل باإلشارة-2
reference.
-3
#include <iostream>
void doubleIt(int);
int main ()
{
int num;
cout << «Enter number: «;
cin >> num;
doubleIt(num);
cout << «The number doubled in main is « << num << endl;
return 0;
}
void doubleIt (int x)
{
cout << «The number to be doubled is « << x << endl;
x *= 2;
cout << «The number doubled in doubleIt is «
<< x << endl;
}
190
الدوال الصديقة والعمليات الوحدة الرابعة
)2( تدريب
.اإلجابة هي فرع د
)3( تدريب
class section{
student sec[50];
int size;
int bsearch(long stno);
public:
section(){size=0;}
void StAdd(student s);
int StDelete(long stno);
void Stlist();
void StRetrieve(long stno);
void section::sort();
friend int transfer(long stno, section& C1, section& C2);
};
int transfer(long stno, section& C1, section& C2)
{
// delete student from class C1
int i=C1.bsearch(stno);
student s;
if(i>-1)
{s=C1.sec[i];
for(int j=i;j<C1.size-2;j++)
C1.sec[j]=C1.sec[j+1];
C1.size--;
//
// insert student in the class C2
int pos=0;
// search for the proper insertion position
while(s.get_stno() > C2.sec[pos].get_stno() && pos<C2.size)
191
الدوال الصديقة والعمليات الوحدة الرابعة
pos++;
// shift element up one position
for(int j=C2.size-1;j>=pos;j--)
C2.sec[j+1]=C2.sec[j];
C2.sec[pos]=s;
C2.size++;
} // if (i>-1)
return i; // to indicate if the student was transfered succefuly
}
)4( تدريب
int section::operator-(long stno)
{int i=bsearch(stno);
if(i>-1)
{for(int j=i;j<size-2;j++)
sec[j]=sec[j+1];
size--;
}
return i;
}
: وهوsection ال تنس إضافة منوذج لهذه العملية داخل تعريف الصنف
int operator-(long stno);
)5( تدريب
istream& operator>>(istream& c, time& t)
{
c>>t.hour>>t.min>>t.sec;
return c;
}
: وهوtime ال تنس إضافة منوذج لهذه العملية داخل تعريف الصنف
friend istream& operator>>(istream& c, time& t);
192
الدوال الصديقة والعمليات الوحدة الرابعة
)6( تدريب
// Date class definition with overloaded increment operators.
#include <iostream>
using std::ostream;
class Date
{
friend ostream &operator<<( ostream &, const Date & );
public:
Date( int m = 1, int d = 1, int y = 1900 ); // default constructor
void setDate( int, int, int ); // set month, day, year
Date &operator++(); // prefix increment operator
Date operator++( int ); // postfix increment operator
const Date &operator+=( int ); // add days, modify object
bool leapYear( int ) const; // is date in a leap year?
bool endOfMonth( int ) const; // is date at the end of month?
private:
int month;
int day;
int year;
static const int days[]; // array of days per month
void helpIncrement(); // utility function for incrementing date
}; // end class Date
// Date constructor
Date::Date( int m, int d, int y )
{
setDate( m, d, y );
} // end Date constructor
193
الدوال الصديقة والعمليات الوحدة الرابعة
// set month, day and year
void Date::setDate( int mm, int dd, int yy )
{
month = ( mm >= 1 && mm <= 12 ) ? mm : 1;
year = ( yy >= 1900 && yy <= 2100 ) ? yy : 1900;
int main()
{
Date d1; // defaults to January 1, 1900
Date d2( 12, 27, 1992 ); // December 27, 1992
196
الدوال الصديقة والعمليات الوحدة الرابعة
Date d3( 0, 99, 8045 ); // invalid date
cout << “d1 is “ << d1 << “\nd2 is “ << d2 << “\nd3 is “ << d3;
cout << “\n\nd2 += 7 is “ << ( d2 += 7 );
:ناجت التنفيذ
d1 is January 1, 1900
d2 is December 27, 1992
d3 is January 1, 1900
d2 += 7 is January 3, 1993
197
الدوال الصديقة والعمليات الوحدة الرابعة
d4 is July 13, 2002
++d4 is July 14, 2002
d4 is July 14, 2002
)7( تدريب
11 النتيجة هي
)8( تدريب
// const member functions and const arguments to member Functions
#include <iostream>
using namespace std;
////////////////////////////////////////////////////////////////
class Distance //English Distance class
{
private:
int feet;
float inches;
public: //constructor (no args)
Distance() { feet=0; inches=0.0;}
//constructor (two args)
Distance(int ft, float in) {feet=ft; inches=in;}
198
الدوال الصديقة والعمليات الوحدة الرابعة
void showdist() const //display distance
{ cout << feet << “\’-” << inches << ‘\”’; }
)9( تدريب
void Set::operator-(int i)
{if(i>=1 && i<=7)
weekdays[i-1]=0;}
)10( تدريب
الذي سيدمر فور انتهاء تنفيذ هذهR ألن نتيجة التقاطع مخزنة في املتغير احمللي
. ولذا فال فائدة من إرجاع عنوانه،العملية
)11( تدريب
Set operator+(Set S, Set T)
{ Set R;
for(int i=0;i<7;i++)
R.weekdays[i]=(S.weekdays[i] || T.weekdays[i]);
return R;
}
)12( تدريب
int Set::SetEmpty()
{for(int i=0;i<7;i++)
if(weekdays[i]) return 0; // the set is not empty
return 1; // the set is empty
}
200
الدوال الصديقة والعمليات الوحدة الرابعة
)13( تدريب
Set operator-(Set S, Set T)
{Set R;
for(int i=0;i<7;i++)
R.weekdays[i]=(S.weekdays[i] && !T.weekdays[i]);
return R;
}
)14( تدريب
ostream& operator<<(ostream& C, Set S)
{for(int i=0;i<7;i++)
C<<S.weekdays[i]<<” “;
return C;
}
)15( تدريب
#include <iostream.h>
const maxCard = 100;
enum Bool {false, true};
class Set {
public:
void EmptySet (void) { card = 0; }
Bool Member (const int);
void AddElem (const int);
void RmvElem (const int);
void Copy (Set&);
Bool Equal (Set&);
void Intersect (Set&, Set&);
void Union (Set&, Set&);
void Print (void);
private:
int elems[maxCard]; // set elements
int card; // set cardinality
201
الدوال الصديقة والعمليات الوحدة الرابعة
};
202
الدوال الصديقة والعمليات الوحدة الرابعة
Bool Set::Equal (Set &set)
{
if (card != set.card)
return false;
for (register i = 0; i < card; ++i)
if (!set.Member(elems[i]))
return false;
return true;
}
void Set::Intersect (Set &set, Set &res)
{
res.card = 0;
for (register i = 0; i < card; ++i)
if (set.Member(elems[i]))
res.elems[res.card++] = elems[i];
}
void Set::Union (Set &set, Set &res)
{
set.Copy(res);
for (register i = 0; i < card; ++i)
res.AddElem(elems[i]);
}
void Set::Print (void)
{
cout << “{“;
for (int i = 0; i < card-1; ++i)
cout << elems[i] << “,”;
if (card > 0) // no comma after the last element
cout << elems[card-1];
cout << “}\n”;
}
.9مسرد املصطلحات
-إعادة التعريف :Overloadingإعادة تعريف العمليات operatorsاملختلفة مثل اجلمع
والطرح وغيرها مبا يتناسب مع تطبيقاتنا.
-األصناف الصديقة :Friend Classesإن تعريف الصنف بأكمله كصنف صديق
للصنف اآلخر يعطي جميع الدوال املنتمية إلى الصنف األول القدرة على معاجلة األجزاء
اخلاصة في الصنف اآلخر.
-مترير العوامل باإلشارة :Parameter Passing by Referenceأن يتم إرسال عناوين
املتغيرات للدالة حتى تستطيع هذه الدالة تغيير القيم املخزنة في هذه املواقع أو العناوين.
-الدوال الصديقة :Friend Functionsهي دالة لها احلق في معاجلة األجزاء اخلاصة
للصنف (املعرفة في اجلزء اخلاص privateمن الصنف) كما لو كانت دالة منتمية للصنف،
وتعرف الدالة على أنها دالة صديقة لصنف ما بوضع منوذج prototypeلهذه الدالة داخل
تعريف الصنف مسبوقا بالكلمة .friendوتختلف طريقة استدعاء الدالة الصديقة عن
طريقة استدعاء الدالة املنتمية.
-العوامل الشكلية :Formal Parametersالعوامل املوجودة في ترويسة الدالة.
-العوامل الفعلية :Actual Parametersالعوامل احلقيقية املوجودة في جملة االستدعاء
في البرنامج.
204
الدوال الصديقة والعمليات الوحدة الرابعة
املجموعة تعرف بأنها عدد من األشياء غير املرتبة املأخوذة بدون تكرار:Set املجموعة -
ويحدد املدى بأصغر قيمة وأكبر قيمة ميكن أن تنتميا إلى. محددUniverse من مدى
وهنالك العديد من العمليات التي تستخدم على املجموعات أهمها إنشاء.املجموعة
إضافة وإزالة عنصر من املجموعة وتقاطع واحتاد املجموعات وغيرها من،املجموعة
.العمليات
أن نقوم باإلعالن عن الصنف في بداية البرنامج:Class Prototype منوذج للصنف -
.سنعرف الصنف فيما بعدّ بإ ّنناcompiler املجمع
ّ لكي نخبر
املراجع.10
1. Bronson, Gary, “Program Development and Design Using C++”,
PWS, Publishing Company, 1997.
2. Cannon, Scott, “Understanding Programming: An Introduction Using
C++”, West Publishing Company, 1997.
3. Cogswell J., Diggins C., Stephens R., Turkanis J., “C++ Cookbook”,
O’Reilly, 2005.
4. Davis S. R., “C++ for Dummies”, Fifth Edition, For Dummies, 2004.
5. Deitel H. M. and Deitel P. J., “C++ How to Program”, 5th, 6th, 7th edit-
ing Prentice Hall, 2005, 2008, 2009.
6. Hekmat Sh., “C++ Essentials”, PragSoft Corporation, 2005.
7. Hubbard J., “SCHAUM’S OUTLINE OF THEORY AND PROB-
LEMS of PROGRAMMING WITH C++”, Second Edition,
SCHAUM’S OUTLINE SERIES, McGRAW-HILL, 2000.
8. Kent, J., “C++ Demystified: A Self-Teaching Guide”, McGraw-Hill/
Osborne 2004.
9. Lippman S. B., Lajoie J., Moo B. E., “C++ Primer”, Fourth Edition,
Addison Wesley Professional, 2005.
10. Meyers S., “Effective C++ , 55 Specific Ways to Improve Your Pro-
grams and Designs”, Third Edition Addison - Wesley Professional,
2005.
205
الدوال الصديقة والعمليات الوحدة الرابعة
11. Moo B. E., “C++ Primer”, 4th Edition, Addison-Wesley, 2005.
12. Prata S., “C++ Primer Plus”, 5th Edition, Sams, 2004.
13. Schildt H., “C/C++ Programmer’s Reference”, Third Edition, Mc-
Graw-Hill/Osborne, 2003.
14. Schildt H.,”C++ from the Ground Up”, Third Edition, McGraw-Hill/
Osborne, 2003.
15. Savitch W., “Absolute C++”, 2nd Edition (Savitch Series), Addison-
Wesley, 2005.
16. Savitch, Walter, “Problem Solving with C++”, Addison-Wesley Pub-
lishing Company, Inc 1996.
206
الدوال الصديقة والعمليات الوحدة الرابعة
الوحدة الخامسة
األصنــاف المشتقــة والقوالـب
Derived Classes and Templates
208
األصناف املشتقة والقوالب الوحدة اخلامسة
محتويات الوحدة
الصفحة امل ــوض ـ ـ ـ ــوع
211 .1املقدمة
211 1.1متهيد
212 2.1أهداف الوحدة
212 3.1أقسام الوحدة
213 4.1القراءات املساعدة
213 5.1ما حتتاج إليه لدراسة الوحدة
214 .2الوراثة Inheritance
214 1.2مفهوم الوراثة ومميزاتها
215 2.2وراثة األصناف
225 3.2مستوى الوصول للعناصر املوروثة (أنواع الوراثة)
228 4.2الوراثة املتعددة Multiple Inheritance
209
األصناف املشتقة والقوالب الوحدة اخلامسة
210
األصناف املشتقة والقوالب الوحدة اخلامسة
.1املقدمة
1.1متهيد
في هذه الوحدة ،عزيزي الدارس ،سنناقش أساليب مختلفة لتحقيق الهدف
األه��م للغات البرمجة الكينونية وهو تقليل احلاجة إلى كتابة دوال جديدة عن
طريق إعادة استخدام الدوال القدمية .Code Reuseإذ سنناقش في هذه الوحدة
كيفية اشتقاق صنف جديد ،يدعى بالصنف املشتق ،Derived Classمن صنف
(أو أكثر) آخر يدعى بالصنف األساس Base Class؛ مما يسمح لنا ،إذا أحسنا
تصميم النظام ،استخدام الكثير من ال��دوال اخلاصة بالصنف األس��اس للصنف
املشتق دون احلاجة إلى إع��ادة كتابتها من جديد .وذلك ألن املتغيرات وال��دوال
تورث إلى الصنف املشتق .الحظ ،عزيزي الدارس، اخلاصة بالصنف األساس ّ
أن إع��ادة استخدام ال���دوال Code Reuseال يقلل ،فقط ،من اجلهد والوقت
الالزمني لتطوير البرامج ،إمنا أيض ًا يقلل من الوقت واجلهد الالزمني لصيانتها.
ويساعدنا أيض ًا على إنتاج برامج يعتمد عليها ،reliableإذ إن استخدام دالة
مجربة خير من كتابة دالة جديدة قد حتوي أخطاء خفية.
ومن الطرق األخرى التي تزودنا بها لغة C++لتساعدنا في حتقيق هدفنا في
إع��ادة استخدام البرامج هو ما يسمى بالدوال القالبية Template Functions
وهي دوال تكتب بطريقة تسمح لنا باستخدامها على أنواع مختلفة من البيانات
دون احلاجة إلى كتابة نسخة من هذه الدوال لكل نوع من أنواع البيانات .كذلك
تسمح لنا لغة C++بتعريف أصناف قالبية وهي أصناف عامة تسمح لنا بتأجيل
حتديد نوع املتغيرات املنتمية إلى حني استخدامها ،وبذلك فهي متنحنا القدرة على
تعريف أصناف خاصة تعمل على أنواع بيانات مختلفة دون احلاجة إلى كتابة هذه
األصناف املختلفة وتعريف دوالها.
وممن املميزات الهامة للغة C++ما يدعى بتعدد األوجه ،polymorphism
حيث تسمح لنا لغة C++بتأخير حتديد هوية الدالة التي ستستدعى إلى وقت
تنفيذ البرنامج (وليس وقت ترجمته كما هي العادة) مما مينحنا القدرة على كتابة
برامج تقوم مبهام من الصعب كتابتها بلغات البرمجة التقليدية أو ما يسمى بلغات
البرمجة اإلجرائية كالباسكال ،على سبيل املثال.
211
األصناف املشتقة والقوالب الوحدة اخلامسة
2.1أهداف الوحدة
يتوقع منك ،عزيزي ال���دارس ،عند فراغك من دراس��ة ه��ذه ال��وح��دة ،أن
تكون قادر ًا على أن :
.1تصمم ما يسمى بشجرة األصناف الهرمية بحيث تقلل احلاجة إلى إعادة كتابة
دوال جديدة قدر اإلمكان.
.2تعرف أصناف ًا جديدة مشتقة من أصناف أخرى مفردة أو متعددة.
.3تبني أنواع الوراثة املختلفة وكيف تستخدمها.
.4تعرف دوال قالبية Template Functionsوتستخدمها بطريقة صحيحة.
.5تعرف صنف ًا قالبي ًا Template Classوتعرف كيف تستخدمه لعمل أصناف
جديدة.
.6تعرف كيف ومتى تستخدم خاصية تعدد األوجه polymorphismاملهمة.
3.1أقسام الوحدة
تتكون هذه الوحدة من ثالثة أقسام رئيسة ،يناقش القسم األول موضوع
األصناف املشتقة والوراثة وهو بذلك يحقق األهداف :األول والثاني والثالث.
بينما يعرض القسم الثالني موضوع الدوال واألصناف القالبية ويحقق هذا القسم
الهدفني الرابع واخلامس .أما القسم الثالث فيبحث موضوع تعدد األوجه ويحقق
الهدف السادس.
212
األصناف املشتقة والقوالب الوحدة اخلامسة
القراءات املساعدة4.1
.لقد حاولنا تقدمي الكثير من املفاهيم املهمة في هذه الوحدة بشكل مبسط
بشكل جيد جد ًا ستجد أنه من املفيد أن تدرس أكبرC++ ولكن كي تتعلم لغة
لذا ننصحك بالرجوع إلى أكبر عدد ممكن من.عدد ممكن من األمثلة والتمارين
: وفيما يلي جتد قائمة من هذه الكتب.C++ كتب لغة
1. Bronson, Gary, “Program Development and Design Using
C++”, PWS Publishing Company, 1997.
2. Cannon, Scott, “Understanding Programming: An Intro-
duction Using C++”, West Publishing Company, 1997.
3. Davis S. R., “C++ for Dummies”, Fifth Edition, 2004.
4. Deitel H. M. & Deitel P J., “C++ How to Program”, 6/e,
Prentice Hall, 2008.
213
األصناف املشتقة والقوالب الوحدة اخلامسة
Inheritance .2الوراثة
1.2مفهوم الوراثة ومميزاتها
ميكن تعريف الوراثة Inheritanceعلى أنها إمكانية أن يرث صنف ما اخلصائص
والعمليات (الدوال) املوجودة في صنف آخر ،مما يساعد على إعادة استخدام
Reusabilityاألصناف التي مت إنشاؤها من قبل .وبناء على هذا التعريف فإنه ميكن
حتديد مفهومني جديدين وهما:
.1صنف القاعدة أو األساس) :(Base classاملوروث هو الصنف الذي يحوي
البيانات والعمليات املراد توريثها لصنف آخر .
.2الصنف املشتق الوارث) :(Derived classوهو الصنف الوارث خلصائص
الصنف األساس وعملياته .
مما سبق ،عزيزي الدارس ،نرى أن لغة C ++متكننا من تعريف صنف جديد مشتق
من صنف آخر .والفائدة من عمل ذلك هو تقليل احلاجة إلى كتابة دوال جديدة للصنف
اجلديد ،ألننا نستطيع استخدام الدوال اخلاصة بالصنف األساس للصنف املشتق ،مما يقلل
في الوقت نفسه الوقت الالزم لصيانة هذه الدوال واملساعدة في إنتاج برامج يعتمد عليها.
ولتقليل احلاجة إلى كتابة برامج جديدة إلى أدنى درجة ممكنة البد من تنظيم األصناف
املختلفة بطريقة هرمية تضمن أن تعرف املتغيرات والدوال التي ستستخدم للعديد من
األصناف املشتقة في أعلى الشجرة الهرمية ،بينما تقع األصناف املشتقة التي ال تستخدم في
تعريف أصناف جديدة في أسفل الشجرة الهرمية .ولعمل ذلك البد من الدراسة والفهم
للنظام املطلوب تطويره واملهام املطلوب أن يحققها بعناية قبل أن يصمم النظام وقبل أن
ينفذ .والصيغة العامة للوراثة جلعل الصنف املشتق derivedيرث الصنف األساس :Base
Class derived : InheritanceType Base
فمثالً ،لنفرض أن لدينا مكتب عقارات Estate Agencyيتعامل ببيع وتأجير عقارات
من أنواع مختلفة هي :منازل مستقلة Housesلكل منها حديقة خاصة به ،وشقق ،Flats
وعمارات متعددة الطوابق .Multistory Buildingوتشترك هذه العقارات جميع ًا بأن
لها سعر ًا وأجرة شهرية ويحتاج مكتب العقارات إلى أن يكون قادر ًا على أن يسترجع
قيمة السعر أو األجرة الشهرية اخلاصة بعقار ما ،وإلى أن يكون قادر ًا على تعديل هذه
القيم عند احلاجة لعمل ذلك.
214
األصناف املشتقة والقوالب الوحدة اخلامسة
ولنفرض أن لكل منزل مستقل مساحة بناء ومساحة حديقة ،واملساحة الكلية للمنزل
تتكون من مجموع هاتني املساحتني .بينما لكل شقة مساحة واحدة .أما العمارات
متعددة الطوابق فان مساحتها الكلية تتكون من مجموع مساحات شققها .وللتبسيط
سنفرض أن جميع الشقق لها املساحة نفسها .وأن عدد هذه الشقق يحسب بضرب عدد
الطوابق بعدد الشقق في كل طابق.
ميكن تنظيم هذه األصناف على شكل هرمي يحقق هدفنا في إعادة استخدام الدوال
قدر اإلمكان .يوضح الشكل ( )1هذا التنظيم الهرمي على شكل شجرة الهرمية.
تخبرنا هذه الشجرة أن هنالك صنف ًا أساس ًا هو propertyوأن هنالك صنفني مشتقني
منه هما Houseو .Flatكما تخبرنا بأن هنالك صنفا رابع ًا هو Multi_Buildingمشتق ًا
من الصنف . Flatوسترى ،عزيزي الدارس ،احلكمة من تنظيم هذه األصناف وفقا لهذه
الهرمية عند تعريفها وكتابة الدوال اخلاصة بها.
2.2وراثة األصناف
إن الصنف املشتق يرث جميع املتغيرات والدوال املنتمية للصنف األساس .مبعنى
أننا نستطيع أن نستخدمها متام ًا كما لو كانت معرفة داخل الصنف املشتق .على سبيل
املثال ،مبا أن لكل العقارات أعاله سعرا وأجرة شهرية فإننا سنعرفها باإلضافة للدوال
215
األصناف املشتقة والقوالب الوحدة اخلامسة
اخلاصة مبعاجلتها داخل الصنف األساس ،propertyمما ميكن جميع األصناف املشتقة من
استخدامها دون احلاجة إلى إعادة تعريفها.
ولكن بقي أن نقرر في أي قسم من أقسام الصنف األساس propertyيجب أن نعرف
هذه املتغيرات والدوال املنتمية .إن تعريف هذه املتغيرات والدوال في القسم اخلاص من
الصنف األساس يجعلنا قادرين على معاجلتها فقط من خالل الدوال املنتمية للصنف
األساس والدوال الصديقة له ،وال ميكننا ذلك من معاجلة هذه املتغيرات والدوال املنتمية
من خالل الدوال املنتمية لألصناف املشتقة .وبالطبع ،يؤدي تعريفها في القسم العام
إلى السماح جلميع دوال البرنامج ،دون استثناء ،مبعاجلتها مما يشكل خرق ًا ملبدأ إخفاء
البيانات .وحلل هذه املشكلة تزودنا لغة C++بنوع ثالث من األقسام يدعى القسم احملمي
. protectedنعرف في هذا القسم جميع املتغيرات والدوال املنتمية التي نريد أن نكون
قادرين على استخدامها من جانب الدوال املنتمية لألصناف املشتقة.
وعلى سبيل املثال ،سنعرف الصنف propertyبحيث يحتوي على املتغيرين املنتميني
Priceو Monthly_Rentوالـ ــدوال الــخاصة مبعاجلتهما كالبناء إلعطائها قيما ابتدائيــة و
increase_rentو decrease_rentلزيادة األجرة وإنقاصها على الترتيب ،باإلضافة إلى
increase_priceو decrease_priceلزيادة السعر وإنقاصه على الترتيب .الحظ أن
تعريف هذه املتغيرات والدوال اخلاصة مبعاجلتها في الصنف األساس يجعلنا قادرين على
توريثها جلميع األصناف املشتقة بحيث تستخدم عليها من دون احلاجة إلى كتابتها مرة أخرى.
{class property
protected:
;float Price
;float Monthly_Rent
public:
)property(float P=10000.0, float Rent=100.0
;{Price=P
;Monthly_Rent=Rent
}
)float increase_rent(float amt
;{ Monthly_Rent +=amt
};return Monthly_Rent
)float decrease_rent(float amt
216
األصناف املشتقة والقوالب الوحدة اخلامسة
;{ Monthly_Rent -=amt
};return Monthly_Rent
)float increase_price(float amt
;{ Price+=amt
};return Price
)decrease_price(float amt
;{ Price -=amt
};return Price
;}
الحظ ،عزيزي الدارس ،وجود القسم احملمي protectedفي تعريف هذا الصنف
حيث عرفنا فيه املتغيرين املنتميني Priceو ،Monthly_Rentمما يجعلها قابلة للمعاجلة
من طرف الدوال املنتمية لألصناف املشتقة من هذا الصنف .الحظ أيض ًا أننا استخدمنا
قيم ًا تلقائية في كتابة البناء كما أننا كتبنا جميع الدوال املنتمية للصنف propertyكدوال
سطرية وذلك لصغر حجمها.
إن تعريف صنف مشتق ال يختلف كثير ًا عن تعريف الصنف األساس إال في حتديد
الصنف أو األصناف التي اشتق منها وحتديد نوع الوراثة .ويتم ذلك بوضع العملية :
بعد اسم الصنف املشتق ثم تعدد أسماء األصناف األساس على أن يسبق كل اسم بنوع
الوراثة .ويفصل اسم كل صنف عن الذي يليه بفاصلة.
وعلى سبيل املثال سنعرف اآلن أول صنف مشتق وهو الصنف ،houseحيث
يحتوي هذا الصنف على متغيرين منتميني جديدين خاصني به هما BAreaو .GArea
ميثل املتغير BAreaمساحة البناء بينما ميثل املتغير GAreaمساحة احلديقة.
class house:public property
{private:
;float BArea
;float GArea
public:
;)house(float P=10000.0,float Rent=100.0,float B=150.0,float G=0.0
};float area(){return BArea+GArea
;)(void print_info
;}
217
األصناف املشتقة والقوالب الوحدة اخلامسة
الحظ ،عزيزي الدارس ،أننا كتبنا بعد اسم الصنف public propertyمما يعني
أن الصنف houseهو صنف مشتق من الصنف propertyوأن نوع الوراثة هي وراثة
عامة .publicإن كون الصنف houseصنف ًا مشتق ًا من الصنف propertyيعني أن هذا
الصنف يرث املتغيرات والدوال املنتمية للصنف .propertyولذا فإن املتغيرات Price
و Monthly_Rentهي أيض ًا متغيرات منتمية إلى الصنف .houseوليس هذا فحسب
بل إن الدوال اخلاصة مبعاجلتها وهي increase_rentو decrease_rentو _decrease
priceو increase_priceهي أيض ًا دوال منتمية للصنف houseمما ميكننا من استدعاء
هذه الدوال على أي كائن من الصنف .houseأي أننا أعدنا استخدام هذه الدوال على
كائنات من صنف جديد من دون احلاجة إلى إعادة كتابتها.
والسؤال اآلن هو :كيف سنتعامل مع هذه املتغيرات و الدوال املنتمية التي ورثها
الصنف house؟ هل نتعامل معها كمتغيرات خاصة أم عامة داخل الصنف house؟
إن ما يحدد ذلك هو نوع الوراثة (احملدد بالكلمة الواردة قبل اسم الصنف األساس في
تعريف الصنف املشتق) ،وهذا النوع في مثالنا أعاله هو publicأي وراثة عامة .إن نوع
الوراثة publicيعني أن:
1 .1املتغيرات والدوال املعرفة في القسم اخلاص من الصنف األساس تصبح غير
قابلة للمعاجلة من طرف الدوال املنتمية للصنف املشتق.
2 .2املتغيرات املعرفة في القسم احملمي protectedمن الصنف األساس تبقى
محمية بالنسبة للصنف املشتق (أي كما لو كانت معرفة في القسم protected
من الصنف املشتق)؛ ما يعني أنه من املمكن معاجلتها من طرف الدوال املنتمية
والصديقة للصنف املشتق وألي صنف مشتق منه.
3 .3أما املتغيرات والدوال املعرفة في القسم العام publicمن الصنف األساس فإنها
تبقى عامة بالنسبة للصنف املشتق (أي كما لو كانت معرفة في القسم public
اخلاص بالصنف املشتق) مما يعني إمكانية معاجلتها من طرف أي دالة.
218
األصناف املشتقة والقوالب الوحدة اخلامسة
;{BArea=B
};GArea=G
إن كل ما يفعله هذا البناء هو استدعاء البناء اخلاص بالصنف األساس حتى يتم إعطاء قيم
ابتدائية للمتغيرات املعرفة في الصنف األساس وهي Priceو Monthly_Rentفي مثالنا.
الحظ أننا وضعنا العبارة:
): property (P,Rent
في نهاية ترويسة البناء حتى تتم عملية استدعاء البناء propertyاخلاص بالصنف
األساس .وبالطبع ليس من الضروري وضع هذه العبارة على سطر جديد .بعد استدعاء
البناء propertyيتم تنفيذ اجلمل املوجودة داخل البناء houseوهي تقوم بإعطاء املتغيرين
BAreaو GAreaقيما ابتدائية .إن هذا شكل آخر إلعادة استخدام الدوال عن طريق
استدعائها في املوقع املناسب.
لنعرف اآلن الدالة print_infoاملنتمية للصنف .houseالتي تقوم بطباعة املعلومات
كافة عن كائن من الصنف houseمثل سعره وأجرته الشهرية وساحة احلديقة ومساحة
البناء واملساحة الكلية.
)(void house::print_info
{cout<<endl<<”The Price is “<<Price
<<”\n The Montly Rent is “<<Monthly_Rent
<<”\n The house area is “<<BArea
<<”\n The area of the Garden is “<<GArea
)(<<”\n The Total Area(Garden+Building) is “<<area
;<<endl
;}
من املهم أن تالحظ ،عزيزي الدارس ،أنه ما كان لهذه الدالة أن تستطيع معاجلة
املتغيرين Priceو Monthly_Rentلو لم يكونا معرفني في القسم اخلاص من الصنف
.property
219
األصناف املشتقة والقوالب الوحدة اخلامسة
نشاط ()1
ق��م ،عزيزي ال���دارس ،بتنفيذ تعريف الصنف propertyوالصنفhouse
ودوالهما املنتمية .ثم غير القسم protectedفي الصنف propertyبحيث يصبح
خاص ًا وذلك باستبدال الكلمة protectedبالكلم privateوحاول ترجمة البرنامج
اجلديد والحظ النتيجة.
الحظ ،عزيزي الدارس ،أن هذا الصنف يضم ،باإلضافة إلى املتغيرين و price
220
األصناف املشتقة والقوالب الوحدة اخلامسة
، Monthly_Rentاملتغير Areaالذي ميثل مساحة الشقة .ولذا فإن البناء اخلاص
بهذا الصنف يقوم بعد استدعاء البناء اخلاص بالصنف األساس بإعطاء هذا املتغير قيمة
ابتدائية.
)flat::flat(float P,float Rent,float A): property(P,Rent
;{ Area=A
}
الحظ أيضا ،عزيزي الدارس ،أننا عرفنا الدالة السطرية areaالتي تقوم بحساب املساحة
الكلية للمبنى بضرب عدد الطوابق بعدد الشقق في كل طابق ثم مبساحة الشقة . Area
والدالة print_infoتقوم بعرض جميع البيانات اخلاصة بكائن من الصنف _Multi
. Building
)(void Multi_Building::print_info
{cout<<endl<<”The Price is “<<Price
<<”\n The Montly Rent is “<<Monthly_Rent
<<”\n The flat area is “<<Area<<endl
<<”\n No of Stories is “<<NoStory
<<”\n No of flats per story is “<<NoFlats
;<<endl
}
في احلقيقة ،نحن لم نستفد في مثالنا أعاله من جميع املزايا التي متنحنا إياها لغة
C++إلعادة استخدام الدوال code reuseإذ كان بإمكاننا أن نعرف الدالة print_info
للصنف األساس propertyبحيث تقوم بطباعة قيم املتغيرين Monthly_Rentو Price
كما يلي:
)(void property::print_info
{cout<<”\n The price is “<<Price
<<”\n The Monthly Rents is “<<Monthly_Rent
;<<endl
}
وبالطبع ،يجب أن نضع منوذج ًا لهذه الدالة داخل تعريف الصنف .وبهذا نستطيع استدعاء
هذه الدالة من خالل الدالة print_infoاخلاصة باألصناف املشتقة من الصنف propertyأي
من خالل print_infoاخلاصة بالصنف houseو print_infoاخلاصة بالصنف .flat
222
األصناف املشتقة والقوالب الوحدة اخلامسة
باإلضافة إلى استدعاء الدالة print_infoاخلاصة بالصنف ،propertyتقوم الدالة
print_infoاخلاصة بالصنف houseبطباعة املتغيرات املعرفة مباشرة داخل الصنف
houseكما يلي:
)(void house::print_info
;)({ property::print_info
cout<<”\n The house area is “<<BArea
<<”\n The area of the Garden is “<<GArea
)(<<”\n The Total Area(Garden+Building) is “<<area
;<<endl
;}
كما أنه ميكننا أن نستدعي الدالة print_infoاخلاصة بالصنف flatمن خالل الدالة
print_infoاخلاصة بالصنف .Multi_Building
)(void Multi_Building::print_info
223
األصناف املشتقة والقوالب الوحدة اخلامسة
;)({ flat::print_info
cout<<»\n No of Stories is «<<NoStory
<<»\n No of flats per story is «<<NoFlats
;<<endl
}
الحظ ،عزيزي الدارس ،أن اجلملة:
;)(flat::print_info
تستدعي الدالة Print_infoاخلاصة بالصنف .flat
نشاط ()2
قم ،عزيزي ال��دارس ،باستخدام احلاسوب لتنفيذ جميع األصناف املعرفة أعاله
ودوالها املنتمية واستخدم التعريف اجلديد للدالة print_infoجلميع األصناف وقم
بكتابة الدالة mainالختبار هذه األصناف والدوال.
تدريب ()1
مكتبة لبيع الكتب تتعامل مع عدة أنواع من املنشورات :publicationsكتب
مدرسية ،text booksمجالت ،journalsكتب محررة ( edited booksحتتوي
على مجموعة من املقاالت املتخصصة) .لكل من هذه املنشورات سعر price
و دار نشر .publisherلكل كتاب م��درس��ي هنالك مؤلف authorباإلضافة
إل��ى موضوع معني .subjectلكل مجلة هنالك محرر editorورس��م اشتراك
محرر .editorنظم هذه األصناف عن
محرر هنالك ِّ
.subscriptionلكل كتاب َّ
طريق رسم شجرة هرمية تبني األصناف األساس واألصناف املشتقة.
تدريب ()2
225
األصناف املشتقة والقوالب الوحدة اخلامسة
محمي Protected محمي Protected خاص Private محمي Protected
عام Public محمي Protected خاص Private عام Public
جدول ( )1العالقة بني نوع الوراثة للبيانات والدوال في الصنف األساس واملشتق -مستوى الوصول
للعناصر املوروثة
الوراثة اخلاصة
إذا سبق اسم الصنف األساس بالكلمة privateفهذا يعني أن نوع الوراثة هي وراثة
خاصة .وفي هذه احلالة تورث جميع املتغيرات والدوال املعرفة في القسم احملمي
protectedوالعام publicمن الصنف األساس بحيث تصبح خاصة في الصنف املشتق
(كما لو كانت معرفة في القسم اخلاص من الصنف املشتق).
أما املتغيرات والدوال املعرفة في القسم اخلاص من الصنف األساس فإن الدوال
املنتمية للصنف املشتق ال تستطيع معاجلتها .ويقال في هذه احلالة أنها غير قابلة للمعاجلة
inaccessible.وهي عبارة غير دقيقة متام ًا إذ ميكن معاجلتها في الصنف املشتق لكن باستخدام الدوال
املنتمية للصنف األساس فقط واملعرفة (أي الدوال) في القسم العام أو احملمي.
الوراثة احملمية
أما إذا سبق اسم الصنف األساس بالكلمة protectedفإن الوراثة في هذه احلالة وراثة
محمية .protectedوفي هذه احلالة ،فإن املتغيرات والدوال املعرفة في القسم احملمي
protectedوالعام publicمن الصنف األساس تصبح محمية في الصنف املشتق (كما لو
كانت معرفة في القسم احملمي من الصنف املشتق).
أما املتغيرات والدوال املعرفة في القسم اخلاص من الصنف األساس فإن الدوال
املنتمية للصنف املشتق ال تستطيع معاجلتها .ويقال في هذه احلالة أنها غير قابلة للمعاجلة
inaccessible؛ إذ ال ميكن معاجلتها في الصنف املشتق إال باستخدام الدوال املنتمية
للصنف األساس فقط واملعرفة (أي الدوال) في القسم العام أو احملمي.
وميكن ان نلخص ما سبق مبا يلي؛ إن الصنف املشتق يرث جميع املتغيرات والدوال
العامة واحملمية من الصنف األساس ،وهذا يشير أو يدل على أنه ميكن اعتبار جميع
املتغيرات والدوال العامة واحملمية من الصنف األساس كأنها معلن عنها في األصناف
226
األصناف املشتقة والقوالب الوحدة اخلامسة
املشتقة .لنوضح ذلك دعنا ،عزيزي الدارس ،نأخذ املثال التالي حيث إن Xهي الصنف
األساس وإن Yهي صنف مشتق من الصنف Xكما هو موضح في البرنامج التالي:
class X
{ public:
;int a
protected:
;int b
private:
;int c
;}
class Y : public X
{ public:
;int d
;}
وعلى فرض أنه مت تعريف الكينونات Objectsالتالية xو yكما يلي :
;X x
;Y y
فإنه ميكننا ان نوضح ذلك من خالل شكل (:)2
(وراثة عامة)
xكائن من الصنف X yكائن من الصنف Y
صنف Yيرث صنف X
الشكل ( :)2مثال يوضح العالقة بني نوع الوراثة للبيانات (املتغيرات) والدوال املنتمية في األصناف
األساسية واملشتقة
فكما نرى من الشكل أن املتغير العام publicاملنتمي aمن الصنف األساس Xورث
كمتغير عام إلى Yوكذلك املتغير احملمي protectedاملنتمي bمن الصنف األساس X
ورث كمتغير محمي إلى .Yولكن املتغير اخلاص privateاملنتمي cمن الصنف األساس
Xلم يورث كمتغير إلى . Yواخلط األفقي في كل كينونة في شكل ( )2يدل على فصل
مناطق املتغيرات العامة واخلاصة واحملمية لكل كينونة.
227
األصناف املشتقة والقوالب الوحدة اخلامسة
أسئلة التقومي الذاتي ()1
ادرس املثال التالي وبني اخلطأ في البرنامج
class X
{ protected:
;int a
;}
class Y : public X
{ public:
} ;void set(X x,int c) { x.a = c
;}
228
األصناف املشتقة والقوالب الوحدة اخلامسة
الشكل ( :)3الوراثة املتعددة
229
األصناف املشتقة والقوالب الوحدة اخلامسة
{
int a;
public:
B1(int x)
{
a=x;
}
int get_a()
{ return a;
}
};
// Class D1 derived from Class B1
class D1:public B1
{
int b;
public:
D1(int x,int y):B1(y)
{
b=x;
}
int get_b()
{
return b;
}
};
class D2:public D1
{
int c;
public:
D2(int x,int y,int z):D1(y,z)
{
c=x;
}
void show()
230
األصناف املشتقة والقوالب الوحدة اخلامسة
{
;cout<<get_a()<<»\n»<<get_b()<<»\n»<<c<<endl
;}}
)(int main
{
;int a,b,c
;»cout<<»Enter a,b,c \n
;cin>>a>>b>>c
;)D2 ob(c,b,a
;)(ob.show
;return 0
}
في هذا املثال ستكون هرمية التوارث كما يلي B1 :هي األساس و الصنف D1يرث
الصنف B1وأخير ًا الصنف D2يرث الصنف .D1
233
األصناف املشتقة والقوالب الوحدة اخلامسة
public:
D()
{
cout<<»D Constructor\n»;
}
~D()
{
cout<<»D Destructor\n»;
system(«PAUSE»);
}
};
int main()
{
B1 ob1;
{
B2 ob2;
}
D ob;
system(«PAUSE»);
return 0;
}
234
األصناف املشتقة والقوالب الوحدة اخلامسة
ويجب عليك ،عزيزي الدارس ,أن تتذكر:
عدة أصناف موروثة فإن دوال البناء يتم تنفيذها من اليسار إلى - 1أنه في حالة وراثة ّ
اليمني والعكس بالنسبة لدوال الهدم.
- 2يتم إرسال املعامالت لدالة بناء الصنف املوروث ( األصناف املوروثة ) عبر دالة بناء
الصنف الوارث.
235
األصناف املشتقة والقوالب الوحدة اخلامسة
.3الدوال واألصناف القالبية
Template Functions and Classes
من املميزات املهمة للغة C++التي جتنبنا احلاجة إلى كتابة دوال متشابهة إلى حد كبير
هي أنها متنحنا إمكانية تعريف دوال وأصناف قالبية ،تسمح لنا هذه الدوال واألصناف
القالبية القدرة على تأجيل حتديد نوع البيانات إلى حني احلاجة إلى هذه القوالب؛ مما
يسمح لنا بإعادة استخدام هذه القوالب مرة بعد مرة وألنواع مختلفة من البيانات.
وبذلك تعطينا لغة C++طريقة أخرى إلعادة استخدام البرامج code reuseال تقل
أهمية عن الوراثة واألصناف املشتقة .وهنالك نوعان من القوالب :الدوال القالبية
واألصناف القالبية .وفيما يلي سنناقش ك ً
ال منها.
236
األصناف املشتقة والقوالب الوحدة اخلامسة
إن تعريف الدوال القالبية ال يختلف عن تعريف الدوال االعتيادية إال في أول جملة
أو ما يسمى بترويسة الدالة؛ إذ تبدأ الدالة القالبية بالكلمة templateلإلشارة إلى أنها دالة
قالبية ،ثم بقائمة من العوامل تبدأ بالرمز< و تنتهي بالرمز > .وفي مثالنا أعاله هنالك
عامل واحد لهذه الدالة القالبية وهو class Tوتعني الكلمة classأن نوع العامل Tهو
اسم لنوع من أنواع البيانات مثل intو floatو… إلخ .وقد استخدم هذا العامل في
تعريف xو yإذ إنها من النوع . T
أما طريقة استدعاء الدالة القالبية فال تختلف عن طريقة استدعاء الدالة االعتيادية،
واملثال التالي يوضح كيفية استدعاء الدالية القالبية أعاله للمقارنة بني رقمني صحيحني.
)(main
;{ int a=2,b=1
;)cout<<IsGreater(a,b
}
237
األصناف املشتقة والقوالب الوحدة اخلامسة
}
;]T a=st[pos
;]st[pos]=st[i
;st[i]=a
} //for i
} //sort
الحظ ،عزيزي الدارس ،أن العامل stهو عنوان أول عنصر في املصفوفة لذا ،فهو
من النوع * ، Tبينما ميثل العامل mعدد عناصر املصفوفة لذا ،فهو من النوع .intالحظ
أيضاً ،عزيزي الدارس ،كيف استخدمنا النوع العام Tلتعريف متغيرات محلية داخل
الدالة مثل minو . aواملثال التالي يبني كيفية استخدام الدالة sortلترتيب مصفوفة
نعرف العملية < للصنف من املوظفني ومصفوفة أخرى من الطلبة .ومن املهم هنا أن ّ
employeeوأيض ًا للصنف studentحيث إنها تستخدم داخل الدالة sortملقارنة ]st[j
مع . minويعود األمر للمبرمج في كيفية تعريف هذه العمليات .في مثالنا التالي سنعرف
العملية < للصنف employeeعلى أساس مقارنة راتبي املوظفني .أما الصنف student
فسنعرف العملية < على أساس مقارنة معدل عالمات الطالبني .ومن ثم ،فإننا عندما
نستدعي sortلترتيب مصفوفة من املوظفني فإنه سيرتب تلك املصفوفة حسب رواتب
هؤالء املوظفني .أما عندما نستدعيه لترتيب مصفوفة من الطلبة ،فإنه سيرتبها حسب
معدل عالمات الطلبة.
>#include<iostream.h
{class employee
;long empNo
;]char empName[20
;double salary
public:
;)(void initialize
};double get_salary(){return salary
;)friend int operator<(employee s1, employee s2
;}
)(void employee::initialize
;“ {cout<<”enter emp name
238
األصناف املشتقة والقوالب الوحدة اخلامسة
cin>>empName;
cout<<”enter emp Number “;
cin>>empNo;
cout<<”enter emp salary “;
cin>>salary;
}
int operator<(employee s1,employee s2)
{if (s1.salary<s2.salary)
return 1;
else
return 0;
}
class student{
long stno;
int csno;
int courses[100];
char stName[20];
public:
double average();
void initialize();
long get_stno() {return stno;}
char* get_stname(){return stName;}
friend int operator<(student s1,student s2);
};
double student::average()
{ double sum=0;
for(int i=0;i<csno;i++)
sum=sum+courses[i];
return sum/csno;
}
int operator<(student s1,student s2)
{if (s1.average()<s2.average())
return 1;
239
األصناف املشتقة والقوالب الوحدة اخلامسة
else
return 0;
}
void student::initialize()
{
cout<<»enter student’s number»;
cin>>stno;
cout<<»enter student’s name «;
cin>>stName;
cout<<»enter no. of courses taken by the student»;
cin>>csno;
for(int i=0;i<csno;i++)
{cout<<»enter grade no.»<<i+1;
cin>>courses[i];
}
}
main()
{ student stu[100];
240
األصناف املشتقة والقوالب الوحدة اخلامسة
;int n
;»cout<<»Enter no. of students
;cin>> n
)for(int i=0;i<n;i++
;)(stu[i].initialize
// call sort to sort the students according to average
;)sort(stu,n
)for(i=0; i<n;i++
;”cout<<stu[i].average()<<”\n
;]employee emp[100
;cout<<”enter number of employees”<<endl
;cin>>n
)for(i=0;i<n;i++
;)(emp[i].initialize
;)sort(emp,n
)for(i=0;i<n;i++
;”cout<<emp[i].get_salary()<<”\n
}
نشاط ()3
قم ،عزيزي الدارس ،بتنفيذ البرنامج أعاله مستخدم ًا احلاسوب وقم بتعديله
بحيث يرتب مصفوفة الطلبة حسب أسماء الطالبة .الحظ أن كل ما حتتاج عمله
هو فقط إعادة تعريف العملية < بحيث تقارن بني الطلبة حسب أسمائهم.
تدريب ()3
اكتب دالة قالبية تأخذ مصفوفة من نوع ما (كـ studentأو )employeeوتقوم
بطباعة تلك القائمة .حلل هذا التدريب ،عليك ،عزيزي ال��دارس ،أن تعرف
العملية << للصنف الذي تود طباعة مصفوفة مشكلة منه (مثل الصنف student
والصنف .)employee
241
األصناف املشتقة والقوالب الوحدة اخلامسة
3.3األصناف القالبية Template Classes
تذكر ،عزيزي الدارس ،أننا عرفنا الصنف ) (sectionشعبة في الوحدة السابقة على
أنها مجموعة من الطلبة من الصنف ) (studentوعرفنا الدوال املنتمية الالزمة ملعاجلتها
إلضافة طالب جديد أو حذف طالب… إلخ) .تصور اآلن أننا بحاجة إلى شعبة من نوع
جديد من الطلبة ،طلبة دراسات عليا Postgradsعلى سبيل املثال( الذي قد يختلف
عن الصنف studentلوجود متغير منتم ميثل اسم املشرف ،ومتغير منتم آخر ميثل سنة
إنهاء الشهادة اجلامعية األولى .ولنفرض أننا بحاجة إلى الدوال نفسها ملعاجلة هذا النوع
اجلديد من الشعب .واضح أننا لو عرفنا هذا النوع اجلديد من الشعب لكررنا كتابة الكثير
من الدوال والتعاريف ال تختلف عن الصنف sectionإال في نوع البيانات التي تعالج.
ولتجنب ذلك تسمح لنا لغة C ++بأن نعرف الصنف ونستخدم في تعريف متغيراته
املنتمية أنواع ًا عامة مترر كعوامل .يسمى ذلك الصنف بالصنف القالبي Template
.Classفمثالً ،لو عرفنا الصنف sectionكصنف قالبي فإن هذا ميكننا من استخدام هذا
الصنف إلنشاء شعبة متغيراتها املنتمية من نوع studentوشعبة أخرى متغيراتها املنتمية
من الصنف PsotGradدون احلاجة إلى تكرار تعريف الصنف sectionأو أي من دواله
املنتمية.
سنعرف اآلن الصنف sectionكصنف قالبي حيث سنعرف املصفوفة secمن النوع
ال آخر لهذا العام Tالذي هو عامل من عوامل هذا الصنف القالبي كما سنستخدم عام ً
الصنف MaxSizeوميثل عدد عناصر الشعبة.
{template <class T, int MaxSize> class section
;]T sec[MaxSize
;int size
;)int bsearch(long stno
public:
};section(){size=0
;)void StAdd(T s
;)int StDelete(long stno
;)(void Stlist
;)void StRetrieve(long stno
;}
242
األصناف املشتقة والقوالب الوحدة اخلامسة
الحظ ،عزيزي الدارس ،أن تعريف هذا الصنف اختلف عن تعريف الصنف
sectionاملعرف في الوحدة الرابعة من النواحي التالية:
.1بدأنا تعريف الصنف بالعبارة:
>template <class T, int MaxSize
التي تدل على أن هذا الصنف هو صنف قالبي له عامالن:
األول Tميثل نوع البيانات والثاني MaxSizeميثل حجم املصفوفة.
.2إن املصفوفة secهي اآلن من النوع Tوحجمها ،أي عدد عناصرها هو .MaxSize
عند إنشاء كائن من هذا الصنف القالبي يجب تزويد هذه العوامل ( Tو )MaxSize
بالقيم الفعلية لها .على سبيل املثال ،إلنشاء شعبة من الطلبة من النوع studentوبحجم
50عنصر ًا فإننا نستخدم اجلملة:
;section<student, 50> cs100
واآلن نستطيع استخدام الكائن ( cs100وهو شعبة من الطلبة) كأي كائن آخر
مستخدمني الدوال املنتمية له .وإلنشاء شعبة من الطلبة من الصنف postgradوبحجم
20طالب ًا فإننا نستخدم اجلملة:
;section<postgrad, 20> cs700
واآلن لدينا الكائن cs700الذي ميثل شعبة من طلبة الدراسات العليا؛ ونستطيع
استخدام هذا الكائن من خالل الدوال املنتمية املعرفة للصنف القالبي.
أما تعريف الدوال املنتمية للصنف القالبي sectionفيجب أن يسبق تعريف أي
دالة بالعبارة:
>template<class T, int MaxSize
ثم يأتي نوع القيمة املرجعة ثم اسم الصنف القالبي فالعوامل (بدون حتديد النوع)
بني الرمزين > < .وفيما يلي نعرف الدوال املنتمية للصنف القالبي .section
، StAdd .1تقوم هذه الدالة بإضافة الكائن sمن النوع العام Tإلى الـ section
>template<class T, int MaxSize
)void section<T,MaxSize>::StAdd(T s
;{int pos=0
// search for the proper insertion position
)while(s.get_stno() > sec[pos].get_stno() && pos<size
;++pos
243
األصناف املشتقة والقوالب الوحدة اخلامسة
// shift elements one position up
)for(int i=size-1;i>=pos;i--
;]sec[i+1]=sec[i
;sec[pos]=s
;size++
}
،bsearch .3تقوم هذه الدالة باستخدام خوارزمية البحث الثنائي للبحث عن طالب
معني في الشعبة مستخدمني رقمه .ومن املهم جد ًا أن تالحظ ،عزيزي الدارس،
أن هذه الدالة تفترض أن لكل طالب مهما كان نوعه( PostGradأو ) student
رقما وأن الدالة get_stnoتسترجع قيمة هذا الرقم ألي طالب؛ مما يعني أن علينا
تعريف الدالة )( get_stnoللصنف studentوللصنف .PostGrad
// binary search
>template<class T, int MaxSize
)int section<T,MaxSize>::bsearch(long sn
{
;int lo=0
;int hi=size-1
)while (lo <= hi
244
األصناف املشتقة والقوالب الوحدة اخلامسة
{ int mid =(lo+hi) /2;
long r=sec[mid].get_stno();
if (r== sn)
return mid;
else
if (sn>r)
lo=mid+1;
else
hi = mid;
} //while
return -1;
}
245
األصناف املشتقة والقوالب الوحدة اخلامسة
else
cout<<”Student no “<<stno<<” was not found \n”;
}
ً باستخدام الصنف القالبي أعاله البد أوstudent إلنشاء شعبة من الطلبة من النوع
ال
get_ يجب أن نعرف لهذا الصنف الدالة،ً وكما قلنا سابقا.student من تعريف الصنف
:student وفيما يلي تعريف محتمل للنوع. << و العمليةstno
#include<iostream.h>
class student{
long stno;
int csno;
double grades[100];
char StName[20];
public:
double average();
void initialize();
long get_stno() {return stno;}
char* get_stname(){return StName;}
double get_grade(int i);
friend ostream& operator<<(ostream& c, student t);
};
ostream& operator<<(ostream& c, student t)
{c<<”\n Student Name “<<t.StName;
c<<”\n Student No “<<t.stno;
c<<”\n Number of courses “<<t.csno;
c<<”\n the average grades is”<<t.average()<<endl;
return c;
}
ً والدالة الرئيسة التالية تعطي مثا
وكيفيةsectionال على كيفية إنشاء كائن من الصنف
.استخدامه
main()
{ // create a section of type student
section<student, 50> cs100;
246
األصناف املشتقة والقوالب الوحدة اخلامسة
int choice;
do
{cout<<”\n\n \t Enter 1 to add a new student \n”;
cout<<”\t Enter 2 to delete a student \n”;
cout<<”\t Enter 3 to some information about a student \n “;
cout<<”\t Enter 4 to get a list of all students \n”;
cout<<”\t Enter 5 to Exit \n”;
cin>>choice;
switch(choice){
case 1: // student add
student s;
s.initialize();
cs100.StAdd(s);
break;
case 2: // delete a student
long sn;
cout<<”Enter student number “;
cin>>sn;
cs100.StDelete(sn);
break;
case 3: // retrieve some data about an employee
cout<<”Enter student number “;
cin>>sn;
cs100.StRetrieve(sn);
break;
case 4: // list information about all students
cout<<”Students information \n\n”;
cs100.Stlist();
break;
case 5: cout<<»End of program \n»;
break;
default: cout<<»invalid choice ... Try again «;
}}
while(choice!=5);
}
247
األصناف املشتقة والقوالب الوحدة اخلامسة
نشاط ()4
قم ،عزيزي الدارس ،بتنفيذ الصنف القالبي sectionودواله املنتمية والصنف
studentودواله املنتمية باإلضافة إلى الدالة الرئيسة أعاله ،وقم باختبار البرنامج.
تدريب ()4
ق��م ،ع��زي��زي ال����دارس ،بتعريف الصنف ،PostGradال���ذي ميثل طالب
دراسات عليا واحرص على أن يختلف تعريف PostGradعن تعريف student
بطريقة ما (مثل أن يحتوي على اس��م املشرف على األق���ل) .ثم قم باستخدام
الصنف القالبي sectionإلنشاء كائن ميثل شعبة من طلبة الدراسات العليا وإجراء
العمليات املختلفة عليها مثل إضافة طالب وحذف طالب …إلخ .تذكر ،عزيزي
الدارس ،أن تعرف الدالة get_stnoوالعملية << للصنف . PostGrad
تدريب ()5
اختر ،عزيزي ال���دارس ،طريقة ديناميكية لتعريف الصنف sectionمن
الوحدة الرابعة وقم بتحويل ذلك الصنف إلى صنف قالبي واستخدامه إلنشاء
شعبة من الطلبة studentوشعبة أخرى من طلبة الدراسات العليا . PstGrad
248
األصناف املشتقة والقوالب الوحدة اخلامسة
Polymorphism .4تعدد األوجه
توفر لغات البرمجة الكينونية العديد من القدرات وامليزات البرمجية التي حتدثنا
عنها في األقسام السابقة والتي تعمل جميع ًا على حتسني هيكلية تصميم البرنامج وإعادة
استخدامه .وفي هذا القسم ،عزيزي الدارس ،سوف نناقش مفهوم جديد وهو تعدد
األوجه Polymorphismفي لغة .C++
249
األصناف املشتقة والقوالب الوحدة اخلامسة
الحظ أن الدالة )( getDataالتي ستستخدم حقيقة تعتمد على القيمة املخزنة
في ]persPtr[n++؛ مما يعني أنه لو كانت القيمة املخزنة هي عنوان كائن من الصنف
studentفإن )( getDataاخلاصة بالصنف studentهي التي تستخدم .بينما لو كانت
القيمة املخزنة هي عنوان لكائن من الصنف professorفإن الدالة )( getDataاخلاصة
بهذا الصنف هي التي تستخدم.
وللقيام بذلك بشكل صحيح يجب أن حتدد الدالة املطلوبة أثناء تنفيذ البرنامج (وليس
أثناء ترجمته)؛ إذ عند التنفيذ فقط سنعرف طبيعة القيم املخزنة في املصفوفة .وإلخبار
مترجم compilerلغة C++بأن يؤجل عملية حتديد هوية )( getDataالذي سيستخدم
إلى وقت تنفيذ البرنامج (حتى نعلم قيمة املصفوفة )persPtrال بد من تعريف الدوال
)( getDataكدوال وهمية . virtual functionsويتم ذلك بوضع الكلمة virtualقبل
منوذج )(getDataفي تعريف كل صنف من األصناف personو studentو .professor
وال يجوز كتابة الكلمة virtualقبل تعريف الدالة نفسها .في الواقع يكفي كتابة الكلمة
virtualقبل منوذج )(getDataاخلاص بالصنف األساس فقط .وفيما يلي تعريف
األصناف personو studentو . professor
الحظ ،عزيزي الدارس ،أن االختالف الوحيد في تعريف هذه األصناف هو وجود
الكلمة virtualقبل منوذج )( getDataوأن ال اختالف في تعريف الدوال )(getData
أو أي دالة أخرى.
>#include <iostream
class person //person class
{
protected:
;]char name[40
public:
)(void getName
} ; { cout<<“ Enter name: “; cin >> name
)(void putName
} ; { cout << “Name is: “ << name << endl
virtual void getData() { } //pure virtual func
virtual bool isOutstanding() {return false;} //pure virtual func
;}
////////////////////////////////////////////////////////////////
250
األصناف املشتقة والقوالب الوحدة اخلامسة
class student : public person //student class
{
private:
float gpa; //grade point average
public:
void getData() //get student data from user
{
person::getName();
cout << “ Enter student’s GPA: “; cin >> gpa;
}
bool isOutstanding()
{ return (gpa > 3.5) ? true : false; }
};
////////////////////////////////////////////////////////////////
class professor : public person //professor class
{
private:
int numPubs; //number of papers published
public:
void getData() //get professor data from user
{
person::getName();
cout << “ Enter number of professor’s publications: “;
cin >> numPubs;
}
bool isOutstanding()
{ return (numPubs > 100) ? true : false; }
};
251
األصناف املشتقة والقوالب الوحدة اخلامسة
{
person* persPtr[100]; //array of pointers to persons
int n = 0; //number of persons on list
char choice;
do {
cout << “Enter student or professor (s/p): “;
cin >> choice;
if(choice==’s’) //put new student
persPtr[n] = new student; // in array
else //put new professor
persPtr[n] = new professor; // in array
persPtr[n++]->getData(); //get data for person
cout << “ Enter another (y/n)? “; //do another person?
cin >> choice;
} while( choice==’y’ ); //cycle until not ‘y’
for(int j=0; j<n; j++) //print names of all
{ //persons, and
persPtr[j]->putName(); //say if outstanding
if( persPtr[j]->isOutstanding() )
cout << “ This person is outstanding\n”;
}
return 0;
} //end main
ومن اجلدير بالذكر أن منوذج الدوال الوهمية في األصناف املشتقة يجب أن يبقى كما
.هو في الصنف األساس
)5( نشاط
. بتنفيذ البرنامج أعاله وحتقق من النتيجة، عزيزي الدارس،قم
252
األصناف املشتقة والقوالب الوحدة اخلامسة
ستالحظ من نشاط 5أن البرنامج يدخل معلومات عن الدارس موسى ومعدله
88وثم يدخل معلومات عن املشرف يوسف وأخيرا معلومات الدارس علي ويطبع
املعلومات املدخلة ويطبع ان موسى هو متفوق ألن الدالة )( isOutstandingاخلاصة
بالصنف األساس studentهي فقط التي تستخدم ألن املترجم حدد تلك الدالة وقت
التنفيذ ألن املصفوفة هي مصفوفة من املؤشرات للصنف األساس (كما هو موضح في
الشكل .)5
تدريب ()6
أدرس ،عزيزي الدارس ،البرنامج التالي الذي ينشئ دائما عقارات ويعطي
عدل ،عزيزي الدارس ،البرنامج بحيث متغيراتها املنتمية قيما تلقائياّ .
.1يصبح يقرأ قيما لتلك املتغيرات .قد يكون احلل األمثل هو أن تعرف الدالة
)( read_infoكدالة وهمية جلميع أصناف العقارات.
.2يقوم بترتيب قائمة العقارات حسب مساحة كل منها .الحظ أنك يجب أن
تستخدم الدالة الوهمية get_areaوالتي تعيد مساحة الكائن .يجب أن
253
األصناف املشتقة والقوالب الوحدة اخلامسة
ألنها حتسب املساحة بطريقة مختلفة حسب نوعية،تكون هذه الدالة وهمية
.الكائن
#include<iostream.h>
class property{
protected:
float Price;
float Monthly_Rent;
public:
property(float P=10000.0, float Rent=100.0)
{Price=P;
Monthly_Rent=Rent;
}
float increase_rent(float amt)
{ Monthly_Rent +=amt;
return Monthly_Rent;}
float decrease_rent(float amt)
{ Monthly_Rent -=amt;
return Monthly_Rent;}
float increase_price(float amt)
{ Price+=amt;
return Price;}
decrease_price(float amt)
{ Price -=amt;
return Price;}
virtual void print_info();
};
void property::print_info()
{cout<<»\n The price is «<<Price
<<»\n The Monthly Rents is «<<Monthly_Rent
<<endl;
}
class house:public property
{private:
254
األصناف املشتقة والقوالب الوحدة اخلامسة
float BArea;
float GArea;
public:
house(float P=10000.0, float Rent=100.0,float B=150.0,float
G=0.0);
float area(){return BArea+GArea;}
virtual void print_info();
};
void house::print_info()
{ property::print_info();
cout<<»\n The house area is «<<BArea
<<»\n The area of the Garden is «<<GArea
<<»\n The Total Area(Garden+Building) is «<<area()
<<endl;
};
class flat:public property
{ protected:
float Area;
public:
flat(float P=5000.0,float Rent=50.0,float A=120.0);
virtual void print_info();
};
void flat::print_info()
{ property::print_info();
cout<<»\n The flat area is «<<Area<<endl;
};
class Multi_Building: public flat
{private:
int NoStory;
int NoFlats;
public:
Multi_Building(float P=20000.0,float Rent=100.0,float
255
األصناف املشتقة والقوالب الوحدة اخلامسة
A=120.0,int S=1,int F=2);
float area(){return NoStory*NoFlats*Area;}
virtual void print_info();
};
void Multi_Building::print_info()
{ flat::print_info();
cout<<»\n No of Stories is «<<NoStory
<<»\n No of flats per story is «<<NoFlats
<<endl;
}
main()
{
} //main
تدريب ()7
قم ،عزيزي الدارس ،بدراسة البرنامج التالي وتنفيذه على احلاسوب ,البرنامج
يقوم بإيجاد مساحة الصنف مثلث CTriangleوالصنف مستطيل CRectangleاملشتق
من الشكل CPolygonكما هو موضح في البرنامج .أعد كتابة البرنامج باستخدام الدالة
الوهمية إليجاد مساحة أي من األشكال السابقة .الحظ أنك يجب أن تستخدم الدالة
الوهمية )(areaوالتي تعيد مساحة الكائن .يجب أن تكون هذه الدالة وهمية ،ألنها
حتسب املساحة بطريقة مختلفة حسب نوعية الكائن.
>#include <iostream
{ class CPolygon
protected:
;int width, height
public:
257
األصناف املشتقة والقوالب الوحدة اخلامسة
{ class CTriangle: public CPolygon
public:
)( int area
} ;){ return (width * height / 2
;}
{ )( int main
;CRectangle rect
;CTriangle trgl
;CPolygon * ppoly1 = &rect
;CPolygon * ppoly2 = &trgl
;)ppoly1->set_values (4,5
;)ppoly2->set_values (4,5
;cout << “input data for polygon are 4 and 5 “ << endl
;cout << “area of Rectangle “ << rect.area() << endl
;cout << “area of Rectangle “<< trgl.area() << endl
;int i
;cin>>i
;return 0
}
258
األصناف املشتقة والقوالب الوحدة اخلامسة
. Programmingبشكل عام ،يستخدم مصطلح تعدد األوجه في لغة ، C++لإلشارة
إلى استخدام االقترانات االفتراضية .virtual methodsويستخدمه بعض املبرمجني
بطرق مختلفة والفقرات التالية تبني االستخدامات املختلفة ملفهوم تعدد األوجه:
Static Polymorphism - 1تعدد األوجه الثابت :هو أن تأخذ الدالة عدة أشكال.
وكيف يتم ذلك ! يتم عن طريق إعادة تعريف الدالة ..Function Overloading
مثالً ،لو فرضنا أنه لدينا دالة رسم تسمى ، drawهذه الدالة تأخذ مرة مستطي ً
ال
ومرة أخرى دائرة ،مثلث ًا ..وهكذا.
;)void draw(Rectangle r
;)void draw(Circle c
;)void draw(Triangle t
في كل تعريف للدالة سنجد أن التطبيق يختلف «شكل التنفيذ يختلف» ،فمثالً،
عند رسم املستطيل فإن الدالة تأخذ في االعتبار طول املستطيل وعرضه .اما عند رسم
الدائرة ،فسنجد أن الدالة تتعامل مع نصف قطر...إلخ .نرى أن هناك اختالف ًا في
التطبيق .وهذا املثال يوضح مفهوم تعدد األوجه الثابت .Static Polymorphism
- 2التعدد الشكلي Dynamic Polymorphismأو تعدد األوجه الديناميكي :يعني
القدرة على ربط كائن من فئة رئيسة Base Classبكائن من فئة مشتقةDerived
Classوقت تنفيذ البرنامج ،عندها سيأخذ الكائن من الفئة الرئيسة عدة أشكال،
واملثال التالي يوضح ذلك:
>#include <iostream
;using namespace std
class Shape
{
public:
;)(virtual void draw
;}
259
األصناف املشتقة والقوالب الوحدة اخلامسة
private:
int height;
int width;
};
int main()
{
Shape* shape;
cout<<»n1- Rectangle»;
cout<<»n2- Circle»;
int choice;
cin>>choice;
if(choice == 1)
shape=new Rectangle;
else
shape=new Circle;
shape->draw();
return 0;
}
لدينا صنف. ندرس البرنامج السابق بصورة أكثر تفصيلية، عزيزي الدارس،دعنا
ً وهو ميثل شك، Shapeرئيس مجرد وهو
لذلك ال ميكنك إنشاء كائن، ال غير معروف
. منه مباشرة
260
األصناف املشتقة والقوالب الوحدة اخلامسة
الصنفان اآلخران هما Rectangleو ، Circleوهما موروثان من الصنف ، Shape
ويجب عليهما إعادة تعريف الدالة .draw
نأتي إلى الدالة)( ،mainنالحظ أن هناك اختيار رقم من اثنني؛ في حالة اختيار 1فإن
الشكل سيصبح مستطيل :
;shape=new Rectangle
فقبل تنفيذ البرنامج ،ال نستطيع معرفة النتيجة من استدعاء الدالة ، drawوكذلك
أثناء البدء في التنفيذ ال نستطيع معرفة النتيجة ،إال عند االختيار .وهذا هو جوهر مفهوم
تعدد األوجه.
.5اخلالصة
من أهم مميزات لغات البرمجة الكينونية هي أنها متكننا من اشتقاق أصناف
معرفة .وهذا يسمح لنا إذا أحسنا تصميم نظامنا من إعادة جديدة من أصناف ّ
استخدام كثير من ال��دوال دون احلاجة إلى إع��ادة كتابتها .كما تزودنا لغة C++
بإمكانية أخرى إلعادة استخدام الدوال واألصناف عن طريق كتابة دوال وأصناف
قالبية ال يحدد بها نوع البيانات املستخدمة؛ مما يسمح لنا بتحديد نوع جديد من
البيانات في كل مرة نستخدم هذه الدوال واألصناف القالبية .مما يجنبنا احلاجة
إلى إعادة كتابة هذه الدوال واألصناف ألنواع مختلفة من البيانات.
كما تزودنا لغة C++بخاصية تعدد األوج��ه املهمة .التي متكننا من كتابة
برامج تصعب كتابتها باستخدام لغات البرمجة اإلجرائية كالباسكال ،مثل ترتيب
قائمة من األصناف املختلفة (على أن تكون هذه األصناف مشتقة من بعضها).
261
األصناف املشتقة والقوالب الوحدة اخلامسة
.6حملة عن الوحدة الدراسية السادسة
في الوحدة السادسة سوف نتطرق إلى شرح تفصيلي عن معاجلة امللفات في لغة
C++؛ ففي القسم األول سوف نتحدث عن طريقة تعريف هيكل البيانات وموقع
امللفات بينها .والقسم الثاني والثالث سوف يشرحان ويعرفان الطرق املختلفة في
تنظيم امللفات ) (File Organizationوامللفات والينابيع ).(files and streams
أما القسم الرابع واخلامس فسوف يناقشان إنشاء امللفات التتابعية وقراءتها وحتديثها
وإنشاء امللفات العشوائية وقراءتها وحتديثها .وأخيرا إدخ��ال الكائنات(Input/
) Output of Objectsوإخراجها في لغة. C++
.7إجابات التدريبات
تدريب ()1
تدريب ()2
>#include<iostream.h
{class publication
262
األصناف املشتقة والقوالب الوحدة اخلامسة
double price;
char publisher[20];
public:
void read()
{ cout<<»Enter the price «;
cin>>price;
cout<<»Enter the publisher»;
cin>>publisher;
}
void print()
{ cout<<»The price: «;
cout<<price<<endl;
cout<<»The publisher:»;
cout<<publisher<<endl;
}
};
class text_book: public publication{
char author[20];
char subject[20];
public:
void read();
void print();
};
void text_book::read()
{ publication::read();
cout<<»Enter the author «;
cin>>author;
cout<<»Enter the subject»;
cin>>subject;
}
void text_book::print()
{ publication::print();
cout<<»The author: «;
cout<<author<<endl;
263
األصناف املشتقة والقوالب الوحدة اخلامسة
cout<<»The subject:»;
cout<<subject<<endl;
}
class edited_book: public publication{
char editor[25];
public:
void read();
void print();
};
void edited_book::read()
{ publication::read();
cout<<»Enter the editor «;
cin>>editor;
}
void edited_book::print()
{ publication::print();
cout<<»The editor: «;
cout<<editor<<endl;
}
class journal: public edited_book
{ double subscription_fee;
public:
void read();
void print();
};
void journal::read()
{ edited_book::read();
cout<<»Enter the subscription fees «;
cin>>subscription_fee;
}
void journal::print()
{ edited_book::print();
cout<<»The subscription fees :»;
cout<<subscription_fee<<endl;
264
األصناف املشتقة والقوالب الوحدة اخلامسة
}
main()
{ text_book b;
b.read();
b.print();
edited_book e;
e.read();
e.print();
journal j;
j.read();
j.print();
}
)3( تدريب
#include<iostream.h>
class employee{
long empNo;
char empName[20];
double salary;
public:
friend istream& operator>>(istream& c,employee& e);
friend ostream& operator<<(ostream& c,employee e);
double get_salary(){return salary;}
};
istream& operator>>(istream& c,employee& e)
{cout<<»enter emp name «;
c>>e.empName;
cout<<»enter emp Number «;
c>>e.empNo;
cout<<»enter emp salary «;
c>>e.salary;
return c;
}
265
األصناف املشتقة والقوالب الوحدة اخلامسة
ostream& operator<<(ostream& c,employee e)
{cout<<»Emp name: «;
cout<<e.empName<<endl;
cout<<»Emp Number: «;
cout<<e.empNo<<endl;
cout<<»Emp salary «;
cout<<e.salary<<endl;
return c;
}
class student{
long stno;
int csno;
int courses[100];
char stName[20];
public:
double average();
friend istream& operator>>(istream& c, student& s);
friend ostream& operator<<(ostream& c, student s);
long get_stno() {return stno;}
char* get_stname(){return stName;}
};
double student::average()
{ double sum=0;
for(int i=0;i<csno;i++)
sum=sum+courses[i];
return sum/csno;
}
)4( تدريب
#include<iostream.h>
class PostGrad{
long stno;
int csno;
double grades[100];
char StName[20];
char supervisor[20];
public:
double average();
void initialize();
long get_stno() {return stno;}
char* get_stname(){return StName;}
double get_grade(int i);
friend ostream& operator<<(ostream& c, PostGrad t);
};
ostream& operator<<(ostream& c, PostGrad t)
{c<<”\n Student Name “<<t.StName;
c<<”\n Student No “<<t.stno;
c<<”\n Number of courses “<<t.csno;
c<<”\n The supervisors name is “<<t.supervisor;
c<<”\n the average grades is”<<t.average()<<endl;
return c;
}
double PostGrad::get_grade(int i)
{
if (i<csno)
268
األصناف املشتقة والقوالب الوحدة اخلامسة
return grades[i];
else
return -1;
}
double PostGrad::average()
{ double sum=0.0;
for(int i=0;i<csno;i++)
sum=sum+grades[i];
return sum/csno;
}
void PostGrad::initialize()
{
cout<<”enter student’s number”;
cin>>stno;
cout<<”enter student’s name “;
cin>>StName;
cout<<”enter supervisor’s name”;
cin>>supervisor;
cout<<”enter no. of courses taken by the student”;
cin>>csno;
for(int i=0;i<csno;i++)
{cout<<”enter grade no.”<<i+1;
cin>>grades[i];
}
}
main()
{ section<PostGrad, 50> cs100;
int choice;
do
{cout<<”\n\n \t Enter 1 to add a new student \n”;
cout<<”\t Enter 2 to delete a student \n”;
cout<<”\t Enter 3 to some information about a student \n ”;
cout<<”\t Enter 4 to get a list of all students \n”;
cout<<”\t Enter 5 to Exit \n”;
cin>>choice;
271
األصناف املشتقة والقوالب الوحدة اخلامسة
switch(choice){
case 1: // student add
PostGrad s;
s.initialize();
cs100.StAdd(s);
break;
case 2: // delete a student
long sn;
cout<<”Enter student number ”;
cin>>sn;
cs100.StDelete(sn);
break;
case 3: // retrieve some data about an employee
cout<<”Enter student number ”;
cin>>sn;
cs100.StRetrieve(sn);
break;
case 4: // list information about all students
cout<<”Students information \n\n”;
cs100.Stlist();
break;
case 5: cout<<»End of program \n»;
break;
default: cout<<»invalid choice ... Try again «;
}}
while(choice!=5);
}
)5( تدريب
#include<iostream.h>
class student{
long stno;
int csno;
272
األصناف املشتقة والقوالب الوحدة اخلامسة
double grades[100];
char StName[20];
public:
double average();
void initialize();
long get_stno() {return stno;}
char* get_stname(){return StName;}
double get_grade(int i);
};
double student::get_grade(int i)
{
if (i<csno)
return grades[i];
else
return -1;
}
double student::average()
{ double sum=0.0;
for(int i=0;i<csno;i++)
sum=sum+grades[i];
return sum/csno;
}
void student::initialize()
{
cout<<»enter student›s number»;
cin>>stno;
cout<<»enter student›s name «;
cin>>StName;
main()
{ section<student> cs100;
int choice;
276
األصناف املشتقة والقوالب الوحدة اخلامسة
do
{cout<<”\n\n \t Enter 1 to add a new student \n”;
cout<<”\t Enter 2 to delete a student \n”;
cout<<”\t Enter 3 to some information about a student \n ”;
cout<<”\t Enter 4 to get a list of all students \n”;
cout<<”\t Enter 5 to Exit \n”;
cin>>choice;
switch(choice){
case 1: // student add
student s;
s.initialize();
cs100.StAdd(s);
break;
case 2: // delete a student
long sn;
cout<<”Enter student number ”;
cin>>sn;
cs100.StDelete(sn);
break;
case 3: // retrieve some data about an employee
cout<<”Enter student number ”;
cin>>sn;
cs100.StRetrieve(sn);
break;
case 4: // list information about all students
cout<<”Students information \n\n”;
cs100.Stlist();
break;
case 5: cout<<»End of program \n»;
break;
default: cout<<»invalid choice ... Try again «;
}}
while(choice!=5);
}
277
األصناف املشتقة والقوالب الوحدة اخلامسة
)6( تدريب
#include<iostream.h>
class property{
protected:
float Price;
float Monthly_Rent;
public:
property(float P=10000.0, float Rent=100.0)
{Price=P;
Monthly_Rent=Rent;
}
float increase_rent(float amt)
{ Monthly_Rent +=amt;
return Monthly_Rent;}
float decrease_rent(float amt)
{ Monthly_Rent -=amt;
return Monthly_Rent;}
float increase_price(float amt)
{ Price+=amt;
return Price;}
decrease_price(float amt)
{ Price -=amt;
return Price;}
virtual void print_info();
virtual double get_area()=0;
virtual void read_info();
};
void property::print_info()
{cout<<»\n The price is «<<Price
<<»\n The Monthly Rent is «<<Monthly_Rent
<<endl;
}
void property::read_info()
{cout<<»\n Enter the price «;
cin>>Price;
278
األصناف املشتقة والقوالب الوحدة اخلامسة
cout<<»\n Enter the monthly rent is «;
cin>>Monthly_Rent;
}
void house::read_info()
{ property::read_info();
cout<<»\n Enter the house area is «;
cin>>BArea;
cout<<»\n The area of the Garden is «;
cin>>GArea;
};
279
األصناف املشتقة والقوالب الوحدة اخلامسة
double house::get_area()
{ return BArea+GArea;}
class flat:public property
{ protected:
float Area;
public:
flat(float P=5000.0,float Rent=50.0,float A=120.0);
void print_info();
virtual double get_area();
virtual void read_info();
};
double flat::get_area()
{return Area;}
flat::flat(float P,float Rent,float A): property(P,Rent)
{ Area=A;
}
void flat::print_info()
{ property::print_info();
cout<<»\n The flat area is «<<Area<<endl;
};
void flat::read_info()
{ property::read_info();
cout<<»\n The flat area is «;
cin>>Area;
};
280
األصناف املشتقة والقوالب الوحدة اخلامسة
virtual double get_area();
virtual void read_info();
};
double Multi_Building::get_area()
{return NoStory*NoFlats*Area;}
Multi_Building::Multi_Building(float P,float Rent,float A,int S,int F)
: flat(P,Rent,A)
{NoStory=S;
NoFlats=F;
}
void Multi_Building::print_info()
{ flat::print_info();
cout<<»\n No of Stories is «<<NoStory
<<»\n No of flats per story is «<<NoFlats
<<endl;
}
void Multi_Building::read_info()
{ flat::read_info();
cout<<»\n Enter No of Stories is «;
cin>>NoStory;
cout<<»\n No of flats per story is «;
cin>>NoFlats;
}
281
األصناف املشتقة والقوالب الوحدة اخلامسة
st[i]=a;
}
}
main()
{
// declare an array of pointers to the base class property
property* A[10];
int choice;
int i=0;
do
{cout<<»\n \n Enter 1 to add a flat to the list of properties «
<<»\n Enter 2 to add a house to the list of properties «
<<»\n Enter 3 to add a Multi story building to the list of properties «
<<»\n Enter 4 to get a sorted list of all properties sorted according
to area»
<<»\n Enter 5 to exit»
<<endl;
cin>>choice;
switch(choice){
case 1:
{A[i]=new flat;
A[i]->read_info();
i+=1;
break; }
case 2:
{A[i]=new house;
A[i]->read_info();
i+=1;
break; }
case 3:
{A[i]=new Multi_Building;
A[i]->read_info();
i+=1;
break;
case 4:
282
األصناف املشتقة والقوالب الوحدة اخلامسة
sort(A,i);
for(int j=0;j<i;j++)
A[j]->print_info();
break; }
case 5:
{cout<<»end program «;
break; }
default: {cout<<»invalid choice try again «; }
} //switch
} while(choice!=5);
int k;
cin>>k;
} //main
وناجت التنفيذ
283
األصناف املشتقة والقوالب الوحدة اخلامسة
)7( تدريب
// virtual members
#include <iostream>
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
virtual int area ()
{ return (0); }
};
int main () {
CRectangle rect;
CTriangle trgl;
CPolygon poly;
CPolygon * ppoly1 = ▭
CPolygon * ppoly2 = &trgl;
CPolygon * ppoly3 = &poly;
ppoly1->set_values (4,5);
284
األصناف املشتقة والقوالب الوحدة اخلامسة
ppoly2->set_values (4,5);
ppoly3->set_values (4,5);
cout << «input data for polygon are 4 and 5 « << endl;
cout << «area of Rectangle « << ppoly1->area() << endl;
cout << «area of Rectangle «<< ppoly2->area() << endl;
cout << «area of Poly « << ppoly3->area() << endl;
int i;
cin>>i;
return 0;
ناجت التنفيذ
285
األصناف املشتقة والقوالب الوحدة اخلامسة
.8مسرد املصطلحات
تعدد األوجه :Polymorphismمن مزايا لغة C++املهمة جدا ،إذ أنها -
متنحنا القدرة على تأجيل حتديد هوية الدالة املراد استدعاؤها من وقت الترجمة
compile timeكما هي العادة إلى وقت تنفيذ البرنامج .run time
صنف القاعدة أو األساس :Base classهو املوروث وهو الصنف الذي -
يحوي البيانات والعمليات املراد توريثها لصنف آخر .
الصنف املشتق الوارث :Derived classهو الصنف الوارث خلصائص -
الصنف األساس وعملياته.
الوراثة :Inheritanceهي إمكانية أن يرث صنف ما اخلصائص والعمليات -
(الدوال) املوجودة في صنف آخر ،مما يساعد على إعادة استخدام
Reusabilityاألصناف التي مت إنشاؤها من قبل.
الوراثة اخلاصة :Private Inheritanceعندما يرث صنف مشتق صنف -
األساس وكان نوع الوراثة خاص ًا فإن كل البيانات والدوال املوجودة في
صنف األساس تصبح خاصة بالصنف املشتق ،ما عدا اخلاصة باألساس فإنها
ال تورث.
الوراثة العامة :Public Inheritanceفي هذا النوع يتم توزيع البيانات -
والدوال كما يلي :احملمي protectedفي صنف األساس base class
يصبح محمي ًا في الصنف املشتق .العام publicفي صنف األساس يصبح
عام ًا في الصنف املشتق .اخلاص ال ّ
يورث أبد ًا (البيانات اخلاصة ال ّ
تورث
أبداً).
الوراثة احملمية :Protected Inheritanceفي هذا النوع تصبح كل بيانات -
محمية بالصنف املشتق ،ما عدا اخلاصة باألساس ّ صنف األساس ودواله
فإنها ال تورث.
286
األصناف املشتقة والقوالب الوحدة اخلامسة
املراجع.9
287
األصناف املشتقة والقوالب الوحدة اخلامسة
288
األصناف املشتقة والقوالب الوحدة اخلامسة
الوحدة السادسة
معالجة الملفات بلغة C++
File Processing with C++
290
معاجلة امللفات بلغة C++ الوحدة السادسة
محتويات الوحدة
الصفحة امل ــوض ـ ـ ـ ــوع
293 .1املقدمة
293 1.1متهيد
293 2.1أهداف الوحدة
294 3.1أقسام الوحدة
294 4.1القراءات املساعدة
295 5.1ما حتتاج إليه لدراسة الوحدة
296 .2هيكلية البيانات Data Hierarchy
298 .3تنظيم امللفات File Organization
300 .4امللفات والينابيع Files and Streams
301 .5امللفات التتابعية Sequential Files
301 1.5إنشاء امللفات التتابعية
303 2.5القراءة من امللفات التتابعية
306 3.5حتديث امللفات التتابعية
307 .6امللفات العشوائية Random Files
309 1.6إنشاء امللفات العشوائية
313 2.6القراءة من امللفات العشوائية
315 3.6حتديث امللفات العشوائية
318 .7إدخال الكائنات وإخراجها
323 .8اخلالصة
323 .9إجابات التدريبات
324 .10مسرد املصطلحات
325 .11املراجع
291
معاجلة امللفات بلغة C++ الوحدة السادسة
292
معاجلة امللفات بلغة C++ الوحدة السادسة
.1املقدمة
1.1متهيد
ال بك إلى الوحدة السادسة من مقرر برمجة ( )1وهي عزيزي الدارس ،أه ً
بعنوان «معاجلة امللفات بلغة .»C++
عما يتعلق مبعاجلة امللفات
تهدف هذه الوحدة إلى إعطائك معلومات أساسية ّ
في لغة C++وتوضيح بعض العمليات األساسية على امللفات بأنواعها املختلفة.
تتناول هذه الوحدة تعريف مفهوم هيكل البيانات وموقع امللفات بينها وإعطاء
حمل��ة مختصرة ع��ن األن���واع املختلفة م��ن البيانات .ويتلو ذل��ك مناقشة الطرق
املختلفة في تنظيم امللفات وتعريف امللفات والينابيع ،ومن ثم توضيح كيفية إنشاء
امللفات التتابعية والعشوائية وقراءتها وحتديثها ،وأخير ًا مت توضيح كيفية التعامل
مع الكائنات من حيث اإلدخال واإلخراج في امللفات.
وقد مت تزويد هذه الوحدة بالعديد من األمثلة التوضيحية ،والتدريبات،
وأسئلة التقومي الذاتي إضافة إلى بعض الرسومات التي توضح بعض املفاهيم.
ال للتدريبات التي تصادفها في نهاية الوحدة.وستجد ،عزيزي الدارس ،حلو ً
مرة أخرى ،نرحب بك في هذه الوحدة ،آملني أن تستمتع بدراستك للمادة
التي نعرضها لك فيها.
2.1أهداف الوحدة
ينتظر منك ،عزيزي الدارس ،بعد فراغك من دراسة هذه الوحدة ،أن تكون
قادر ًا على أن:
تعرف هيكل البيانات وموقع امللفات بينها.
ّ .1
تعرف الطرق املختلفة في تنظيم امللفات (.)File Organization ّ .2
تعرف امللفات والينابيع (.)Files and Streams ّ .3
.4تنشئ امللفات التتابعية وتقرؤها وحتدثها.
.5تنشئ امللفات العشوائية وتقرؤها وحتدثها.
.6تدخل الكائنات وتخرجها (.)Input/Output of Objects
293
معاجلة امللفات بلغة C++ الوحدة السادسة
3.1أقسام الوحدة
تتكون هذه الوحدة من ستة أقسام رئيسة .فالقسم األول يبحث في هيكلية
البيانات وموقع امللفات بينها وبذلك يتحقق الهدف األول .أم��ا القسم الثاني
فيوضح الطرق املختلفة في تنظيم امللفات ويحقق بدوره الهدف الثاني .أما القسم
الثالث فيشرح تعريف امللفات والينابيع وبذلك يتحقق الهدف الثالث .ويوضح
القسم الرابع إنشاء امللفات التتابعية وقراءتها وحتديثها ويحقق بدوره الهدف الرابع.
أما القسم اخلامس فيشرح امللفات العشوائية من حيث اإلنشاء والقراءة والتحديث
وبذلك يتحقق الهدف اخلامس .وأخير ًا فإن القسم السادس يوضح كيفية إدخال
الكائنات وإخراجها ويحقق بدوره الهدف السادس من أهداف هذه الوحدة.
4.1القراءات املساعدة
294
معاجلة امللفات بلغة C++ الوحدة السادسة
5.1ما حتتاج إليه لدراسة الوحدة
لقد تضمنت هذه الوحدة العديد من األمثلة والبرامج ،وبذلك فإنك ستحتاج،
عزيزي الدارس ،إلى استخدام جهاز احلاسوب للقيام بكتابة البرامج وتنفيذها:
فاملادة املعروضة في هذه الوحدة ال تعطي الفائدة املرجوة منها إال بتطبيق البرامج
التوضيحية ال���واردة فيها .وم��ن هنا ،من املهم وج��ود جهاز احلاسوب بجانبك
إضافة إلى مترجم لغة س��ي ++وبعض القرطاسية كقلم رص��اص وورق أبيض
لإلجابة عن األسئلة والتدريبات املعطاة في ثنايا الوحدة ولكتابة مسودات بعض
البرامج الالزمة .وباإلضافة إلى ذلك ،فإنك ستحتاج ،عزيزي ال��دارس ،إلى
املكان الهادئ واملناسب.
295
معاجلة امللفات بلغة C++ الوحدة السادسة
.2هيكلية البيانات Data Hierarchy
إن البيانات التي تتعامل معها احلواسيب هي مجموعة من األصفار والواحدات التي
البايت
تسمى اخلانات الثنائية) . (Bitsإن مجموعة مكونة من ثماني خانات ثنائية تشكل ْ
الذي بدوره ميثل رمز ًا في مجموعة الرموز املسمى اآلسكي ) (ASCIIحيث يحتل كل رمز
في الذاكرة الرئيسة للحاسوب بايت ًا واحداً .تشكل مجموعة البايتات حقو ً
ال )(Fieldsمعينة
يكون لها معنى معني حسب طبيعة التطبيق والبرنامج .فمثالً ،مجموعة الرموز املكونة
من أحرف أبجدية صغيرة وكبيرة ميكن أن متثل اسم دارس في جامعة القدس املفتوحة.
إن مجموعة احلقول املترابطة ترابط ًا منطقي ًا ميكن أن متثل سجالً؛ فمثالً ،ميكن اعتبار اسم
الدارس ،رقمه ،عنوانه ،تاريخ ميالده سجالً .وفي أغلب احلاالت ،يكون هناك حقل
خاص يأخذ قيم ًا منفردة للسجالت املختلفة يسمى مفتاح السجل()Record Key؛ بحيث
ال يكون هناك أي سجلني لهما القيمة نفسها لهذا املفتاح مثل حقل رقم الدارس الذي ميكن
اعتباره مفتاحا لسجل الدارس ،حيث إن لكل دارس رقم ًا خاص ًا يختلف عن أرقام بقية
الدارسني .إن مجموعة سجالت الدارسني ميكن أن متثل ملفاً .يوضح الشكل ( )1رسم ًا
توضيحي ًا لهيكلية البيانات .وبصورة عامة ،فإن األنظمة املختلفة تتعامل مع مجموعة
من امللفات؛ فعلى سبيل املثال ،فإن نظام التسجيل في جامعة القدس املفتوحة ميكن أن
يشتمل على ملف خاص بالدارسني ،وملف خاص باملشرفني األكادمييني ،وملف خاص
باملقررات ،... ،وهكذا .فإن مجموعة هذه امللفات املترابطة منطقي ًا ميثل قاعدة بيانات
) (Databaseحتتاج إلى مجموعة من برامج النظم إلنشائها ومعاجلتها تسمى نظام إدارة
قواعد البيانات ).(Database Management System
296
معاجلة امللفات بلغة C++ الوحدة السادسة
الشكل ( :)1هيكلية البيانات
297
معاجلة امللفات بلغة C++ الوحدة السادسة
.3تنظيم امللفات File Organization
هناك عدة طرق مختلفة لتنظيم السجالت في امللفات تعتمد بصورة أساسية على
كيفية تخزين هذه السجالت في امللفات واحلاجة السترجاعها بصورة معينة .هناك
طريقتان أساسيتان في تنظيم امللفات:
)1التنظيم التتابعي للملفات
يتم ،في هذه الطريقة تخزين السجالت بالترتيب حسب قيم مفتاح السجل ،فعلى
سبيل املثال ،ميكن أن تتم عملية تخزين سجالت الدارسني في جامعة القدس املفتوحة
حسب رقم الدارس بحيث يحتوي السجل األول على سجل الدارس الذي يحمل أصغر
رقم ومن ثم ،الذي يحمل الرقم الذي يليه وهكذا .إن امللف الذي يحتوي على سجالت
مخزنة بهذه الطريقة يسمى امللف التتابعي ( .)Sequential Fileإن من سيئات استخدام
امللفات التتابعية أنها تضع قيود ًا على كيفية استرجاع السجالت والوصول إليها؛ إذ
ال ميكن الوصول إلى السجل رقم nإال بعد الوصول إلى كل السجالت التي تسبقه
في التخزين ،أي السجالت من 1وحتى ،n-1أي أن امللفات التتابعية غير مالئمة
للتطبيقات والبرامج التي حتتاج الوصول إلى سجل معني مباشرة.
مثال ()1
إذا كانت السجالت R1,R2,…Rnمنظمة بصورة تتابعية كما يلي:
فإنه للوصول إل��ى R100يجب الوصول إل��ى جميع السجالت التي تسبق
( R100أي السجالت من R1إلى .)R99
مثال ()2
إذا كانت السجالت R1,R2,…Rnمنظمة بصورة عشوائية كما يلي:
فإنه ميكن الوصول إلى R100دون احلاجة للوصول إلى جميع السجالت
التي تسبق ( R100أي السجالت من R1إلى .)R99
.1وضح الفرق األساسي بني امللفات التتابعية وامللفات العشوائية من حيث كيفية
الوصول للبيانات.
.2ما املقصود بتنظيم امللفات؟
299
معاجلة امللفات بلغة C++ الوحدة السادسة
.4امللفات والينابيع Files and Streams
إن لغة سي ++تنظر إلى امللف كمجموعة من البايتات املخزنة واحد ًا تلو اآلخر،
حيث ينتهي كل ملف بعالمة معينة تسمى عالمة نهاية امللف )(End-of-file Marker؛
فعند القيام بفتح ملف ما يتم إنشاء كائن يربط مع ينبوع معني ) ،(Streamفقد سبق
واطلعت ،عزيزي الدارس ،على الكائنات cin, coutفي الوحدات السابقة من هذا
املقرر التي يتم إنشاؤها عند تضمني برنامج معني مللف الترويسة iostreamحيث متثل
الينابيع املرتبطة مع هذه الكائنات قنوات اتصال بني البرامج وامللفات التي تستخدمها.
ال بالشاشة ويستخدم لعرض كما أن هناك كائن ًا آخر يدعى cerrيكون ،عادة ،موصو ً
رسائل األخطاء .وعادة ما يتم تعيني coutجلهاز غير الشاشة بينما يتم تعيني cerrلشاشة
احلاسوب .فعلى سبيل املثال ،لعرض الرسالة » «File not foundمن خالل ،cerr
نكتب » ;cerr << «File not Foundفي البرنامج.
وللقيام بعملية معاجلة للملفات في لغة سي ،++يجب تضمني ملفات الترويسة
iostreamو .fstreamتستخدم هذه امللفات للحصول على األصناف التي يتم فتح
امللفات املختلفة من خالل إنشاء كائنات خاصة باالعتماد عليها ،كما سيتم توضيح ذلك
في األقسام التالية من هذه الوحدة.
300
معاجلة امللفات بلغة C++ الوحدة السادسة
.5امللفات التتابعية Sequential Files
1.5إنشاء امللفات التتابعية
إن لغة سي ++ال تفرض أي شكل أو تركيب على امللفات .فكما أشرنا سابقاً،
عزيزي الدارس ،فان لغة سي ++تتعامل مع امللفات وكأنها مجموعة من البايتات،
وبناء على ذلك ،فإن مصطلح السجل مثالً ،غير موجود في امللفات بصورة عامة بل
تقع مسؤولية حتديد الكيفية التي سيتم فيها تخزين البيانات في امللفات على املبرمجني.
للتعامل مع أصناف امللفات ،ال بد من تضمني البرنامج ملف الترويسة ،fstream.h
الذي يشتمل على تعريف ألصناف امللفات مثل ofstreamالذي يستخدم في حالة إنشاء
ملف تتابعي للكتابة فيه و ifstreamالذي يستخدم في حالة القراءة من ملف تتابعي،
وكال الصنفني يشتمالن على الدوال املنتمية اخلاصة بالتعامل مع امللفات.
يتم ،عند فتح ملف تتابعي ،حتديد الغرض من فتحه فيما إذا كان إلدخال بيانات فيه
أو كتابتها أو لقراءة بيانات منه وذلك عبر حتديد ذلك في اجلملة اخلاصة بإنشاء كائن من
النوع املطلوب.
يوضح البرنامج التالي كيفية إنشاء ملف تتابعي خاص بالدارسني في جامعة القدس
املفتوحة ،حيث تشتمل البيانات اخلاصة بكل دارس على حقول متثل رقمه ،اسمه،
معدله حيث ميكن اعتبار هذه احلقول ممثلة لسجل الدارس ويكون رقم الدارس ،في هذه
احلالة ،هو مفتاح السجل.
>#include <iostream.h
>#include <fstream.h
>#include <stdlib.h
)int main(void
{
;int sno
;]char sname[32
;float saverage
;)ofstream outStudentFile(«student.dat»,ios::out
)if(!outStudentFile
301
معاجلة امللفات بلغة C++ الوحدة السادسة
{
;cerr << «File could not be opened» << endl
;)exit(1
}
»cout << «Enter student no, name and average, at end enter end-of-file:
;<< endl
)while(cin >> sno >> sname >> saverage
{
;outStudentFile << sno <<› ‹ << sname << ‹ ‹ << saverage <<endl
}
;)(outStudentFile.close
;return 0
}
وكما تالحظ ،عزيزي الدارس ،فإن عملية فتح هذا امللف التتابعي قد متت عبر
إنشاء كائن باسم outStudentFileمن الصنف ofstreamحيث مت استخدام معاملني مع
البناء اخلاص بهذا الصنف .ميثل املعامل األول student.datاسم امللف اخلارجي الذي
سيتم تخزين البيانات فيه على وسط التخزين املساعد املستخدم مثل القرص الصلب؛
حيث يتم التخزين في املجلد احلالي وميكن تغيير مكان تخزين امللف من خالل حتديد
املسار الكامل ملكان التخزين .أما املعامل الثاني ios::outفيمثل منط فتح امللف(file
) open modeحيث مت فتح امللف في هذا املثال للكتابة فيه ) .(outputومن اجلدير ذكره
هنا أن فتح امللف بهذا النمط يؤدي إلى حذف كل البيانات املوجودة أص ً
ال في امللف ،إن
ال وإعطاء املستخدمكان هناك بيانات .وللحفاظ على البيانات املوجودة في امللف أص ً
إمكانية إضافة بيانات جديدة على امللف فإنه ميكن اختيار منط فتح امللف ios::appحيث
يعني االختصار appكلمة appendأي إضافة ملا هو موجود أص ً
ال في امللف.
ومن اجلدير ذكره ،عزيزي الدارس ،أنه ميكن إنشاء كائن من الصنف ofstream
دون فتح امللف ،وإمنا تأجيل ذلك إلى مرحلة الحقة في البرنامج وحتديد ذلك عبر الدالة
املنتمية openكما يلي:
;ofstream outStudentFile
;)outStudentFile.open(«student.dat»,ios::out
302
معاجلة امللفات بلغة C++ الوحدة السادسة
وبغض النظر عن الكيفية التي مت فيها فتح امللف التتابعي للكتابة فيه ،فإنه من
الضروري أن تتم عملية التأكد من أن عملية فتح امللف قد متت بنجاح وذلك من خالل
الشرط ! outStudentFileالذي يرجع القيمة falseإذا لم تتم عملية فتح امللف بنجاح؛
كأن ال يكون هناك مساحة كافية على القرص الصلب إلنشاء امللف املطلوب .بعد ذلك،
يقوم البرنامج بقراءة بيانات عدد من الدارسني إلى أن يقوم املستخدم بإدخال رمز نهاية
امللف ^Zفي بيئة نظام التشغيل Windowsأو ما يقابلها في نظم التشغيل األخرى.
أما إذا كان مكان تخزين امللف في مجلد غير املجلد الذي يحتوي البرنامج فإنه ميكن
أن حتدد ذلك عبر حتديد املسار الكامل للملف؛ ففي املثال ،إذا كان امللف student.dat
مخزن ًا على القرص الصلب \:Cوفي املجلد mydataفإنه ميكن كتابة جملة فتح امللف
كما يلي:
;)outStudentFile.open(«c:\\mydata\\student.dat»,ios::out
وأخيراً ،عزيزي الدارس ،ميكن للمبرمج إغالق امللف عبر استخدام الدالة املنتمية
closeوذلك بكتابة جملة )( outStudentFile.closeعلم ًا بأنه سيتم تنفيذ هذه اجلملة
بصورة ضمنية عبر الهدام اخلاص بالكائن .outStudentFile
)int main(void
{
303
معاجلة امللفات بلغة C++ الوحدة السادسة
;int sno
;]char sname[32
;float saverage
;)ifstream inStudentFile(«student.dat»,ios::in
)if(!inStudentFile
{
;cerr << «File could not be opened» << endl
;)exit(1
}
وكما تالحظ ،عزيزي الدارس ،فإن عملية فتح هذا امللف التتابعي قد متت من خالل
إنشاء كائن باسم inStudentFileمن الصنف ifstreamحيث مت استخدام معاملني مع
البناء اخلاص بهذا الصنف .ميثل املعامل األول student.datاسم امللف اخلارجي الذي
ستتم منه عملية قراءة البيانات ،أما املعامل الثاني ios::inفيمثل منط فتح امللف (file
) open modeحيث مت فتح امللف في هذا املثال للقراءة منه ).(input
من الضروري ،عزيزي الدارس ،أن تتم عملية التأكد من أن عملية فتح امللف قد متت
بنجاح عبر الشرط ! inStudentFileالذي يرجع القيمة falseإذا لم تتم عملية فتح امللف
بنجاح كأن ال يكون هناك ملف على القرص الصلب باالسم املستخدم .بعد ذلك ،يقوم
البرنامج بقراءة بيانات عدد من الدارسني إلى أن يصل نهاية امللف.
وأخيراً ،عزيزي الدارس ،فإنه بإمكان املبرمج أن يغلق امللف عبر استخدام الدالة
املنتمية closeوذلك بكتابة جملة )( inStudentFile.closeعلم ًا بأنه سيتم تنفيذ هذه
اجلملة بصورة ضمنية عبر الهدام اخلاص بالكائن .inStudentFile
304
معاجلة امللفات بلغة C++ الوحدة السادسة
يتم البدء في القراءة من امللف التتابعي في لغة سي ،++من بداية امللف حتى الوصول
إلى السجل املطلوب وقد يكون هناك حاجة لقراءة ملف تتابعي عدة مرات ،وللقيام
بذلك تزودنا لغة سي ++بدالة منتمية تسمى seekgأي seek getإلعادة وضع مؤشر
بداية القراءة عند رقم بايت معني في حالة القراءة من امللف باستخدام كائن ،istream
كما أن هناك دالة منتمية أخرى تسمى seekpأي seek putإلعادة وضع مؤشر بداية
الكتابة عند رقم بايت معني في حالة الكتابة من امللف باستخدام كائن .ostream
هناك معامل آخر للدالة املنتمية seekgميكن استخدامه لتحديد اجتاه البحث في
امللف ،حيث ميكن أن يكون اجتاه البحث ios::begحيث تعني begكلمة beginning
أي البداية ،وذلك لتحديد اجتاه البحث ليكون نسبة إلى بداية امللف وهو اخليار املستخدم
في حالة عدم حتديد قيمة للمعامل الثاني للدالة .كما ميكن أن يكون اجتاه البحث نسبة
إلى املوقع احلالي وذلك عبر استخدام ios::cur؛ حيث تعني curكلمة currentأي املوقع
احلالي ،أما إذا استخدمت القيمة ios::endفذلك يعني أن اجتاه البحث هو نسبة إلى نهاية
امللف وذلك واضح من استخدام الكلمة endأي النهاية.
مثال ()3
إذا علمت أن اسم كائن istreamهو inEmployeeFileفاكتب صيغة الدالة
املنتمية لوضع مؤشر القراءة من امللف عند بداية امللف.
ميكن عمل ذلك باستخدام اجلملة
;)inEmployeeFile.seekg(0
كما أن هناك عمليات مماثلة خاصة بالكائن ostreamوذل��ك عبر استخدام
الدالة املنتمية .seekp
تدريب ()1
.1إذا علمت أن اسم كائن istreamهو inEmployeeFileفاكتب صيغة الدالة
املنتمية لوضع مؤشر القراءة من امللف عند:
أ -البايت رقم nمن امللف.
ب -البايت رقم nنسبة إلى املوقع احلالي.
305
معاجلة امللفات بلغة C++ الوحدة السادسة
3.5حتديث امللفات التتابعية
إن املقصود بعملية حتديث امللفات التتابعية ،عزيزي الدارس ،هو القيام بعمليات
إضافة لسجالت معينة أو حذف سجالت موجودة أصالً ،أو إجراء تغييرات على بعض
ال في هذه امللفات .يجب االنتباه ،عند إجراء عمليات التحديث، السجالت املخزنة أص ً
إلى أن ذلك قد يؤدي إلى فقدان بعض السجالت املوجودة في هذه امللفات نتيجة الكتابة
عليها؛ فعلى سبيل املثال ،عند تعديل اسم الطالب Samirليصبح Sameerوكتابة االسم
اجلديد على امللف ،فإن ذلك سيؤدي إلى تدمير جزء من احلقل التالي في السجل نفسه
أو رمبا تدمير جزء من السجل التالي مما يؤدي إلى إحداث خلل في محتويات هذا امللف.
هناك مشكلة أخرى فيما يتعلق بالبيانات العددية ،حيث إن هذه البيانات تتم كتابتها
بصورتها النصية نتيجة استخدام الكائن cout؛ فعلى سبيل املثال ،عند كتابة العدد 12فإنه
يحتل بايتني ،أما عند كتابة العدد 1234فإنه يحتل أربعة بايتات وهذا أيضا يؤدي إلى تدمير
جزء من احلقل التالي في السجل أو السجل التالي ،وذلك إذا مت استبدال العدد 12بالعدد
1234أو أي عدد آخر مكون من عدد أكبر من اخلانات التي يتكون منها العدد األصلي.
إن عميلة حتديث امللفات التتابعية يجب أن تتم عبر قراءة جميع السجالت في امللف
التتابعي املرغوب في إجراء التحديث عليه وكتابة السجالت في ملف تتابعي جديد،
وذلك بعد إجراء التحديث أو التغيير املطلوب في حال الرغبة في إجراء تعديل على
سجالت موجودة أصالً ،أو كتابة السجالت اجلديدة في مواقعها املناسبة إذا كان املطلوب
إضافة سجالت جديدة ،أو عدم كتابة السجالت املطلوب حذفها في امللف اجلديد.
مثال ()4
لنفترض أن امللف التتابعي اخلاص مبلف الطالب يحتوي البيانات التالية:
Sno Sname Saverage
1 Ali 90.5
2 Hasan 81.3
3 Sameer 85.2
للقيام بعميلة حتديث هذا امللف عبر تعديل االسم في السجل الذي به رقم
306
معاجلة امللفات بلغة C++ الوحدة السادسة
الطالب 1ليصبح Khalidوحذف السجل الذي به رقم الطالب 2فإن ذلك يتم
كما يلي:
1 .1تتم عملية القراءة للملف األصلي سج ً
ال تلو اآلخر.
2 .2تتم مقارنة رقم الطالب بأرقام السجالت املنوي تعديلها.
3 .3إذا ساوت قيمة رقم الطالب القيمة 1تتم عملية كتابة السجل بعد استبدال
قيمة االسم بالقيمة اجلديدة وهي .Khalid
4 .4إذا ساوت قيمة رقم الطالب القيمة 2ال تتم عملية كتابة السجل (أي يتم
حذفه).
307
معاجلة امللفات بلغة C++ الوحدة السادسة
0 10 20 30 40
10 bytes 10 bytes 10 bytes 10 bytes 10 bytes
فكما تالحظ ،عزيزي الدارس ،لكل سجل طول ثابت هو 10بايتات ،وبناء عليه،
فإذا بدأ السجل األول من املوقع 0فإن السجل الثاني سيبدأ من املوقع 10والثالث سيبدأ
من املوقع 20وهكذا .وميكننا التوصل إلى عالقة بسيطة تربط بني رقم السجل املطلوب
الوصول إليه مع موقعه الفعلي من حيث التخزين ،حيث ميكن كتابة هذه العالقة بالصيغة
التالية:
املوقع الفعلي للسجل = nموقع بداية امللف * )n-1( +حجم السجل
إن لغة سي ++توفر مجموعة من الدوال املنتمية التي متكننا من كتابة سجل معني في
أي موقع من امللف العشوائي عبر حتديد املوقع الفعلي لهذا السجل ضمن امللف العشوائي
باستخدام العالقة السابقة ،ولذلك ،فإنه من املمكن إضافة سجل جديد في موقع معني
ال كما ميكن حذف أي سجل أو تعديله دون التأثير على بقية السجالت املوجودة أص ً
في امللف العشوائي دون أن يؤثر ذلك على بقية السجالت ،وسنقوم بتوضيح ذلك في
األقسام التالية من هذه الوحدة.
تدريب ()2
.1إذا علمت أن موقع تخزين بداية ملف ما هو 1000وأن حجم السجالت
التي سيتم تخزينها في هذا امللف هو 100بايت لكل سجل ،فاحسب موقع
تخزين السجل السادس.
308
معاجلة امللفات بلغة C++ الوحدة السادسة
1.6إنشاء امللفات العشوائية
توفر لغة سي ++مجموعة من الدوال املنتمية للكائن ostreamالتي متكننا من التعامل
مع امللفات العشوائية ،فللكتابة في ملف عشوائي في موقع محدد ،ميكن استخدام الدالة
املنتمية writeالتي تقوم بكتابة البيانات بالصيغة الثنائية حيث ميكن حتديد هذا املوقع احملدد
باستخدام الدالة املنتمية seekpبعد حساب موقع السجل باستخدام العالقة السابقة.
ولتوضيح املفاهيم السابقة ،سنقوم ،عزيزي الدارس ،بإنشاء ملف عشوائي لتخزين
سجالت الدارسني في جامعة القدس املفتوحة؛ حيث تشتمل البيانات اخلاصة بكل
دارس على رقمه ،اسمه ،معدله حيث ميكن اعتبار هذه احلقول ممثلة لسجل الدارس
ويكون رقم الدارس ،في هذه احلالة ،هو مفتاح السجل ،أي أن هذا السجل ميكن متثيله
بالصورة التالية:
struct student
{
;int sno
;]char name[32
;float average
;}
)int main(void
{
struct student
{
309
معاجلة امللفات بلغة C++ الوحدة السادسة
;int no
;]char name[32
;float average
;}
;)ofstream outStudentFile(«studrf.dat»,ios::in|ios::out|ios::binary
)if(!outStudentFile
{
;cerr << «File could not be opened» << endl
;)exit(1
}
)for(int i=0;i<No_Of_Records;i++
outStudentFile.write(reinterpret_cast <const char >*
;))(&blankrecord),sizeof(struct student
;)(outStudentFile.close
;return 0
}
وكما تالحظ ،عزيزي الدارس ،فقد مت فتح امللف باستخدام اجلملة التالية:
;)ofstream outStudentFile(«studrf.dat»,ios::in|ios::out|ios::binary
310
معاجلة امللفات بلغة C++ الوحدة السادسة
سيحتالن العدد نفسه من البايتات عندما يخزنان بالصورة الثنائية.
كما توفر لغة سي ++عملية تسمى reinterpret_castولذلك للقيام بعملية
حتويل مؤشر من نوع معني إلى مؤشر من نوع آخر ليس له عالقة بالنوع األول ،ولذلك
كان استخدام هذه العملية في اجلملة:
outStudentFile.write(reinterpret_cast <const char *> (&blankrecord),
;))sizeof(struct student
وبعد القيام بعملية إنشاء ملف فارغ ليحتوي على السجالت املنظمة بالصورة
العشوائية ،ميكن كتابة السجالت فيه بعد فتح امللف باستخدام اجلملة التالية:
;)ofstream outStudentFile(«studrf.dat»,ios::in|ios::out|ios::binary
التي حتدد ،عبر استخدام املعامل الثاني للدالة ،أنه ملف للقراءة والكتابة في الوقت
نفسه وأنه سيتم تخزين البيانات فيه بالصورة الثنائية .بعد ذلك ،للقيام بإضافة سجل
جديد ،فإنه يجب حتديد موقع تخزينه في امللف وذلك باستخدام العالقة التي مت
توضيحها سابق ًا التي ميكن كتابتها من خالل اجلملة التالية:
;))outStudentFile.seekp((studentrecord.no-1)* sizeof(struct student
وكما تالحظ ،عزيزي الدارس ،فإن هذه اجلملة ستقوم بتحديد موقع التخزين عبر
الدالة seekpالتي ستضع مؤشر الكتابة في امللف على موقع يبعد (studentrecord.no-
)1)* sizeof(struct studentمن البايتات عن موقع بداية امللف.
البرنامج التالي يقوم بكتابة عدد من السجالت في ملف عشوائي ،حيث يتوقف
البرنامج عندما يقوم املستخدم بإدخال رقم دارس أقل من 0أو أكبر من ،100وهي
مجموعة أرقام الدارسني الذين قمنا بإنشاء سجالت فارغة لهم .أما إذا أردت ،عزيزي
الدارس ،أن تتعامل مع أرقام دارسني من 0إلى 1000فيجب أن تقوم بكتابة سجالت
فارغة وإنشائها لهم باستخدام برنامج مشابه للبرنامج املوضح في البرنامج السابق.
>#include <iostream.h
>#include <fstream.h
>#include <stdlib.h
>#include <string.h
;const int No_Of_Records =100
311
معاجلة امللفات بلغة C++ الوحدة السادسة
int main(void)
{
struct student
{
int no;
char name[32];
float average;
};
int sno;
char sname[32];
float saverage;
ofstream outStudentFile(«studrf.dat»,ios::in|ios::out|ios::binary);
if(!outStudentFile)
{
cerr << «File could not be opened» << endl;
exit(1);
}
studentrecord.no=sno;
strcpy(studentrecord.name,sname);
312
C++ معاجلة امللفات بلغة الوحدة السادسة
;studentrecord.average=saverage
التي تقوم بإنشاء كائن باسم inStudentFileحيث يوضح املعامل الثاني ios::inأن
هذا امللف سيتم فتحه للقراءة منه .بعد ذلك ،يجب التأكد من أن عملية فتح امللف قد
متت بنجاح ،وإال يجب إيقاف البرنامج مباشرة ألننا لن نتمكن من قراءة أي سجل في
هذه احلالة.
ميكن استخدام الدالة املنتمية readللقراءة من موقع معني يتم حتديده باستخدام الدالة
املنتمية .seekgولتحدد موقع السجل املنوي قراءته ،يجب في البداية أن نقوم بقراءة
قيمة مفتاح السجل أي رقم الدارس ومن ثم استخدام العالقة التي مت توضيحها سابق ًا
لتقوم الدالة seekgباستخدام القيمة الناجتة من هذه العالقة في حتديد املوقع الفعلي الذي
ستتم منه قراءة السجل املطلوب.
البرنامج التالي يقوم بقراءة عدد من السجالت من ملف عشوائي باستخدام الدالة
readوالدالة seekgكما أوضحنا سابقاً.
313
معاجلة امللفات بلغة C++ الوحدة السادسة
#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
struct student
{
int no;
char name[32];
float average;
};
int sno;
ifstream inStudentFile(«studrf.dat»,ios::in);
if(!inStudentFile)
{
cerr << «File could not be opened» << endl;
exit(1);
}
314
C++ معاجلة امللفات بلغة الوحدة السادسة
;cout << «\nStudent Data for student no « << studentrecord.no
;cout << «\nName: « << studentrecord.name
;cout << «\nAverage: « << studentrecord.average
315
معاجلة امللفات بلغة C++ الوحدة السادسة
#include <fstream.h>
#include <stdlib.h>
#include <string.h>
const int No_Of_Records =100;
int main(void)
{
struct student
{
int no;
char name[32];
float average;
};
int sno;
char sname[32];
float saverage;
ofstream outStudentFile(«studrf.dat»,ios::in|ios::out|ios::binary);
if(!outStudentFile)
{
cerr << «File could not be opened» << endl;
exit(1);
}
studentrecord.no=sno;
strcpy(studentrecord.name,sname);
studentrecord.average=saverage;
} // end while
outStudentFile.close();
return 0;
}
317
C++ معاجلة امللفات بلغة الوحدة السادسة
.7إدخال الكائنات وإخراجها
Input/Output of Objects
إن عملية التعامل مع الكائنات من حيث الكتابة في امللفات التتابعية والقراءة منها
حتتاج إلى نظرة متفحصة لكيفية القيام بعمليات القراءة والكتابة بصورة عامة ،حيث إننا،
في معظم احلاالت ،نقوم بالتعامل مع أمناط البيانات التقليدية وليس مع أمناط البيانات
املعرفة من جانب املستخدمني أو املبرمجني عند التعامل مع الينابيع اخلاصة بالقراءة
والكتابة .وللتعامل مع الكائنات فإن أفضل طريقة هي التعامل مع أعضاء البيانات (data
) membersالتي يحتوي عليها الكائن وكتابتها في امللفات التتابعية بالصورة التقليدية.
كما أن عملية قراءة بيانات الكائن تتم عبر قراءة بيانات األعضاء ومن ثم ،تخزينها في
الكائن ليتم التعامل معها داخل البرنامج وكأنها كائنات متت عملية قراءتها وكتابتها ،أي
أننا نقوم بعملية مشابهة لعملية إعادة التعريف للعمليات << و >>.إن جزء ًا من املشكلة
نابع من الطريقة التي تتم فيها عملية تخزين البيانات في امللفات التتابعية وهي الطريقة
النصية أو الرمزية ) (text or character formوذلك بخالف طريقة تخزين البيانات في
امللفات العشوائية وهي الطريقة الثنائية.
ولذلك فإن عملية التعامل مع امللفات العشوائية تشبه ،بصورة كبيرة ،عملية
التعامل مع السجالت ) .(structsولتوضيح ذلك ،عزيزي الدارس ،دعنا نعاود تعريف
سجل الطالب بصورة صنف جديد كما يلي:
class Student
{
public:
;int no
;]char name[32
;float average
;}
إن التعامل مع هذا الصنف يتم بصورة شبيهة جد ًا بالتعامل مع السجالت .البرنامج
التالي يقوم بعملية الكتابة في ملف عشوائي متت عليه عملية إنشاء ملف فارغ بالطريقة
نفسها التي مت توضيحها سابقاً.
>#include <iostream.h
318
معاجلة امللفات بلغة C++ الوحدة السادسة
#include <fstream.h>
#include <stdlib.h>
#include <string.h>
const int No_Of_Objects =100;
int main(void)
{
class Student
{
public:
int no;
char name[32];
float average;
};
Student studentobject;
int sno;
char sname[32];
float saverage;
ofstream outStudentFile(«studrfo.dat»,ios::in|ios::out|ios::binary);
if(!outStudentFile)
{
cerr << «File could not be opened» << endl;
exit(1);
}
studentobject.no=sno;
strcpy(studentobject.name,sname);
studentobject.average=saverage;
outStudentFile.seekp((studentobject.no-1)* sizeof(Student));
} // end while
outStudentFile.close();
return 0;
}
فقد متت عملية فتح امللف والتعامل معه باألسلوب، عزيزي الدارس،وكما تالحظ
والفارق الوحيد أننا اآلن نقوم،نفسه وبالطريقة نفسها التي تعاملنا بها مع السجالت
بعد،بتجميع البيانات التي نريد كتابتها على امللف العشوائي بصورة كائن تتم كتابته
.write باستخدام الدالة،seekp حتديد موقع تخزينه مستخدمني الدالة
.البرنامج التالي يقوم بقراءة كائنات مخزنة في ملف عشوائي
#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
#include <string.h>
320
C++ معاجلة امللفات بلغة الوحدة السادسة
const int No_Of_Objects =100;
int main(void)
{
class Student
{
public:
int no;
char name[32];
float average;
};
Student studentobject;
int sno;
ifstream inStudentFile(«studrfo.dat»,ios::in);
if(!inStudentFile)
{
cerr << «File could not be opened» << endl;
exit(1);
}
321
C++ معاجلة امللفات بلغة الوحدة السادسة
;))inStudentFile.seekg((sno-1)* sizeof(Student
وكما تالحظ ،عزيزي الدارس ،فقد متت عملية فتح امللف والتعامل معه باألسلوب
نفسه وبالطريقة نفسها التي تعاملنا بها مع السجالت ،والفارق الوحيد أننا اآلن نقوم
بقراءة الكائن من امللف العشوائي بعد حتديد موقع تخزينه مستخدمني الدالة ،seekg
باستخدام الدالة .readأما لعرض محتويات أو مكونات هذا الكائن ،فإننا نقوم بعرض
محتويات أعضاء البيانات التي يحتويها هذا الكائن.
322
معاجلة امللفات بلغة C++ الوحدة السادسة
.8اخلالصة
ناقشنا ،عزيزي الدارس ،في هذه الوحدة معاجلة امللفات بلغة ++Cوأهم
العمليات الضرورية عليها ،حيث عرضنا هيكلية البيانات وال��ط��رق املختلفة
لتنظيم امللفات .كما أوضحنا املفاهيم املتعلقة بامللفات والينابيع وكيفية إنشاء
امللفات التتابعية وقراءتها وحتديثها وكذلك كيفية إنشاء امللفات العشوائية وقراءتها
وحتديثها .وأخير ًا ناقشنا كيفية إدخال الكائنات وإخراجها من امللفات.
.9إجابات التدريبات
تدريب ()1
أ -البايت رقم nمن امللف.
;)inEmployeeFile.seekg(n
تدريب ()2
املوقع الفعلي للسجل = nموقع بداية امللف * )n-1( +حجم السجل
املوقع الفعلي للسجل السادس ( رقم 100 * )1-6( 1000+ =) 6
= 1500
323
معاجلة امللفات بلغة C++ الوحدة السادسة
.10مسرد املصطلحات
تنظيم امللفات : File Organizationكيفية تخزين السجالت على وسط -
التخزين.
امللف : Fileمجموعة كبيرة من البيانات املترابطة التي تخزن على وسط التخزين -
بصورة دائمة.
امللفات التتابعية : Sequential Filesامللفات التي حتتوي على السجالت -
مخزنة واحد ًا تلو اآلخر بحيث ال ميكن الوصول إلى السجالت إال بصورة
تسلسلية.
امللفات العشوائية : Random Filesامللفات التي حتتوي على السجالت مخزنة -
بحيث ميكن الوصول إلى أي سجل دون احلاجة للوصول إلى السجالت التي
تسبقه في التخزين.
نظام إدارة قواعد البيانات : Database Management Systemمجموعة من -
برامج النظم التي تساعد في عملية إنشاء قواعد البيانات ومعاجلتها وإدارتها.
هيكلية البيانات :Data Hierarchyطريقة لتوضيح بنية عناصر البيانات التي -
يتم التعامل معها في احلاسوب.
الينابيع : Streamsطرق معينة لربط البرامج أثناء تنفيذها مع أدوات اإلدخال -
واإلخراج.
324
معاجلة امللفات بلغة C++ الوحدة السادسة
املراجع.11
1. Adams, J. and Nyhoff, L., “C++ An Introduction to Computing”,
Prentice Hall, 3rd Edition, 2003.
2. Deitel, H. and Deitel, P., “C++ How to Program”, ISBN: 0-13-185757-
6, Deitel & Associates, 5th Edition, 2006.
3. Keogh, J., “Object Oriented Programming: Principles and Fundamen-
tals”, Dreamtech Press, 2004.
4. Skansholm, J. “C++ From the Beginning”, Addison Wesley, 2nd Edition,
2002.
5. Stoustrup, B., “The C++ Programming Language”, Addison Wesley,
2nd Edition, 1993.
6. Wirfs-Brock, R., Wilkerson, B., Wiener, L., “Designing Object-Ori-
ented Software”, Prentice Hall, Inc.
325
C++ معاجلة امللفات بلغة الوحدة السادسة