- - كتاب-برمجة1 (1) -

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 331

‫برنامج التكنولوجيا‬

‫والعلوم التطبيقية‬

‫برمجة (‪)1‬‬ ‫رقم املقرر ‪1291‬‬

‫لغة برمجة ‪C ++‬‬

‫إعـ ـ ـ ــداد‬
‫د‪ .‬خميس ُعمر‬ ‫د‪ .‬خليل الهندي‬
‫د‪ .‬يوسف أبو زر‬ ‫د‪ .‬عماد نزال‬
‫د‪ .‬نبيل عرفان‬

‫حقوق الطبع محفوظة‬


‫‪2011‬‬
‫جامعة القدس املفتوحة‬
‫مقرر ‪ :‬برمجة (‪ / )1‬رقم املقرر ‪1291‬‬
‫د‪ .‬خليل الهندي‬ ‫إعداد المادة العلمية‬
‫د‪ .‬خميس ُعمر‬
‫أ‪.‬د‪ .‬عبدالرؤوف احلالق‬ ‫ا لتحـــــــــكـيم‬
‫أ‪ .‬محمد البرازي‬ ‫التحـــرير اللغــوي‬ ‫فريق‬
‫أ‪ .‬محمد البرازي‬ ‫التصميم التعــليمي‬ ‫اإلعداد‬
‫أ‪ .‬محمد ج‪ .‬الطيطي‬ ‫التصمــيم الفـــني‬ ‫واإلنتاج‬
‫‪........... .......‬‬ ‫التدقيق الطـــباعي‬
‫أ‪ .‬هند ناصر‬ ‫التنضيد الطـــباعي‬

‫د‪ .‬عمــــــــاد نزال ‪ -‬د‪ .‬يو�سف �أبو زر‬ ‫إعداد المادة العلمية‬
‫د‪ .‬نبيل عرمان‬
‫د‪ .‬حممد حممود ذويب‬ ‫ا لتحـــــــــكـيم‬
‫د‪ .‬خليل خليفة‬ ‫التحـــرير اللغــوي‬ ‫فريق‬
‫�أ‪ .‬فريهان بركات‬ ‫التصميم التعــليمي‬ ‫التطوير‬
‫�أ‪ .‬حممد ج‪ .‬الطيطي‬ ‫التصمــيم الفـــني‬
‫�أ‪ .‬فريهان بركات‬ ‫التدقيق الطـــباعي‬
‫�أ‪ .‬عي�سى الهندي ‪� -‬أ‪ .‬عالية �أبو رمان‬ ‫التنضيد الطـــباعي‬
‫�أ‪ .‬ن�سيم �أحمد احلو�ساين‬ ‫المراجع�ة النهائي�ة‬

‫منشورات جامعة القدس المفتوحة‬

‫‪2010‬‬
‫حقوق النشر والطبع محفوظة جلامعة القدس املفتوحة‬

‫ص‪.‬ب (‪ )77‬أم السماق تلفون (‪ )5522561‬عمان ‪ -‬األردن‬


‫بريد ألكتروني‪amman@qou.edu :‬‬

‫‪001.434‬‬ ‫رقــم التصـنـــــــــيف‬


‫جامعة القد�س املفتوحة‬ ‫المؤلف ومن هو في حكـمه‬
‫برجمة (‪)1‬‬ ‫عنـــــوان المصــــــنف‬
‫‪ -1‬برجمة احلا�سوب‬ ‫رؤوس الموضـــــــوعات‬
‫‪ -2‬احلوا�سيب‬
‫)‪(2008/3/690‬‬ ‫رقم اإليــــــــــــــداع‬
‫عمان ‪ -‬جامعة القد�س املفتوحة‬ ‫المالحظــــــــــــــات‬

‫* مت اعداد بيانات الفهر�سة الأولية من قبل املكتبة الوطنية‬


‫مقدمة المقرر‬
‫وصف املقرر‪:‬‬
‫تعد لغة ‪ C ++‬من أكثر لغات البرمجة انتشار ًا واستخدام ًا في احلياة العملية وذلك‬ ‫ّ‬
‫لكونها لغة مرنة وقوية متكن املبرمجني من كتابة برامج تعالج مشاكل متنوعة سواء أكانت‬
‫علمية أم صناعية أم جتارية‪ ،‬وتظهر قوة هذه اللغة وإمكانياتها في كتابة برامج النظم (‪System‬‬
‫‪ )Software‬مثل نظم التشغيل واملترجمات وغيرها‪.‬‬
‫فقد أصبح هناك اهتمام واضح بالبرمجة الكينونية في تطوير البرمجيات‪ ،‬ونتيجة لذلك‬
‫عملت إضافات للغة واسعة االنتشار وهي لغة ‪ C‬واشتق منها لغة ‪ ++C‬بحيث اشتملت اللغة‬
‫ال في لغة ‪ ،C‬إضافة إلى العديد من املزايا واإلضافات‬ ‫اجلديدة على جميع املزايا املوجودة أص ً‬
‫اخلاصة بالبرمجة الكينونية‪ ،‬وبذلك ميكن تطوير برمجيات مختلفة باستخدام أساليب البرمجة‬
‫الكينونية باستخدام لغة ‪ C ++‬مما ميكن املبرمج من االستفادة من جميع اخلصائص اجليدة في‬
‫البرمجة الكينونية مثل إعادة استخدام البرامج (‪ ،)Software Reuse‬والوراثة (‪،)Inheritance‬‬
‫وإخفاء املعلومات (‪ )Information Hiding‬وغيرها من امليزات األخرى‪.‬‬
‫لقد وضع هذا املقرر بحيث تعالج الوحدة األولى املفاهيم األساسية في تطوير البرامج‬
‫الكينونية بصورة مستقلة عن أي لغة برمجة‪ ،‬وتعالج الوحدات ‪ 5-2‬املفاهيم األسـاسية للغة‬
‫‪ ،C ++‬وتعالج الوحدة السادسة موضوع معاجلة امللفات بلغة ‪.C ++‬‬

‫األهداف العامة للمقرر‪:‬‬


‫بعد االنتهاء من دراسة هذا املقرر والقيام بالتدريبات واألنشطة الواردة فيه‪ ،‬يتوقع منك‪،‬‬
‫عزيزي الدارس‪ ،‬أن تصبح قادرا على أن‪:‬‬
‫تعرف املفاهيم األساسية لتطوير برامج كينونية املنحى‪.‬‬
‫‪ّ .1‬‬
‫تعرف كيفية التحليل والتصميم الكينوني والبرمجة الكينونية‪.‬‬ ‫‪ّ .2‬‬
‫‪ .3‬تستخدم األصناف بعناصرها املختلفة وتنشئ كائنات منها‪.‬‬
‫‪ .4‬تستخدم اإلدخال‪/‬اإلخراج في لغة ‪. C ++‬‬
‫تعرف املصفوفات وتبني كيفية التعامل مع مصفوفات من الكائنات وإجراء العمليات‬ ‫‪ّ .5‬‬
‫عليها‪.‬‬
‫تعرف وتستخدم الدوال واألصناف الصديقة والعمليات‪.‬‬ ‫‪ّ .6‬‬
‫أ‬
‫‪ .7‬تبني أنواع الوراثة املختلفة وكيفية استخدامها‪.‬‬
‫تعرف وتستخدم الدوال واألصناف القالبية‪.‬‬ ‫‪ّ .8‬‬
‫تعرف خاصية تعدد األوجه وتستخدمها عند حاجتها‪.‬‬ ‫‪ّ .9‬‬
‫تعرف وتستخدم امللفات بأنواعها املختلفة‪.‬‬‫‪ّ .10‬‬

‫محتوى املقرر‪:‬‬
‫ويتألف هذا املقرر من ست وحدات دراسية على النحو التالي‪:‬‬
‫الوحدة األولـى‪ :‬تطوير البرامج الكينونية‪ ،‬تهدف هذه الوحدة إلى تبيان املفاهيم املستخدمة‬
‫في البرمجة الكينونية والتحليل الكينوني والتصميم الكينوني‪.‬‬
‫الوحدة الثانيـة‪ :‬التراكيب واألصناف‪ ،‬تعرض هذه الوحدة أهم أنواع البيانات املركبة‪ .‬وتركز‬
‫بشكل خاص على األصناف والكائنات والدوال املنتمية‪ .‬وهي بذلك أولى‬
‫الوحدات التي تناقش املفاهيم األساسية للغة ‪.C ++‬‬
‫الوحدة الثالثـة‪ :‬املصفوفات واألصناف‪ ،‬تشرح هذه الوحدة كيفية التعامل مع قائمة من‬
‫الكائنات بطرق مختلفة‪.‬‬
‫الوحدة الرابعة‪ :‬الدوال الصديقة والعمليات‪ ،‬تناقش كيفية تعريف الدوال الصديقة والعمليات‬
‫وكيفية استخدامها‪.‬‬
‫الوحدة اخلامسة‪ :‬األصناف املشتقة والقوالب‪ ،‬تناقش هذه الوحدة الطرق املختلفة إلعادة‬
‫استخدام البرامج عن طريق اشتقاق األصناف والوراثة والدوال واألصناف‬
‫القالبية‪.‬‬
‫تتطرق هذه الوحدة إلى تعريف هيكل البيانات‬‫الوحدة السادسة‪ :‬معاجلة امللفات بلغة ‪ّ ،C ++‬‬
‫وموقع امللفات بينها والطرق املختلفة في تنظيم امللفات والينابيع وكيفية‬
‫إنشاء امللفات التتابعية وامللفات العشوائية وقراءتها وحتديثها وأيض ًا إدخال‬
‫الكائنات وإخراجها‪.‬‬

‫والله ولي التوفيق؛؛؛‬

‫ب‬
‫محتويات المقرر‬
‫الصفحة‬ ‫عنوان الوحدة‬ ‫رقم الوحدة‬

‫‪1‬‬ ‫تطوير البرامج الكينونية‬ ‫(‪)01‬‬

‫‪21‬‬ ‫التراكيب واألصناف‬ ‫(‪)02‬‬

‫‪69‬‬ ‫املصفوفات واألصناف‬ ‫(‪)03‬‬

‫‪149‬‬ ‫الدوال الصديقة والعمليات‬ ‫(‪)04‬‬

‫‪207‬‬ ‫األصناف املشتقة والقوالب‬ ‫(‪)05‬‬

‫‪289‬‬ ‫معاجلة امللفات بلغة ‪C++‬‬ ‫(‪)06‬‬

‫ج‬
‫د‬
‫الوحدة األولى‬
‫تطوير البرامج الكينونية‬
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.‬‬

‫‪ 5.1‬ما حتتاج إليه لدراسة الوحدة‬


‫لقد تضمنت هذه الوحدة العديد من املفاهيم األساسية‪ ،‬وبذلك فإن طبيعة‬
‫امل��ادة املعروضة‪ ،‬عزيزي ال���دارس‪ ،‬حتتاج إل��ى جو ه��ادئ خ��اص ومريح حتى‬
‫تستطيع التركيز على املفاهيم املعطاة‪ ،‬وتستوعبها بالشكل املناسب‪ .‬وك��ل ما‬
‫حتتاجه بعد ذلك بعض القرطاسية كقلم رصاص وورق أبيض لتقوم بتطبيق بعض‬
‫املفاهيم وحتل األسئلة والتدريبات املعطاة في ثنايا الوحدة‪.‬‬

‫‪6‬‬
‫تطوير البرامج الكينونية‬ ‫الوحدة األولى‬
‫‪ .2‬املفاهيم األساسية لتطوير البرامج الكينونية‬
‫يعد مصطلح “الكينونية” من املصطلحات املهمة في عالم احلاسوب هذه األيام‪ ،‬فأنت‪،‬‬ ‫ّ‬
‫عزيزي الدارس‪ ،‬تسمع مصطلحات كثيرة لها عالقة بهذا املصطلح مثل التحليل الكينوني‪،‬‬
‫التصميم الكينوني‪ ،‬البرمجة الكينونية وغير ذلك من املصطلحات ذات الصلة‪ .‬ولعلك تتساءل‬
‫عن املعنى الدقيق لهذا املصطلح الذي أصبح يتردد بصورة كبيرة بني الدارسني لتخصصات‬
‫احلاسوب إضافة إلى العاملني في مجاالت احلاسوب املختلفة‪ ،‬وحقيقة األمر أنه ال يوجد‬
‫تعريف أو معنى دقيق ومطلق ملصطلح “الكينونية” ولكننا نستطيع القول أنه طريقة أو أسلوب‬
‫جديد في تطوير البرامج واألنظمة احلاسوبية من خالل التركيز على الكينونات‪/‬الكائنات‬
‫ال من التركيز على الوظائف الرئيسة التي يجب على هذه األنظمة إجنازها‪.‬‬ ‫الرئيسة فيها بد ً‬
‫في هذه الوحدة‪ ،‬سنعرض‪ ،‬عزيزي الدارس‪ ،‬املفاهيم األساسية املتعلقة بالتحليل الكينوني‬
‫باعتبارها خطوة أساسية في أي عملية تطوير للبرامج الكينونية من خالل حتديد الكينونات‬
‫ثم‪ ،‬سنعرض املفاهيم واألفكار الرئيسة املتعلقة بالتصميم‬ ‫وخصائصها والعالقات بينها ومن ّ‬
‫الكينوني من خالل بناء العمليات الرئيسة املرتبطة بالكينونات املختلفة إضافة إلى اختيار لغة البرمجة‬
‫الكينونية املناسبة‪ ،‬وأخيراً‪ ،‬سنعرض املفاهيم األساسية املتعلقة بالبرمجة الكينونية من خالل التركيز‬
‫على خصائص البرامج الكينونية اجليدة واخلصائص األساسية في لغات البرمجة الكينونية‪.‬‬
‫وكما أشرنا‪ ،‬عزيزي الدارس‪ ،‬فإن التطوير الكينوني هو طريقة أو أسلوب جديد في تطوير‬
‫ال من التركيز على‬‫البرامج واألنظمة احلاسوبية من خالل التركيز على الكينونات الرئيسة فيها بد ً‬
‫الوظائف الرئيسة التي يجب على هذه األنظمة إجنازها‪ ،‬ولكن ما هو الكائن‪/‬الكينونة؟‬
‫إن مفهوم الكائن (‪ )Object‬مرتبط مبجموعة من اخلصائص التي حتدد من خالل مجموعة‬
‫من املتغيرات التي تصف حالته ومجموعة من العمليات التي يستطيع إجنازها‪ .‬إن كل كائن‬
‫(‪ )Object‬له حالة ميكن أن تتغير أثناء تنفيذ البرنامج‪ .‬ولكن من أين نحصل على الكينونات‪/‬‬
‫تعد األصناف (‪ )Classes‬مصنع ًا للكائنات‪ ،‬حيث تستخدم إلنشاء الكائنات التي‬ ‫الكائنات؟ ّ‬
‫حتتوي على متغيرات ودوال منتمية‪ .‬وستتعرف‪ ،‬عزيزي الدارس‪ ،‬على كيفية إنشاء الكائنات‬
‫والتعامل معها في الوحدات القادمة من هذا املقرر‪.‬‬
‫تعد البرمجة الكينونية أسلوب ًا جديد ًا في البرمجة‪ ،‬حيث توفر إمكانيات متعددة مثل التنظيم‬
‫تعد أسلوب ًا جديد ًا في التفكير‬
‫اجليد‪ ،‬واالستخدام املتعدد‪ ،‬واملقاطع البرمجية املتكاملة‪ :‬كما ّ‬
‫وفي بناء البرامج؛ حيث جتمع البرامج حقول البيانات مع البرامج الفرعية التي تستند في عملها‬

‫‪7‬‬
‫تطوير البرامج الكينونية‬ ‫الوحدة األولى‬
‫على تلك احلقول في بنية واحدة تسمى كائن ًا (‪ )Object‬ويتم التعامل مع الكائن (‪)Object‬‬
‫وكأنه وحدة واحدة‪.‬‬
‫ومن املناسب أن يكون هدفنا عند استخدام الكينونات أن نبني نظام ًا أو منوذج ًا يحاكي‬
‫املسألة قيد الدراسة كما هو في الواقع‪ ،‬ولتقريب ذلك إلى األذهان‪ ،‬ميكن اعتبار احلاسوب‬
‫منوذجاً‪ ،‬ومن ثم‪ ،‬فإن هذا النموذج ميتلك كينونات تشكل جزءا من تركيبته‪ ،‬وكل كينون له‬
‫مهمة محددة ويستخدم أدواته اخلاصة به وله روابط مع الكينونات األخرى‪ ،‬فعلى سبيل املثال‪،‬‬
‫تعد لوحة املفاتيح والفأرة ووحدة املعاجلة املركزية والقرص الصلب كينونات تشكل جزء ًا من‬ ‫ّ‬
‫تركيبة احلاسوب ولكل منها مهمة محددة‪.‬‬

‫مثال (‪)1‬‬
‫اكتب برنامج ًا حلساب صافي رواتب عدد من املوظفني في دائرة معينة‪ ،‬وإمكانية معرفة‬
‫صافي راتب أي موظف عند اللزوم‪.‬‬

‫إن مواصفات قيد كل موظف هي‪ :‬االسم‪ ،‬الراتب اإلجمالي‪ ،‬االقتطاعات‪،‬‬


‫صافي الراتب‪ ،‬الدائرة‪ .‬وحلل املسألة يحدد كل كينون وتركيبه‪ ،‬وميكن مالحظة وجود‬
‫كينونني في هذه املسألة هما املوظف والدائرة‪ .‬ونحتاج معرفة املتغيرات التي يتضمنها كل‬
‫كينون وهي بيانات محلية خاصة بالكينون نفسه وذلك كما يلي‪:‬‬
‫الكينون «موظف»‬
‫يضم مجموعة اخلصائص التالية‪:‬‬
‫• اسم املوظف‪ :‬مجموعة الرموز‪.‬‬
‫• الراتب اإلجمالي‪ :‬عدد حقيقي‪.‬‬
‫• االقتطاعات‪ :‬عدد حقيقي‪.‬‬
‫• صافي الراتب‪ :‬عدد حقيقي‪.‬‬
‫الكينون «الدائرة»‬
‫يضم مجموعة اخلصائص التالية‪:‬‬
‫•اسم الدائرة‪ :‬مجموعة رموز‪.‬‬
‫•مجموعة كينونات (موظفني)‪ :‬منظومة من املوظفني‪.‬‬
‫•عدد املوظفني في الدائرة‪ :‬عدد صحيح‪.‬‬

‫‪8‬‬
‫تطوير البرامج الكينونية‬ ‫الوحدة األولى‬
‫ومن اجلدير بالذكر أن البرمجة الكينونية تهدف إلى إضافة ميزات التعميم عند تصميم‬
‫الكينون األمر الذي يتطلب حتديد مجموعة من العمليات على الكينون وتعريفها وهي التي‬
‫تساعد املستخدم على تنفيذ مهامه‪ .‬ومن العمليات في الكينون «موظف»‪ ،‬نذكر ما يلي‪:‬‬
‫‪ -1‬بناء‪/‬إنشاء الكينون وحتديد املعطيات األولية‪.‬‬
‫‪ -2‬حتديد اسم املوظف وإسناده‪.‬‬
‫‪ -3‬تعيني البيانات اخلاصة باملوظف‪.‬‬
‫‪ -4‬حساب صافي الراتب‪.‬‬
‫‪ -5‬إظهار النتائج‪.‬‬
‫‪ -6‬إنهاء وجود الكينون‪.‬‬

‫وبالطريقة نفسها حتدد العمليات املطلوبة للكينون “الدائرة”‪ .‬كما أنه من اجلدير‬
‫بالذكر أن املهام التي يقوم بها كل كينيون تشكل اخلدمات التي يقدمها الكينون للكينونات‬
‫األخرى‪ ،‬فإذا مت استدعاء عملية من خالل كينون معني‪ ،‬فإنه يطلب منه تأدية مهمة تفيد‬
‫كينون ًا آخر‪.‬‬

‫أسئلة التقومي الذاتي (‪)1‬‬


‫‪ .1‬ما املقصود بالكينونية؟‬
‫‪ .2‬وضح الفرق بني الكينونات واألصناف‪.‬‬

‫‪9‬‬
‫تطوير البرامج الكينونية‬ ‫الوحدة األولى‬
‫‪Object-Oriented Analysis‬‬ ‫‪ .3‬التحليل الكينوني‬
‫إن الهدف من التحليل الكينوني هو أن تصبح‪ ،‬عزيزي الدارس‪ ،‬على معرفة أكبر‬
‫مبتطلبات البرنامج أو النظام والبدء في عملية بناء لنموذج أولي للبرنامج من خالل التركيز‬
‫على الكائنات األساسية‪ ،‬حيث ميكن تلخيص هذه العملية مبا يلي‪:‬‬
‫‪ -‬حتديد الكائنات في هذا النموذج األولي‪.‬‬
‫‪ -‬وصف خصائص الكائنات‪.‬‬
‫‪ -‬ربط الكائنات املختلفة مع بعضها‪.‬‬
‫‪ -‬جتميع الكائنات املختلفة‪.‬‬

‫‪ 1.3‬حتديد الكينونات‬
‫قد تبدو عملية حتديد الكائنات األساسية معقدة في البداية‪ ،‬إال أن التفكير في البرنامج‬
‫الكينوني والنظر إليه باعتباره منوذج ًا لتمثيل الواقع يجعل هذه العملية سهلة نسبي ًا وذلك‬
‫من خالل جعل كل شيء له خصائص معينة ميكن متثيلها من خالل بيانات معينة في هذا‬
‫الواقع كائناً‪ ،‬كما أن هناك طريقة أخرى لتحديد الكائنات وذلك من خالل التركيز على‬
‫األسماء األساسية املستخدمة في وصف وظائف البرنامج أو النظام والنظر إليها باعتبارها‬
‫كائنات محتملة في البرنامج الكينوني‪.‬‬

‫‪ 2.3‬حتديد خصائص الكينونات‬


‫إن لكل كائن (‪ )Object‬مجموعة من امليزات التي تصفه وتوضح حالته‪ ،‬وميكن‪،‬‬
‫عزيزي الدارس‪ ،‬االستفادة من عملية وصف خصائص الكائنات لتحديد الكائنات الصحيحة‬
‫واملطلوبة‪ ،‬وذلك من خالل استثناء بعض الكائنات التي ليس لها خصائص ضمن البرنامج‪.‬‬
‫فإذا افترضنا أننا بصدد حتديد الكينونات في نظام التسجيل في جامعة القدس‬
‫املفتوحة‪ ،‬فإننا نالحظ أن هناك عدد ًا من الكينونات مثل املقررات الدراسية والدارسني واملشرفني‬
‫األكادمييني وغيرها‪ .‬إن كل كائن (‪ )Object‬من هذه الكينونات له مجموعة من اخلصائص التي‬
‫ميكن متثيلها ببيانات معينة‪ .‬فاملقرر‪ ،‬على سبيل املثال‪ ،‬له مجموعة من اخلصائص مثل الرقم‪،‬‬
‫االسم‪ ،‬عدد الساعات‪ ،‬الوصف ‪...‬إلخ‪ .‬والدارس له مجموعة من اخلصائص مثل الرقم‪،‬‬
‫يعد مؤشر ًا أساسي ًا‬
‫االسم‪ ،‬العنوان‪ ،‬رقم الهاتف‪ ،‬العنوان‪...‬إلخ‪ .‬إن وجود هذه اخلصائص ّ‬
‫كينونات أساسية في نظام التسجيل‪.‬‬
‫ٌ‬ ‫على أن املقررات والدارسني‬
‫‪10‬‬
‫تطوير البرامج الكينونية‬ ‫الوحدة األولى‬
‫وعادة ما متثل األصناف التي تشتق منها الكينونات باستخدام رمز خاص هو‬
‫املستطيل‪ ،‬حيث يقسم املستطيل إلى ثالثة أقسام‪ ،‬يوضع في القسم األول العلوي اسم‬
‫الصنف‪ ،‬وفي القسم الثاني خصائص الصنف‪ ،‬وفي القسم الثالث مجموعة العمليات‬
‫املختلفة اخلاصة بالصنف‪.‬‬

‫ ‬‫مثال (‪)2‬‬
‫لتمثيل الصنف “العربة” التي لها اخلصائص “السرعة‪ ،‬اللون‪ ،‬الوزن” والعملية‬
‫“تغيير السرعة”‪ ،‬نستخدم الرمز اخلاص بذلك ونقوم بتقسيمه حسب ما مت توضيحه‬
‫سابق ًا كما يلي‪:‬‬

‫العربة‬

‫السرعة‬
‫اللون‬
‫الوزن‬

‫تغيير السرعة‬

‫‪ 3.3‬حتديد العالقات بني الكينونات‬


‫إن عملية التحليل الكينوني ستوضح العالقات والروابط األساسية بني الكائنات املختلفة‬
‫وذلك من خالل التركيز على التفاعالت املختلفة بني الكائنات وعالقة الكائنات املختلفة بعضها‬
‫ببعض‪ ،‬وبشكل عام‪ ،‬فإن هناك ثالثة أنواع أساسية من العالقات‪:‬‬
‫‪ -‬عالقة يعرف (‪)knows‬‬
‫‪ -‬عالقة يكون (‪)is‬‬
‫‪ -‬عالقة ميلك (‪)has‬‬
‫يوضح الشكل (‪ )1‬عالقة يعرف (‪ )knows‬بني الدارس واملقرر الذي سيدرسه؛ إذ‬
‫يجب على الدارس أن يعرف املقرر الذي سيدرسه كما يجب أن يكون ملقرر معني قائمة‬
‫من الدارسني الذين سيدرسونه‪.‬‬
‫‪11‬‬
‫تطوير البرامج الكينونية‬ ‫الوحدة األولى‬
‫املقرر‬ ‫يدرس‬ ‫الطالب‬

‫الشكل (‪)1‬‬

‫مكون من‬
‫ٌ‬ ‫أما العالقة الثانية فهي عالقة ميلك (‪ ،)has‬التي تصف حقيقة أن كائن ًا معين ًا‬
‫أجزاء أخرى حيث إن كل جزء ميثل كائن ًا آخر‪ .‬فعلى سبيل املثال‪ ،‬يتكون جهاز احلاسوب‬
‫من شاشة‪ ،‬لوحة مفاتيح‪ ،‬فأرة‪... ،‬إلخ ويوضح الشكل (‪ )2‬عالقة ميلك (‪ )has‬بني‬
‫جهاز حلاسوب وأجزائه املختلفة‪.‬‬

‫احلاسوب‬ ‫)‪Empty (weak aggregation‬‬


‫)‪(strong aggregation‬‬

‫فأرة‬ ‫لوحة مفاتيح‬ ‫شاشة‬

‫الشكل (‪)2‬‬

‫أما العالقة الثالثة في عملية تطوير البرامج الكينونية فهي عالقة يكون (‪ )is‬التي‬
‫تصف حقيقة أن كائن ًا معين ًا يكون له خصائص عامة يشترك بها مع أصناف أخرى حيث‬
‫يتم وصف اخلصائص العامة من خالل صنف رئيس‪ ،‬فعلى سبيل املثال‪ ،‬فإن السيارات‪،‬‬
‫والدراجات والقطارات لها خصائص مشتركة مثل السرعة والوزن حيث ميكن تعميمها‬
‫في صنف رئيس يسمى العربة كما هو موضح في الشكل (‪.)3‬‬

‫احلاسوب‬

‫فأرة‬ ‫لوحة مفاتيح‬ ‫شاشة‬

‫الشكل (‪)3‬‬
‫‪12‬‬
‫تطوير البرامج الكينونية‬ ‫الوحدة األولى‬
‫وهناك بعض اخلصائص احمللية التي تكون خاصة باألصناف الفرعية وال تنطبق على‬
‫الصنف الرئيس مثل قوة احملرك للسيارة وعدد العربات للقطار‪ ،‬وهل هناك غيار أم ال‬
‫للدراجة‪ .‬وباستخدام هذه العالقة فإنه ميكن القول أن السيارة عربة وأن الدراجة عربة وكذلك‬
‫القطار فإنه عربة‪ .‬متثل هذه العالقة خاصية أساسية في البرمجة الكينونية وهي مفهوم الوراثة‬
‫(‪ )Inheritance‬التي متكننا من استخدام كائنات عرفت سابق ًا وتعديلها واإلضافة عليها مبا‬
‫يتناسب مع متطلبات برامج جديدة‪.‬‬
‫وهناك مالحظة بخصوص الرموز املستخدمة في وصف العالقات املختلفة؛ إذ قمنا‬
‫باستخدام الرموز املستخدمة في لغة بناء النماذج املوحدة املسماة ‪Unified Modeling‬‬
‫‪ Language) UML‬التي تعد لغة شبه قياسية في بناء النماذج اخلاصة بتطوير البرامج واألنظمة‬
‫الكينونية‪.‬‬

‫‪ 4.3‬جتميع الكينونات‬
‫إن البرامج الكبيرة نسبيا تشتمل عادة على عدد كبير نسبيا من األصناف‪ ،‬ولذلك فإنه من‬
‫املفيد جد ًا جتميع األصناف املترابطة مع بعضها في مجموعة من الرزم (‪ )Packages‬وذلك‬
‫لتسهيل عملية التعامل مع البرنامج ولتنظيم األصناف املختلفة مبا يكفل تسهيل عملية التعديل‬
‫والصيانة لهذه األصناف‪.‬‬

‫ ‬
‫تدريب (‪)1‬‬
‫إذا علمت أن السيارة مكونة من محرك‪ ،‬وعجالت‪ ،‬وجسم‪ ،‬فمثل ذلك‬
‫باستخدام العالقة املناسبة‪.‬‬

‫ ‬
‫تدريب (‪)2‬‬
‫إذا علمت أن األشخاص املوجودين في جامعة القدس املفتوحة ينتمون إلى عدة فئات؛‬
‫فمنهم اإلداريون‪ ،‬واألكادمييون والدارسون‪ ،‬فمثل ذلك باستخدام العالقة املناسبة‪.‬‬

‫ ‬‫أسئلة التقومي الذاتي (‪)2‬‬


‫‪ .1‬ما الفرق بني عالقة «يكون» وعالقة «يعرف»؟‬
‫‪ .2‬كيف ميكن حتديد الكينونات األساسية في برنامج معني؟‬
‫‪13‬‬
‫تطوير البرامج الكينونية‬ ‫الوحدة األولى‬
‫‪ .4‬التصميم الكينوني ‪Object-Oriented Design‬‬
‫لقد قمنا‪ ،‬عزيزي الدارس‪ ،‬بوصف عملية التحليل الكينوني التي متثل املرحلة‬
‫األولى في عملية تطوير البرامج الكينونية‪ ،‬وإلى جانب كونها العملية األولى‪ ،‬إال أنها‬
‫متثل املرحلة األساسية التي تعتمد عليها املراحل الالحقة حيث يأتي بعدها عملية التصميم‬
‫الكينوني التي يتم أثناءها عمل الرسومات والنماذج اخلاصة بالبرنامج الكينوني وإكمال‬
‫إضافة بعض املعلومات لألصناف وذلك من خالل التركيز على كيفية عمل األشياء وليس‬
‫فقط على مجرد التركيز على حتديد هذه األشياء‪.‬‬

‫‪ 1.4‬بناء العمليات‬
‫ففي هذه املرحلة‪ ،‬يتم البدء بالكائنات التي حددت في مرحلة التحليل الكينوني وتتم‬
‫إضافة التفصيالت الضرورية التي قد تشمل تطبيق مجموعة من اخلوارزميات املالئمة‬
‫واتخاذ القرارات اخلاصة بكيفية إجناز العمليات املختلفة واملعلمات (‪ )Parameters‬التي‬
‫حتتاجها‪ ،‬إضافة إلى اختيار الطريقة األنسب للتمثيل الداخلي لهذه الكائنات‪.‬‬
‫فعلى سبيل املثال‪ ،‬إذا كنا بصدد بناء برنامج للتعامل مع األشكال الهندسية املختلفة‬
‫مثل املستطيل‪ ،‬املربع‪ ،‬الدائرة‪ ،‬املثلث‪ ،‬فإنه من الواضح أن املستطيل هو أحد األصناف‬
‫األساسية هنا‪ .‬إن هناك مجموعة من اخلصائص التي ميكن حتديدها للمستطيل مثل‬
‫الطول والعرض حيث يتم ذلك في العادة أثناء مرحلة التحليل‪ .‬أما في مرحلة التصميم‬
‫فإننا نحتاج إلى حتديد العمليات األساسية على هذا املستطيل مثل عملية حساب مساحة‬
‫املستطيل حيث نقوم بتحديد كيفية حساب املساحة من خالل عملية ضرب طول املستطيل‬
‫بعرضه‪ .‬من الواضح أن هذه العملية حتتاج‪ ،‬بصورة عامة‪ ،‬إلى معلمتني أساسيتني وهما‬
‫طول املستطيل وعرضه‪ ،‬وسوف تقوم بإعادة قيمة املساحة‪.‬‬

‫‪ 2.4‬اختيار لغة البرمجة الكينونية‬


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

‫‪14‬‬
‫تطوير البرامج الكينونية‬ ‫الوحدة األولى‬
‫واجلدير ذكره أنه يفضل أن تتم عملية تنفيذ التصميم الكينوني بلغة برمجة كينونية‬
‫يعد شرط ًا أساسياً؛ إذ ميكن أن تتم عملية التنفيذ بلغة غير كينونية وذلك‬
‫إال أن ذلك ال ّ‬
‫يعد أمر ًا‬
‫من خالل التعامل مع األصناف وكأنها تراكيب بيانات جتريدية‪ ،‬إال أن ذلك ال ّ‬
‫محبذ ًا ألننا سنفقد اخلصائص واحلسنات األساسية املرتبطة بتطوير البرامج الكينونية‬
‫وخاصة الفائدة من إعادة استخدام بعض األصناف والوراثة وإخفاء املعلومات وغيرها‬
‫من امليزات اجليدة لتطوير البرامج الكينونية‪.‬‬

‫أسئلة التقومي الذاتي (‪)3‬‬

‫‪ .1‬ما الفرق األساسي بني التحليل الكينوني والتصميم الكينوني؟‬


‫‪ .2‬وضح الفائدة من استخدام لغة برمجة كينونية لتنفيذ التصميم الكينوني‪.‬‬

‫‪15‬‬
‫تطوير البرامج الكينونية‬ ‫الوحدة األولى‬
‫‪ .5‬البرمجة الكينونية ‪Object-Oriented Programming‬‬
‫إن الهدف من مرحلة البرمجة في تطوير البرامج واألنظمة‪ ،‬بصورة عامة‪ ،‬هو تنفيذ‬
‫النظام والوصول إلى برامج قابلة للتنفيذ‪ ،‬إال أن كون البرنامج قاب ً‬
‫ال للتنفيذ ال يعني‬
‫بالضرورة أنه برنامج جيد باملقاييس العامة كما أن هناك مجموعة من اخلصائص اجليدة‬
‫للغات البرمجة الكينونية وهو ما سنقوم بتوضيحه في األقسام التالية من هذه الوحدة‪.‬‬

‫‪ 1.5‬خصائص البرامج اجليدة‬


‫ميكن اعتبار البرنامج جيدا إذا توافرت فيه اخلصائص األساسية التالية‪:‬‬
‫‪ -1‬أن يكون برنامج ًا صحيحاً‪ :‬ميكن القول أن برنامج ًا معين ًا هو برنامج صحيح إذا قام بأداء‬
‫وتعد هذه اخلاصية من‬
‫الوظيفة احملددة في مرحلة التحليل والتصميم من دون أخطاء‪ّ .‬‬
‫أهم اخلصائص الالزم توافرها في البرامج‪ ،‬بصورة عامة؛ إذ إن البرامج التي حتتوي على‬
‫أخطاء ال ميكن قبولها بحال من األحوال وبذلك فإنه ال ميكن القول بأنها برامج جيدة‪.‬‬
‫‪ -2‬أن يكون برنامج ًا فعا ًال‪ :‬ميكن القول أن البرنامج فعال إذا قام بأداء الوظيفة املطلوبة من‬
‫خالل االستغالل األمثل لنظام احلاسوب ومكوناته‪ .‬إن هذه اخلاصية أيضا مهمة جدا؛‬
‫فالبرنامج الذي يحل مسألة معينة في وقت أطول من الوقت الذي يحتاجه اإلنسان حلل‬
‫املسألة نفسها ال ميكن قبوله أو القول بأنه برنامج جيد من حيث الفعالية‪.‬‬
‫ال إلعادة االستخدام‪ :‬ميكن القول أن البرنامج قابل إلعادة‬ ‫‪ -3‬أن يكون برنامج ًا قاب ً‬
‫االستخدام إذا ُبني من مكونات ميكن االستفادة منها في بناء برامج أخرى‪ .‬وفي العادة‬
‫تكون البرامج اجلديدة التي سيتم االستفادة في بنائها من برامج أخرى برامج مشابهة‬
‫للبرامج األصلية‪ .‬ومن اجلدير ذكره أنه ليس من السهولة بناء برامج قابلة إلعادة‬
‫االستخدام حيث إن هناك مجموعة من القيود والشروط التي يجب أن حتققها املكونات‬
‫التي ميكن إعادة استخدامها‪ ،‬إال أن الفائدة التي نحصل عليها تكون كبيرة ومبررة للجهد‬
‫املبذول للوصول إلى تلك البرامج‪.‬‬

‫وتعد اخلصائص السابقة من اخلصائص األساسية للبرامج اجليدة‪ ،‬بصورة عامة‪ ،‬إال أن‬ ‫ّ‬
‫هناك مجموعة أخرى من اخلصائص املهمة التي يجب توافرها في البرامج‪ ،‬بصورة عامة‪،‬‬
‫مثل أن يكون برنامج ًا مرن ًا وقاب ً‬
‫ال للتكيف مع الظروف والبيئات التي سيعمل بها وأن تكون‬
‫تكلفة صيانته منخفضة‪.‬‬
‫‪16‬‬
‫تطوير البرامج الكينونية‬ ‫الوحدة األولى‬
‫‪ 2.5‬اخلصائص األساسية في لغات البرمجة الكينونية‬
‫إن من اخلصائص األساسية‪ ،‬عزيزي الدارس‪ ،‬للغات البرمجة الكينونية هو توافر‬
‫املكونات الرئيسة التي متكننا من حتقيق فائدتني أساسيتني في البرمجة الكينونية وهما‪:‬‬
‫‪ .1‬خاصية إخفاء املعلومات (‪)Information Hiding‬‬
‫ان املقصود بهذه اخلاصية في لغة البرمجة الكينونية أن حتتوي هذه اللغة على تركيب معني‬
‫ميكننا من تعريف خصائص الكائن ووضعه‪ ،‬من متغيرات ودوال منتمية‪ ،‬في مكان واحد في‬
‫البرنامج وبحيث ال تتمكن الكائنات األخرى من الوصول إلى هذه اخلصائص أو رؤيتها‪.‬‬

‫‪ .2‬خاصية الوراثة (‪)Inheritance‬‬


‫إن املقصود بهذه اخلاصية في لغة البرمجة الكينونية أن حتتوي هذه اللغة على تركيب‬
‫معني ميكننا من إعادة استخدام كائنات عرفت سابق ًا وتعديلها واإلضافة عليها مبا يتناسب مع‬
‫متطلبات البرنامج اجلديد‪ .‬إن هذه اخلاصية‪ ،‬عزيزي الدارس‪ ،‬مرتبطة بالعالقة (يكون) التي‬
‫مت توضيحها سابقاً‪.‬‬
‫إن هاتني اخلاصيتني ال تعنيان‪ ،‬عزيزي الدارس‪ ،‬أنهما اخلاصيتان املطلوبتان فقط في‬
‫لغات البرمجة الكينونية اجليدة‪ ،‬ولكنهما متثالن احلد األدنى من اخلصائص اجليدة املطلوبة‪.‬‬
‫فعلى سبيل املثال‪ ،‬فإن وجود إمكانية بناء برامج فرعية عامة (‪ )Generic Subprograms‬هي‬
‫من اخلصائص املهمة في لغات البرمجة‪ ،‬وال شك في أن وجودها يسهل في عملية بناء برامج‬
‫عامة ميكنها التعامل مع أنواع مختلفة من البيانات‪ ،‬مما يسهل على املبرمجني عملية كتابة برامج‬
‫فرعية لكل نوع من أنواع البيانات ويقلل من حجم األسطر وعددها املطلوب كتابتها‪ ،‬مما يزيد‬
‫من إنتاجية املبرمجني‪ ،‬بصورة عامة‪ ،‬حيث تعد إنتاجية املبرمجني عند استخدام لغة برمجة‬
‫معينة من العوامل املهمة في اختيار لغة البرمجة التي سيتم استخدامها في بناء برنامج معني‪.‬‬

‫أسئلة التقومي الذاتي (‪)4‬‬

‫‪ .1‬ما اخلصائص األساسية للبرامج الكينونية اجليدة؟‬


‫‪ .2‬ما اخلصائص األساسية للغات البرمجة الكينونية اجليدة؟‬

‫‪17‬‬
‫تطوير البرامج الكينونية‬ ‫الوحدة األولى‬
‫‪ .6‬اخلالصة‬
‫مت في هذه الوحدة إعطاؤك معلومات أساسية فيما يتعلق بتطوير البرامج‬
‫الكينونية‪ ،‬وخاصة التحليل‪ ،‬والتصميم الكينوني‪ ،‬وتوضيح بعض املفاهيم‬
‫واملصطلحات األساسية اخلاصة بذلك‪ .‬لقد تناولت هذه الوحدة تعريف املفاهيم‬
‫األساسية اخلاصة بتطوير البرامج الكينونية إضافة إلى تعريف مفهوم التحليل‬
‫الكينوني من حيث‪ ،‬حتديد الكينونات وخصائصها والعالقات بينها‪ .‬وتبع ذلك‬
‫مناقشة التصميم الكينوني من حيث‪ ،‬بناء العمليات واختيار لغة البرمجة الكينونية‬
‫املناسبة‪ .‬وأخيرا توضيح املفاهيم األساسية املرتبطة بالبرمجة الكينونية وخصائص‬
‫البرمجة اجليدة إضافة إلى اخلصائص األساسية للغات البرمجة الكينونية‪.‬‬

‫‪ .7‬حملة عن الوحدة الدراسية الثانية‬


‫في الوحدة الثانية‪ ،‬سوف نتطرق إلى تفصيالت أكثر بلغة‪ ،C ++‬حيث سيتم‬
‫شرح مفاهيم أساسية وخاصة مفهوم األصناف الذي يعد الفكرة األساسية في‬
‫لغات البرمجة الكينونية (‪ .)Object-Oriented Languages‬كما سنتطرق ملفاهيم‬
‫التراكيب (‪ )Structures‬التي تقابل السجالت (‪ )Records‬وعالقتها باألصناف‬
‫وتوضيح كيفية التعامل معها واستخداماتها في البرامج املختلفة‪.‬‬

‫‪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.‬‬

‫‪ 5.1‬ما حتتاج إليه لدراسة الوحدة‬


‫حتتاج لدراسة هذه الوحدة إلى جو هادئ وإلى بعض األوراق وقلم رصاص‪.‬‬
‫كما حتتاج‪ ،‬حلل التمارين والقيام باألنشطة‪ ،‬إلى جهاز حاسوب مزود مبترجم للغة ‪C‬‬
‫‪ .++‬حاول‪ ،‬عزيزي الدارس‪ ،‬حل جميع متارين البرمجة باستخدام احلاسوب واختبار‬
‫برامجك قبل النظر إلى احلل الصحيح في نهاية الوحدة‪.‬‬

‫‪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‬‬ ‫العالمة‬ ‫ ‬

‫ويعرف هذا التركيب في لغة ‪ C ++‬كما يلي‪:‬‬


‫‪struct Student‬‬
‫ ‪{char‬‬ ‫;]‪StName[21‬‬
‫‪int‬‬ ‫;‪StNumber‬‬
‫‪char‬‬ ‫;]‪Department[7‬‬
‫‪float‬‬ ‫;‪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( ‫تدريب‬

،‫ يحتوي على االسم‬،Person ‫ اكتب تركيب ًا لشخص‬،‫عزيزي الدارس‬


‫ وفي الدالة الرئيسة عرف متغير من نوع شخص‬،‫ الوزن‬،‫ الطول‬،‫العمر‬
.‫ للعمر‬35 ‫ للعمر ومتغير آخر من النوع نفسه أسند القيمة‬22 ‫وأسند القيمة‬

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‬‬
‫;}‬

‫يتضح من التعريف أعاله أن الصنف ‪ property‬يتكون من املتغيرات املنتمية التالية‪:‬‬


‫ميثل مساحة العقار‪.‬‬ ‫‪ Area‬‬
‫ميثل قيمة األجرة الشهرية للعقار‪.‬‬ ‫ ‬
‫‪MonthRentVal‬‬
‫ميثل سعر العقار‪.‬‬ ‫‪ Price‬‬
‫ويضم الصنف ‪ property‬الدوال املنتمية ‪ member functions‬التالية‪:‬‬
‫‪1.‬‬ ‫;)‪float increase_rent(float Incr‬‬
‫تستخدم هذه الدالة لزيادة األجرة الشهرية للعقار باملقدار ‪ Incr‬وتعيد هذه الدالة‬
‫قيمة اإليجار بعد الزيادة‪.‬‬
‫‪2.‬‬ ‫;)‪float decrease_rent(float Decr‬‬
‫تستخدم هذه الدالة إلنقاص األجرة الشهرية للعقار باملقدار ‪ Decr‬وتعيد هذه الدالة‬
‫قيمة اإليجار بعد النقصان‪.‬‬
‫‪3.‬‬ ‫;)‪float increase_price(float Incr‬‬
‫تستخدم هذه الدالة لزيادة سعر العقار باملقدار ‪ Incr‬وتعيد قيمة السعر بعد الزيادة‪.‬‬
‫‪4.‬‬ ‫;)‪float decrease_price(float Decr‬‬
‫تستخدم هذه الدالة إلنقاص سعر العقار باملقدار ‪ Decr‬وتعطينا قيمة السعر بعد النقصان‪.‬‬

‫‪34‬‬
‫التراكيب واألصناف‬ ‫الوحدة الثانية‬
‫‪5.‬‬ ‫;)(‪float yearly_rent‬‬
‫حتسب هذه الدالة قيمة األجرة السنوية للعقار‪.‬‬
‫‪6.‬‬ ‫;)(‪float get_price‬‬
‫تسترجع هذه الدالة قيمة املتغير املنتمي ‪.Price‬‬
‫‪7.‬‬ ‫;)(‪float get_area‬‬
‫تسترجع هذه الدالة قيمة املتغير املنتمي‪. Area‬‬
‫‪8.‬‬ ‫;)(‪float get_MonthRent‬‬
‫تسترجع هذه الدالة قيمة املتغير املنتمي ‪.MonthRentVal‬‬

‫‪ 2.3‬إنشاء الكائنات من الصنف‬


‫ال من إنشاء الصنف ليتسنى إنشاء أي من الكائنات‬ ‫لقد وضحنا سابق ًا أنه ال بد أو ً‬
‫املثيلة للصنف‪ ،‬فإن تعريف صنف ‪class‬جديد هو تعريف لنوع جديد للبيانات ‪Data‬‬
‫‪ Type‬مع العمليات املمكن استخدامها مع هذا النوع‪ ،‬ويستخدم هذا النوع اجلديد إلنشاء‬
‫متغيرات تسمى كائنات ‪ objects‬من هذا النوع تكون مثيلة له‪ .‬بالعودة إلى املثال (‪،)1‬‬
‫دعنا ننشئ كائن ًا من الصنف ‪ RNum‬الذي عناصره أعداد كسرية‪:‬‬

‫مثال (‪)3‬‬

‫‪>include <iostream.h #‬‬


‫‪class RNum‬‬
‫‪{public:‬‬
‫ ‪void‬‬ ‫;)‪assign(int, int‬‬
‫ ‪double‬‬ ‫;)(‪convert‬‬
‫ ‪void‬‬ ‫;)(‪invert‬‬
‫ ‪void‬‬ ‫;)(‪print‬‬
‫‪private:‬‬
‫‪int‬‬ ‫;‪a,b‬‬
‫;}‬
‫)(‪main‬‬
‫;‪{RNum r‬‬
‫;)‪r.assign(36,5‬‬

‫‪35‬‬
‫التراكيب واألصناف‬ ‫الوحدة الثانية‬
‫;“ =‪cout<<”r‬‬
‫;)(‪r.print‬‬
‫;‪cout<<” = “<<r.convert()<<endl‬‬
‫;)(‪r.invert‬‬
‫;“ = ‪cout<<”1/r‬‬
‫;)(‪r.print‬‬
‫;‪cout<<endl‬‬
‫;‪int p‬‬
‫}‬

‫)‪void RNum::assign(int num, int denum‬‬


‫;‪{ a=num‬‬
‫;‪b=denum‬‬
‫}‬
‫)(‪double RNum::convert‬‬
‫;‪{ return double(a)/b‬‬
‫}‬
‫)(‪void RNum::invert‬‬
‫‪{ int‬‬ ‫;‪t=a‬‬
‫;‪ a=b‬‬
‫;‪ b=t‬‬
‫}‬
‫)(‪void RNum::print‬‬
‫;‪{ cout<<a<<’/’<<b‬‬
‫}‬

‫فقد مت إنشاء الكائن ‪ r‬من الصنف ‪ ،RNum‬وأصبح واضح ًا أن الكائن ‪ r‬أعلن عنه مثل‬
‫املتغير العادي ولكن من نوع ‪ ،RNum‬وهذا يسمى نوع معرف من طرف املستخدم‪ .‬فمن‬
‫الواضح أن الكائن ‪ r‬له القدرة على نداء الدوال املنتمية للصنف ‪ RNum‬األربع‪،)(assign‬‬
‫‪ .)(print ، )(invert ، )(convert‬فمثالً‪ ،‬الدالة املنتمية ‪ ،)(print‬مت نداؤها عبر إحلاق‬
‫اسمها باسم مالكها بعد فصلهما بنقطة ‪ ، )(r.print‬وعليه‪ ،‬فإن الدالة املنتمية يتوجب‬
‫نداؤها بهذا الشكل‪.‬‬

‫‪36‬‬
‫التراكيب واألصناف‬ ‫الوحدة الثانية‬
‫ ‬
‫مثال (‪)4‬‬

‫إلنشاء الكائن ‪ p‬من الصنف ‪ property‬الوارد في املثال (‪ ،)2‬نستخدم اجلملة‬


‫‪;property p‬‬
‫فيما تنشئ اجلملة‬
‫‪;property p1,p2‬‬

‫كائنني‪ p1 :‬و ‪ p2‬من الصنف ‪ property‬حيث يحتوي كل من هذه الكائنات‬


‫على حزمة من املتغيرات والدوال املنتمية التي عرفت داخل الصنف ‪. property‬‬

‫وميكننا استخدام أي من هذه الكائنات عبر الدوال املنتمية (بعد كتابتها بالطبع)‬
‫باستدعائها بالطريقة املناسبة‪ ،‬وذلك بكتابة اسم الكائن ثم نقطة (‪ )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‬دالة لزيادة سعر الكتاب‪.‬‬

‫‪ 2.1.4‬البناؤون والهدامون ‪Constructors and Destructors‬‬


‫عزيزي الدارس‪ ،‬لعلك أدركت أن آلية التعامل مع حقول الصنف سواء املتغيرات أو‬
‫الدوال تعتمد على نوع محدد الوصول للحقل‪ ،‬والسؤال‪ :‬كيف يتم استخدام املتغيرات‬
‫املنتمية مبحدد وصول من النوع اخلاص‪ ،‬وبشكل أدق‪ ،‬كيف حتدد القيم االبتدائية لهذه‬
‫املتغيرات املنتمية ؟ قد يخطر بالبال أننا نستطيع إجراء ذلك من خالل جملة إسناد‪ ،‬نعم‪،‬‬
‫هذا صحيح‪ ،‬ولكن يبقى السؤال األهم عن موقع هذه اجلملة‪ ،‬فماذا لو وضعت جملة‬
‫اإلسناد داخل دالة الـ )(‪ ،main‬عندها جند أن املتغير املنتمي واملعرف في القسم اخلاص‬
‫‪ private‬ال ميكن معاجلته والتعامل معه إال من خالل الدوال املنتمية للصنف‪ .‬ومبا أن‬
‫الدالة ()‪ main‬ليست منتمية للصنف فإنها ال متلك صالحية تغيير هذا املتغير أو معاجلته‪.‬‬
‫‪43‬‬
‫التراكيب واألصناف‬ ‫الوحدة الثانية‬
‫إن احلل لهذه القضية في لغة ‪ C ++‬يكمن في استخدام ما يعرف بدالة الباني (الب ّناء)‪-‬‬
‫بفتح الباء‪ ،‬حيث إن الب ّناء هو دالة منتمية للصنف تأخذ اسم ًا هو اسم الصنف نفسه الذي‬
‫يتبع له‪ ،‬وتعلن بدون قيمة معادة‪ ،‬وتستدعى تلقائي ًا عند إنشاء كائن من صنفها‪.‬‬
‫للتوضيح‪ ،‬وبالعودة إلى املثال (‪ ،)2‬كيف ميكننا أن نحدد القيم االبتدائية للمتغيرات‬
‫املنتمية للصنف ‪property‬؟ فإلعطاء ‪ MonthRentVal‬اخلاص بالكائن ‪ p1‬القيمة‬
‫‪ ،200.0‬على سبيل املثال‪ ،‬نكتب داخل دالة الـ ‪ ،)(main‬اجلملة‪:‬‬
‫;‪p1.MonthRentVal=200.0‬‬
‫لكن املتغير املنتمي ‪ - MonthRentVal‬كما أسلفنا ‪ -‬معرف في القسم اخلاص ‪private‬‬
‫للصنف ‪ . property‬وعليه‪ ،‬فإننا ال نستطيع معاجلته والتعامل معه إال عبر الدوال املنتمية‪.‬‬
‫ومبا أن الدالة ()‪ main‬ليست منتمية للصنف ‪ property‬فإنها ال متلك صالحية تغيير هذا‬
‫املتغير أو معاجلته‪ .‬تكون اجلملة أعاله (;‪ )p1.MonthRentVal=200.0‬صحيحة لو كانت‬
‫املتغيرات املنتمية إلى ‪ property‬معرفة في اجلزء العام ‪ public‬للصنف ‪. property‬‬
‫وكما أسلفنا‪ ،‬فإن لغة ‪ C ++‬متكننا من إعطاء املتغيرات املنتمية قيم ًا ابتدائية عبر استخدام‬
‫خاص من الدوال تدعى الدوال الب ّناءة أو البناؤون‪ .‬ولتعريف “ب ّناء”‪ Constructor‬خاص‬ ‫ٍ‬ ‫نو ٍع‬
‫منوذج للب ّناء داخل التعريف كما‬
‫ٍ‬ ‫بالصنف ‪ property‬أعاله يجب إعادة تعريف الصنف لوضع‬
‫يلي‪:‬‬
‫{‪class property‬‬
‫‪private:‬‬
‫ ‬ ‫ ‪float‬‬ ‫;‪Area‬‬
‫ ‬ ‫‪float‬‬ ‫;‪MonthRentVal‬‬
‫ ‬ ‫‪float‬‬ ‫;‪Price‬‬
‫‪public:‬‬
‫ ‬ ‫;)‪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‬‬
‫;} ‬

‫‪44‬‬
‫التراكيب واألصناف‬ ‫الوحدة الثانية‬
‫منوذج ‪ prototype‬جديد في التعريف‬
‫ٍ‬ ‫ال بد أنك الحظت‪ ،‬عزيزي الدارس‪ ،‬وجود‬
‫اجلديد للصنف ‪ property‬وهو‬
‫;)‪property(float sz, float Rent, float prc‬‬

‫إن هذا النموذج خاص بالب َّّناء اخلاص بإعطاء الكائنات من صنف ‪ property‬قيم ًا‬
‫ابتدائية‪ .‬الحظ‪ ،‬عزيزي الدارس‪ ،‬أن هذه الدالة الب َّناءة تختلف عن بقية الدوال من‬
‫حيث‪:‬‬
‫‪ .1‬أن اسم الدالة الب َّناءة هو اسم الصنف نفسه أي ‪ property‬في مثالنا‪.‬‬
‫تعرف نوع ًا للقيمة املرجعة ‪ return type‬وذلك ألن الب َّناء ال‬
‫‪ .2‬وأن الدالة الب َّناءة ال ّ‬
‫نعرف نوع ًا للقيمة املرجعة للدالة الب ّناءة حتى لو‬
‫يرجع قيمة‪ .‬وإنه من اخلطأ أن ّ‬
‫كان ‪.void‬‬

‫وميكننا كتابة الب ّناء اخلاص بالصنف ‪ property‬كما يلي ‪:‬‬


‫)‪property::property(float sz, float Rent, float prc‬‬
‫;‪ {Area=sz‬‬
‫;‪ MonthRentVal=Rent‬‬
‫;‪ Price=prc‬‬
‫} ‬

‫إن ما يقوم به هذا الب ّناء هو إعطاء املتغيرات قيم ًا ابتدائية مناسبة حسب قيم العوامل‪.‬‬
‫لقد سبق ذكرنا‪ ،‬عزيزي الدارس‪ ،‬أن الب ّناء يستدعى تلقائي ًا عند إنشاء كائن من‬
‫الصنف املطلوب وعليه‪ ،‬يجب تزويده بالعوامل الضرورية‪ ،‬ولذا فإنه من اخلطأ إنشاء‬
‫الكائن ‪ 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‬‬
‫} ‬

‫الذي يعطي القيمة ‪ 0‬لكل من املتغيرات املنتمية‪.‬‬


‫ويقابل مفهوم الب ّنائني مفهوم آخر هو مفهوم الهدامني ‪ Destructors‬الذي يستخدم‬
‫عند انتهاء احلاجة إلى الكائن املنشأ‪ .‬ويعتمد عمل الهدام على طبيعة الكائن؛ فإذا كان‬
‫ال هندسي ًا مرسوم ًا على الشاشة فقد يقوم مبسح هذه‬‫الكائن ميثل نافذة (‪ )window‬أو شك ً‬
‫النافذة أو الشكل الهندسي‪ .‬وإذا كان يستخدم ذاكرة محجوزة ديناميكيا بوساطة إحدى‬
‫دوال احلجز مثل ‪ calloc‬و ‪ ،new‬فقد يقوم بتحرير هذه الذاكرة بحيث تتاح الفرصة‬
‫الستخدامها ألغراض أخرى‪ .‬وقد يقتصر عمل الهدام على طباعة رسالة قصيرة تشير‬
‫إلى انتهاء حياة الكائن حسب ما يراه املبرمج‪.‬‬
‫وأيضا ال يرجع الهدامون قيمة ومن ثم‪ ،‬ال نعرف نوع ًا لهذه القيمة‪ .‬ويحمل الهدام‬
‫اسم الصنف نفسه الذي ينتمي إليه مسبوق ًا بالرمز ~‪ .‬والدالة التالية تعطي مثا ً‬
‫ال على‬
‫هدام بسيط (ال يفعل شيئ ًا في احلقيقة)‪.‬‬
‫ّ‬
‫)(‪property::~property‬‬
‫{ ‬
‫} ‬
‫وكأي دالة أخرى‪ ،‬يجب وضع منوذج للهدام داخل تعريف الصنف لإلشارة إلى أنها‬
‫دالة منتمية‪ .‬ويستدعى الهدام‪ ،‬كأي دالة منتمية أخرى‪ ،‬بكتابة اسم الكائن‪ ،‬ثم نقطة‬
‫متبوعة باسم الهدام (أي اسم الصنف مسبوقا بـ ~) كما في اجلملة‪:‬‬
‫;)(‪p1.~property‬‬

‫‪46‬‬
‫التراكيب واألصناف‬ ‫الوحدة الثانية‬
‫ ‬
‫تدريب (‪)8‬‬
‫عزيزي الدارس‪ ،‬في املثال (‪ )3‬مت اإلعالن عن الصنف ‪ RNum‬الذي عناصره‬
‫أعداد كسرية وميتلك الدالة املنتمية )(‪ ،assign‬املطلوب كتابة برنامج يستخدم‬
‫الصنف نفسه مع إضافة بناء لهذا الصنف بد ً‬
‫ال من هذه الدالة ‪.)(assign‬‬

‫‪ 3.1.4‬أنواع الدوال املنتمية‬


‫تعلم‪ ،‬عزيزي الدارس‪ ،‬فوائد استخدام الدوال ومميزاته‪ ،‬وتقسيم البرامج الكبيرة إلى‬
‫دوال أصغر حجم ًا يستدعي بعضها بعضا مما يصغر حجم البرنامج ويختصر الوقت الالزم‬
‫لتطويره وصيانته‪ .‬ولكن هذه الفوائد واملميزات لها ثمن يدفع على شكل زيادة في الوقت‬
‫والذاكرة الالزمني لتنفيذ البرنامج؛ ذلك ألن احلاسوب يقوم‪ ،‬قبل استدعاء الدالة‪ ،‬يقوم‬
‫بعمليات إضافية مثل تخزين قيم املتغيرات احمللية وعنوان اجلملة التي يجب أن يستأنف‬
‫تنفيذ البرنامج من عندها (عند االنتهاء من تنفيذ الدالة)‪ ،‬وهو ما يدعى عنوان العودة‬
‫‪ ،return address‬ثم ال بد عند االنتهاء من تنفيذ الدالة أن تسترجع هذه القيم املخزنة‪.‬‬
‫وقد ال يكون هذا الثمن مبرر ًا حني تكون الدوال صغيرة جد ًا وتستدعى بكثرة‪ ،‬ولذا‬
‫فإن لغة ‪ C ++‬متكن املبرمج من أن يطلب من املترجم ‪ compiler‬أن يتعامل مع هذه الدوال‬
‫الصغيرة بطريقة مختلفة تدعى طريقة ‪( inline expansion‬االنتشار السطري)‪ ،‬حيث تستبدل‬
‫كل جملة استدعاء لهذا النوع من الدوال مبجموعة اجلمل التي تشكل جسم الدالة ‪function‬‬
‫‪ body‬بعد إجراء بعض التعويضات إلعطاء العوامل القيم الصحيحة‪ .‬وبذلك تنتفي احلاجة‬
‫إلى تخزين قيم املتغيرات احمللية وعنوان العودة ثم استرجاعها عند انتهاء تنفيذ الدالة‪.‬‬
‫على سبيل املثال‪ ،‬جملة االستدعاء التالية‪:‬‬
‫)(‪float x=p1.get_price‬‬
‫تستبدل من قبل املترجم باجلملة‪:‬‬ ‫ ‬
‫;‪float x=p1.Price‬‬

‫ولكن لهذه الطريقة أيض ًا ثمن‪ ،‬حيث إنها تزيد حجم البرنامج الناجت عن عملية‬
‫الترجمة وهو ما يدعى بـ البرنامج التنفيذي ‪ .executable program‬ولذا‪ ،‬فإنها قد ال‬
‫تكون مناسبة للدوال كبيرة احلجم التي تستدعي عدد ًا كبير ًا من املرات؛ إذ إن ذلك قد‬
‫يزيد حجم البرنامج التنفيذي بصورة كبيرة‪.‬‬
‫‪47‬‬
‫التراكيب واألصناف‬ ‫الوحدة الثانية‬
‫يطلب املبرمج من املترجم استخدام هذه الطريقة «االنتشار السطري» (‪inline‬‬
‫‪ )expansion‬عن طريق تعريف الدوال الصغيرة على أنها دوال سطرية ‪،inline function‬‬
‫وللمترجم احلق في رفض هذا الطلب والتعامل مع هذه الدوال بالطريقة التقليدية خاصة‬
‫إذا كانت الدالة كبيرة‪.‬‬
‫وجند أن الدوال السطرية هي نوع من أنواع الدوال املنتمية للصنف‪ ،‬وفيما يلي نبني‬
‫كيفية التعامل مع الدوال السطرية‪:‬‬

‫الدوال السطرية ‪Inline Functions‬‬


‫وهنالك طريقتان لتعريف الدوال السطرية‪:‬‬
‫الطريقة األولى‪:‬‬
‫تقضي بأن تعرف الدالة داخل تعريف الصنف‪ .‬فمثال‪ ،‬لتعريف الدالتني _‪increase‬‬
‫‪ rent‬و ‪ get_price‬على أنهما دوال سطرية‪ ،‬نكتب تعريفهما بشكل كامل (وليس فقط‬
‫مناذج لهما) داخل تعريف الصنف كما يلي‪:‬‬
‫{‪class property‬‬
‫‪private:‬‬
‫;‪float Area‬‬
‫;‪float MonthRentVal‬‬
‫;‪float Price‬‬
‫‪public:‬‬
‫;)(‪property‬‬
‫;)‪property(float sz, float Rent, float prc‬‬
‫)‪float increase_rent(float Incr‬‬
‫;‪{MonthRentVal+=Incr‬‬
‫; ‪return MonthRentVal‬‬
‫}‬
‫;)‪float decrease_rent(float Decr‬‬
‫;)‪float increase_price(float Incr‬‬
‫)‪float decrease_price(float Decr‬‬
‫;)(‪float yearly_rent‬‬
‫)(‪float get_price‬‬
‫;‪{return Price‬‬

‫‪48‬‬
‫التراكيب واألصناف‬ ‫الوحدة الثانية‬
‫}‬
‫;)(‪float get_area‬‬
‫;)(‪float get_MonthRent‬‬
‫;}‬

‫الطريقة الثانية‬
‫نستطيع ترك تعريف الدوال خارج تعريف الصنف وفي الوقت نفسه تعريفهما على‬
‫أنهما دوال سطرية‪ ،‬وذلك بوضع كلمة ‪ inline‬كأول كلمة في ترويسة الدالة (أول جملة‬
‫في التعريف)‪ .‬فمثال‪ ،‬لتعريف الدالة ‪ increase_price‬كدالة سطرية‪ ،‬نستطيع كتابتها‬
‫خارج تعريف الصنف كما يلي‪:‬‬
‫)‪inline float increase_rent(float Incr‬‬
‫;‪{ Price+=Incr‬‬
‫ ‬ ‫};‪return Price‬‬

‫الحظ‪ ،‬عزيزي الدارس‪ ،‬أن كلمة ‪ inline‬لم توضع في منوذج ‪increase_rent‬‬


‫داخل تعريف الصنف‪.‬‬

‫‪ 2.4‬إعادة التحميل للدوال ‪Function Overloading‬‬


‫يعرف الدالة في لغة ‪ C ++‬ليس اسمها فقط بل اسمها باإلضافة إلى عدد‬ ‫إن ما ّ‬
‫عواملها ونوعيتها ‪ ،argument type‬مما يعني أننا نستطيع أن نكتب أكثر من دالة حتت‬
‫االسم نفسه‪ ،‬فمثال‪ ،‬نستطيع كتابة الدالة ‪ power‬بطريقتني‪ :‬األولى تأخذ العاملني‪ x :‬و‬
‫‪ n‬وحتسب لنا ‪ x‬مرفوعة للقوة ‪( n‬حيث أن ‪ .)n>=1‬والطريقة الثانية نأخذ عام ً‬
‫ال واحداً‪،‬‬
‫‪ ،x‬ويحسب حاصل تربيعه كما يلي‪:‬‬
‫التعريف األول‪:‬‬
‫)‪int power(int x, int n‬‬
‫;‪{int p=x‬‬
‫)‪for(int i=0;i<n;i++‬‬
‫;‪p*=x‬‬
‫;‪return p‬‬
‫}‬

‫‪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‬‬

‫وكثيرا ما تكتب الدوال البناءة ‪ constructors‬وحتدد لعواملها قيم ًا تلقائية‪ .‬على‬


‫سبيل املثال‪ ،‬ميكننا إعادة تعريف البناء اخلاص بالصنف ‪ property‬بحيث يستخدم‬
‫القيم التلقائية ‪ 200.0‬و ‪ 10000.0‬للعوامل ‪ Rent‬و ‪ prc‬على الترتيب‪ ،‬وذلك بإعادة‬
‫كتابة النموذج اخلاص بـ ‪ property‬كما يلي‪:‬‬
‫;)‪property(float sz, float Rent=200.0, float prc=10000.0‬‬
‫وبالطبع‪ ،‬يبقى تعريف الدالة كما هو بدون تغيير أي‪:‬‬ ‫ ‬
‫)‪property::property(float sz, float Rent, float prc‬‬
‫;‪ {Area=sz‬‬
‫;‪ MonthRentVal=Rent‬‬
‫;‪ Price=prc‬‬
‫}‬
‫الحظ‪ ،‬عزيزي الدارس‪ ،‬أننا لم نعرف قيمة تلقائية للعامل ‪ sz‬وعليه‪ ،‬يجب تزويده‬
‫بقيمة عند استدعاء البناء‪ .‬أما العامالن ‪ Rent‬و ‪ prc‬فيمكننا حذف قيمهما عند استدعاء‬

‫‪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; }
};

void PrintDate(const Date &XDate)


{
cout << XDate.GetMonth() << “/” <<
XDate.GetDay() << “/” <<
XDate.GetYear() << endl;
}

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‬‬
‫بقراءة قيم جلميع متغيراته املنتمية و دالة أخرى لطباعة قيم هذه املتغيرات‪.‬‬

‫إدخال السالسل الرمزية‬


‫تستطيع‪ ،‬عزيزي الدارس‪ ،‬استخدام الكائن ‪ cin‬لقراءة سالسل رمزية ال حتتوي‬
‫على فراغات وذلك ألن ‪ cin‬يتوقف عن قراءة السلسلة عند أول فراغ أو رمز السطر‬
‫اجلديد ‹\‪ .›n‬لذا فإنك ال تستطيع إدخال االسم ‪ Mohammad Ali‬عند قراءة االسم‬
‫بالطريقة التالية‪:‬‬
‫;]‪char name[30‬‬
‫;‪cin>>name‬‬
‫إذ سيقرأ فقط االسم ‪.Mohammad‬‬
‫وحلل هذه املشكلة تزودنا لغة ‪ C++‬بالدالة ‪ getline‬املعرفة للصنف ‪ istream‬وتستخدم‬
‫لقراءة سطر كامل أو جزء منه بجميع محتوياته‪ .‬والصيغة العامة لـ ‪ getline‬هي‪:‬‬
‫)‪cin.getline(str,length,terminating_character‬‬
‫حيث‪:‬‬
‫‪ str‬هي السلسلة الرمزية ‪ string‬التي نود قراءة قيمتها‪.‬‬
‫‪ length‬هو عدد صحيح ميثل العدد األعلى للرموز ‪ characters‬التي نود قراءتها‪.‬‬
‫‪ terminating_character‬هو الرمز الذي نود أن تتوقف القراءة عنده (دون قراءته)‬

‫واملثال التالي يوضح ذلك‪:‬‬


‫;]‪char name[30‬‬
‫;)›‪cin.getline(name,30,›\n‬‬
‫تتوقف عملية القراءة عند قراءة ‪ 29‬رمز ًا ‪character (1- 29) and a terminating‬‬
‫أو عند الضغط على مفتاح ‪ Enter‬الذي يدخل الرمز ‹\‪ ›n‬إلى جدول املدخالت‪.‬‬
‫أما اجلملة‬
‫;)›‪cin.getline(name,20,›X‬‬
‫فستتوقف عن القراءة عند قراءة ‪ 19‬رمز ًا (‪and a terminating character )19-1‬‬
‫أو عند إدخال الرمز ‹‪.›X‬‬

‫‪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‬‬
‫قم‪ ،‬عزيزي الدارس‪ ،‬بكتابة برنامج صغير وتنفيذه لتجربة كل من سالسل‬
‫الهروب السابقة (باستخدام احلاسوب)‪.‬‬

‫أن ‪ cout‬كائن ‪ object‬ميثل شاشة العرض‪ .‬ولكن كائن من أي صنف‬


‫سبق أن ذكرنا ّ‬
‫‪class‬؟ واجلواب هو أن ‪ cout‬كائن من الصنف ‪ .ostream‬وهذا الصنف ‪ class‬معرف‬
‫مسبقا ‪ predefined‬داخل امللف ‪ iostream.h‬والستخدامه ما علينا سوى وضع جملة‬
‫التضمني‪:‬‬
‫>‪#include<iostream.h‬‬
‫في بداية البرنامج‪ .‬وهذا ضروري كي يتضمن برنامجنا تعريف الصنف ‪ostream‬‬
‫والدوال والعمليات اخلاصة به حتى نستطيع استخدام الكائن ‪.cout‬‬

‫‪ .6‬اخلالصة‬
‫تعد األصناف مصنع الكائنات‪ ،‬حيث تستخدم إلنشاء كائنات حتتوي على متغيرات‬ ‫ّ‬
‫ودوال منتمية‪ .‬وتختلف األصناف عن التراكيب بشكل رئيس في أن لها دوال منتمية‪.‬‬
‫تستخدم هذه الدوال املنتمية ملعاجلة األجزاء اخلاصة من الكائنات‪ .‬وال يحق للدوال‬
‫األخرى معاجلة هذه األجزاء وذلك بهدف حتقيق مبدأ إخفاء البيانات‪ .‬وهنالك نوعان‬
‫قيم ابتدائية‬
‫خاصان من الدوال املنتمية يسمى النوع األول «البناؤون»‪ ،‬ويستخدم إلعطاء ً‬
‫للكائنات عند إنشائها‪ .‬أما النوع الثاني من الدوال املنتمية الذي يدعى «الهدامون»‬
‫فيستخدم عند انتهاء احلاجة إلى كائن معني ألغراض مختلفة‪ .‬تتم عملية إدخال البيانات‬
‫بوساطة الكائن ‪ cin‬والعملية >> بينما تتم عملية إخراج النتائج باستخدام الكائن ‪cout‬‬
‫والعملية <<‪.‬‬

‫‪59‬‬
‫التراكيب واألصناف‬ ‫الوحدة الثانية‬
‫ميكن في لغة ‪ C++‬أن نكتب أكثر من دالة حتت االسم نفسه‪ .‬وال يسبب ذلك أي‬
‫لبس ألن عدد العوامل ونوعها‪ ،‬باإلضافة إلى اسم الدالة‪ ،‬يستخدم لتحديد التعريف‬
‫الواجب استخدامه‪ .‬كذلك ميكن تعريف الدوال الصغيرة كدوال سطرية مما ميكن مترجم‬
‫لغة ‪ C++‬من استبدال جمل استدعاء هذه الدوال باجلمل املعرفة لها‪ ،‬مما يوفر في كثير من‬
‫األحيان بعضا من وقت التنفيذ‪ .‬كما ميكن أن نعرف قيم ًا تلقائية لعوامل الدوال تستخدم‬
‫بشكل تلقائي عند استدعاء هذه الدوال دون حتديد العوامل املطلوبة‪.‬‬

‫‪ .7‬حملة عن الوحدة الدراسية الثالثة‬


‫في الوحدة التالية‪ ،‬عزيزي ال���دارس‪ ،‬سنناقش مواضيع مهمة مثل كيفية‬
‫تعريف مصفوفات من الكائنات والتعامل معها وكيفية إنشاء قائمة متصلة من‬
‫الكائنات‪.‬‬

‫‪ .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);


int myFunc(int i, int j);
float myFunc(int a, int b, float c);
void main(void)
{ cout<<myFunc(10)<<endl;
cout<<myFunc(10,10)<<endl;
cout<<myFunc(10,4,10.8)<<endl;
int p;
cin>>p;
}

int myFunc(int i)
{ return i; }

int myFunc(int i, int j)


{ return i+j; }

float myFunc(int i, int j, float k)


{ return i*(j+k); }

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;
}

Book ‫ يجب إضافة النموذجني التاليني إلى تعريف الصنف‬،‫بالطبع‬


Book();
void print_book();

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‬‬

‫‪94‬‬ ‫‪ .4‬احتواء مصفوفة من الكائنات في صنف‬


‫‪106‬‬ ‫‪ .5‬املؤشرات واألصناف ‪Pointers and Classes‬‬
‫‪109‬‬ ‫‪ .6‬حجز الذاكرة بطريقة ديناميكية ‪Dynamic Memory Allocation‬‬
‫‪124‬‬ ‫‪ .7‬القوائم املتصلة ‪Linked Lists‬‬

‫‪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.‬‬

‫‪ 5.1‬ما حتتاج إليه لدراسة الوحدة‬


‫حتتاج لدراسة هذه الوحدة إلى جو هادئ وإلى بعض األوراق وقلم رصاص‪ .‬كما‬
‫حتتاج حلل التمارين والقيام باألنشطة إلى جهاز حاسوب مزود مبترجم للغة ‪.C++‬‬

‫‪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‬ميثل اسم الطالب (مصفوفة رمزية)‪.‬‬

‫ويضم تعريف الصنف ‪ student‬الدوال املنتمية التالية‪:‬‬


‫‪ ،initialize() .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‬‬
‫}‬
‫‪75‬‬
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬
‫‪ ،get_stno() .2‬هي دالة سطرية ‪ inline function‬تستخدم السترجاع رقم طالب‬
‫معني‪.‬‬
‫‪ ،get_stname() .3‬هي أيض ًا دالة سطرية تستخدم السترجاع اسم طالب (كائن)‬
‫معني‪ .‬الحظ‪ ،‬عزيزي الدارس‪ ،‬أن نوع القيمة املرجعة ‪ return type‬لهذه الدالة‬
‫هو *‪ char‬وذلك ألن اسم الطالب هو مصفوفة رمزية ولذا فإننا نسترجع عنوان أول‬
‫عنصر (حرف) بها‪.‬‬
‫وعلى افتراض أننا نود متثيل شعبة من ‪ 50‬طالباً‪ ،‬فإننا نستخدم اجلملة التالية‪:‬‬
‫;]‪student st[50‬‬
‫حيث تعرف هذه اجلملة املصفوفة ‪ st‬من ‪ 50‬طالب ًاَ‪ .‬وكالعادة‪ ،‬فإن ]‪ st[0‬ميثل أول‬
‫طالب في املصفوفة وأن ]‪ st[49‬ميثل الطالب األخير (رقم ‪ )50‬في املصفوفة‪.‬‬
‫عند تعريف مصفوفة من الكائنات ‪ objects‬فإن البناء ‪ constructor‬اخلاص بصنف‬
‫هذه الكائنات‪ ،‬إن وجد‪ ،‬يستدعى إلعطاء كل كائن قيمة ابتدائية‪ .‬وعليه‪ ،‬فإن اجلملة‬
‫;]‪Student st[50‬‬
‫صحيحة فقط إذا‪:‬‬
‫‪ .1‬لم يكن هنالك بناء معرف للصنف ‪ student‬كما هو احلال في مثالنا‪.‬‬
‫‪ .2‬كان هنالك بناء ليس له عوامل أو أن هنالك قيم ًا تلقائية ‪ default values‬معرفة‬
‫لهذه العوامل‪.‬‬
‫‪ .3‬أما في حالة وجود بناء يحتاج إلى عوامل فإن اجلملة السابقة تصبح خاطئة؛ إذ ال‬
‫بد من تزويد هذا البناء بالعوامل التي يحتاجها إلعطاء هذه الكائنات قيم ًا ابتدائية‪.‬‬
‫إن جملة مثل‬
‫;]‪student st[3‬‬
‫ما هي في حقيقة األمر إال اختصار للجملة‬
‫})(‪student st[3]={student(),student(),student‬‬
‫التي تعني أن العناصر‪ ]st[0 :‬و ‪ ]st[1‬و ‪ ]st[2‬تعطى قيم ًا ابتدائية باستدعاء البناء‬
‫)(‪( student‬دون عوامل) لكل منها‪.‬‬
‫أما في حالة وجود البناء‬
‫)‪student::student(long s‬‬
‫};‪{stno=s‬‬
‫الذي يأخذ عام ً‬
‫ال واحد ًا يستخدمه إلعطاء املتغير املنتمي ‪ stno‬قيمة ابتدائية (أما بقية‬

‫‪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‬لطالب معني‪.‬‬

‫وهكذا يصبح تعريف الصنف ‪ student‬بعد هذه اإلضافات كما يلي‪:‬‬


‫{‪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‬‬
‫;}‬

‫وتعرف الدالة املنتمية )(‪ average‬كما يلي‪:‬‬


‫)(‪double student::average‬‬
‫;‪{ double sum=0.0‬‬
‫)‪for(int i=0;i<csno;i++‬‬
‫;]‪sum=sum+grades[i‬‬
‫;‪return sum/csno‬‬
‫}‬

‫وتستخدم هذه الدالة حلساب معدل طالب معني فمثالً‪ ،‬اجلملة التالية تستدعي)(‪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‬‬

‫‪ 1.3‬ترتيب مصفوفة من الكائنات‬


‫على سبيل أمثلة أخرى على الدوال التي تستخدم مصفوفة من الكائنات‪ ،‬سندرس كيف‬
‫ميكن ترتيب (فرز) مصفوفة من ‪ n‬من الطلبة ترتيب ًا تصاعدي ًا حسب رقم الطالب ‪ stno‬ثم كيف‬
‫ميكن البحث عن طالب معني إذا أعطينا رقمه في قائمة من الطلبة‪.‬‬

‫‪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;
}

‫ نرى أنه مت تعريف‬،‫ عزيزي الدارس‬،‫ في البرنامج‬main() ‫وبدراسة الدالة الرئيسة‬


:‫ كما في اجلملة التالية‬50 ‫مصفوفة من الدارسني عددهم‬
student stu[50];
:‫ كما يلي‬sort ‫ومت استدعاء الدالة‬
sort(stu,n);

)2( ‫نشاط‬
‫ بنفسك (على الورق) اخلوارزمية أعاله لترتيب قائمة‬،‫ عزيزي الدارس‬،‫تتبع‬
:‫من الطلبة على افتراض أن أرقامهم هي‬
.5 ، 15 ، 1 ، 7 ، 10

87
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬
‫ ‬ ‫تدريب (‪)3‬‬
‫اكتب‪ ،‬عزيزي الدارس‪ ،‬دالة لترتيب مصفوفة من الطلبة حسب الترتيب األبجدي‬
‫ألسماء الطلبة‪ .‬الحظ‪ ،‬أنك تستطيع استخدام الدالة ‪ strcmp‬لتقارن بني السالسل‬
‫ضمن برنامجك اجلملة >‪.#include<string.h‬‬ ‫الرمزية على أن ُت ّ‬

‫ ‬
‫تدريب (‪)4‬‬
‫عدل البرنامج أعاله بحيث يقوم بترتيب املصفوفة بشكل تنازلي بدال من تصاعدي‪.‬‬

‫‪ 2.3‬البحث الثنائي ‪Binary Search‬‬


‫سنناقش اآلن‪ ،‬عزيزي الدارس‪ ،‬خوارزمية بحث شهيرة تدعى خوارزمية البحث‬
‫الثنائي‪Binary search .‬تستخدم هذه اخلوارزمية للبحث عن عنصر معني في مصفوفة مرتبة‬
‫ترتيب ًا تصاعدي ًا (أو تنازلياً) إذا عرفنا بعض البيانات اخلاصة بهذا العنصر‪ .‬فمثالً‪ ،‬للبحث‬
‫عن طالب معني في مصفوفة مرتبة ترتيب ًا تصاعدي ًا حسب رقم الطالب‪ ،‬يلزمنا معرفة رقم‬
‫ذلك الطالب‪ .‬وتكون نتيجة البحث حتديد موقع الطالب في املصفوفة إن وجد وإن لم جتد‬
‫اخلوارزمية ذلك الطالب تعيد قيمة خاصة لإلشارة إلى ذلك كالقيمة ‪ -1‬على سبيل املثال‪.‬‬
‫تعتمد هذه اخلوارزمية على كون املصفوفة مرتبة تصاعدي ًا للبحث عن الطالب‬
‫املطلوب بطريقة ال تتطلب إجراء العديد من املقارنات‪ ،‬وذلك لتقليل الزمن الالزم لعملية‬
‫البحث‪ .‬تقوم اخلوارزمية مبقارنة رقم الطالب املراد البحث عنه برقم الطالب الواقع في‬
‫منتصف املصفوفة‪ ،‬وهنالك ثالث نتائج محتملة لنتيجة املقارنة‪:‬‬
‫‪ .1‬أن يكون الرقمان متساويني‪ :‬في هذه احلالة نكون قد وجدنا ضالتنا وبالطبع‪ ،‬ترجع‬
‫اخلوارزمية موقع العنصر الواقع في منتصف املصفوفة وتتوقف‪.‬‬
‫‪ .2‬أن يكون رقم الطالب الذي نبحث عنه أكبر من رقم الطالب املوجود في منتصف‬
‫املصفوفة‪ :‬في هذه احلالة نحتاج فقط للبحث في النصف الثاني من املصفوفة‪ ،‬وذلك‬
‫لكون املصفوفة مرتبة تصاعدياً؛ إذ ال ميكن أن جند الطالب املطلوب في النصف األول‬
‫من املصفوفة‪.‬‬
‫‪ .3‬أن يكون رقم الطالب الذي نبحث عنه أقل من رقم الطالب املوجود في منتصف‬
‫املصفوفة‪ :‬في هذه احلالة نحتاج فقط للبحث في النصف األول من املصفوفة‪.‬‬
‫‪88‬‬
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬
‫وتستمر عملية البحث في النصف األول أو النصف الثاني من املصفوفة بطريقة‬
‫مشابهة‪ .‬إذ نقارن رقم الطالب الذي نبحث عنه برقم الطالب في منتصف اجلزء األول‬
‫(أو اجلزء الثاني) لتحديد الربع الذي يقع الطالب الذي نبحث عنه فيه‪ .‬وهكذا تستمر‬
‫اخلوارزمية في البحث حتى جند الطالب أو أن نستنتج أن الطالب ال ميكن أن يكون‬
‫موجود ًا في املصفوفة‪.‬‬
‫والدالة ‪ bsearch‬تطبق هذه اخلوارزمية‬
‫‪// 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‬‬
‫}‬
‫تستخدم هذه اخلوارزمية املتغير ‪ lo‬لإلشارة إلى رقم أول عنصر في اجلزء الذي نبحث‬
‫به‪ ،‬واملتغير ‪ hi‬لإلشارة إلى رقم آخر عنصر في اجلزء الذي نبحث به‪ .‬وألن اخلوارزمية تبدأ‬
‫البحث في املصفوفة كاملة فإن القيمة االبتدائية لـ ‪ lo‬صفر و قيمة ‪ hi‬االبتدائية هي ‪n-1‬؛‬
‫حيث ‪ n‬عدد العناصر في املصفوفة‪ .‬وتستخدم هذه اخلوارزمية املتغير ‪ mid‬لإلشارة إلى‬
‫رقم العنصر الواقع في منتصف اجلزء الذي نبحث به ولهذا فإن قيمته ‪ .)lo+hi)/2‬الحظ‪،‬‬
‫عزيزي الدارس‪ ،‬كيف تتغير حدود اجلزء الذي نبحث به اعتماد ًا على نتيجة مقارنة رقم‬
‫الطالب الذي نبحث عنه مع رقم الطالب الواقع في منتصف اجلزء الذي نبحث به (أي رقم‬
‫الطالب [ ‪ .)st [mid‬فإذا كان الرقم الذي نبحث عنه هو األكبر فإن قيمة ‪ lo‬تصبح ‪mid+1‬‬

‫‪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‬‬
‫;}‬

‫يحتوي الصنف ‪ section‬على متغيرين منتميني‪:‬‬


‫‪ ،sec .1‬هي مصفوفة من الكائنات من نوع ‪ student‬تتسع لـ ‪ 50‬كائن ًا على األكثر‪.‬‬
‫‪ ،size .2‬ميثل احلجم احلقيقي للمصفوفة‪ ،‬أي عدد الطلبة الفعلي في املصفوفة‪.‬‬

‫كما يحتوي الصنف على ‪ 6‬دوال ‪:‬‬


‫‪ ،section() .1‬هذا هو البناء اخلاص بالصنف ‪ ،section‬وكل ما يفعله هو جعل قيمة‬
‫املتغير ‪ size‬صفر ًا وذلك‪ ،‬ألن املصفوفة فارغة في البداية‪ .‬وهذه الدالة هي دالة‬
‫سطرية ألنها معرفة داخل الصنف نفسه‪.‬‬
‫‪ ،bsearch(long stno) .2‬تستخدم هذه الدالة خوارزمية البحث الثنائي للبحث عن‬
‫طالب معني ذي رقم معطى‪ .‬الحظ‪ ،‬عزيزي الدارس‪ ،‬أن هذه الدالة معرفة في‬

‫‪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‬إلى القيمة السابقة‪.‬‬

‫الشكل (‪ :)1‬عملية اإلزاحة الواجب عملها إلضافة الرقم ‪ 6‬إلى املصفوفة‬

‫)‪void section::StAdd(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‬‬
‫‪// insert element‬‬
‫;‪sec[pos]=s‬‬
‫‪// modify size‬‬
‫;‪size++‬‬
‫}‬

‫‪ ،StDelete(long stno) .4‬تقوم هذه الدالة بشطب طالب معني ذي الرقم ‪ stno‬املعطى‬
‫باعتباره عام ً‬
‫ال للدالة‪ .‬تستخدم هذه الدالة ‪ bsearch‬لتحديد موقع العنصر املراد‬
‫ال الطالب‬‫شطبه‪ .‬وقبل عملية الشطب الفعلية نتأكد من أن‪ bsearch‬قد وجد فع ً‬
‫املراد حذفه (تذكر بأن ‪ bsearch‬يرجع القيمة ‪ 1-‬إذا لم يجد العنصر املطلوب)‪.‬‬
‫وتتم عملية احلذف بإزاحة العناصر الواقعة في املصفوفة بعد العنصر املراد حذفه‬
‫‪96‬‬
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬
‫موقع ًا واحد ًا إلى اليسار والشكل ‪ 2‬يوضح هذه العملية‪ .‬وتعدل قيمة ‪ size‬بطرح‬
‫‪ .1‬تعيد هذه الدالة قيمة صحيحة أكبر من ‪ 1-‬لإلشارة إلى أن عملية الشطب متت‬
‫بنجاح‪ .‬أما إذا لم تتم عملية الشطب بنجاح (ألن الطالب لم يكن موجود ًا أصالً)‬
‫فإن القيمة اخلالصة ‪ 1-‬تعاد إلى الدالة املستدعية‪.‬‬

‫الشكل (‪ :)2‬عملية اإلزاحة الالزمة حلذف العنصر ‪ 5‬من املصفوفة‬


‫ ‬
‫)‪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‬‬
‫}‬

‫‪ 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”;
}

‫ ثم يقوم‬،cs100 )‫ لتعريف الكائن (الشعبة‬section ‫والبرنامج التالي يستخدم الصنف‬


‫ ويقوم البرنامج باستدعاء‬،‫بعرض عدة خيارات للمستخدم مثل إضافة طالب أو شطب طالب‬
.switch ‫ ويتم حتديد الدالة املطلوب تنفيذها من خالل جملة‬.‫الدالة املنتمية املناسبة لتنفيذ األمر‬
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 «;
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);
98
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬
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);
}

‫ في برنامج‬section‫ والصنف‬student ‫ عزيزي الدارس جمع األصناف السابقة‬،‫وميكنك‬


:‫واحد كما يلي‬
#include<string.h>
#include<iostream.h>
#include<stdio.h>
class student{
long stno;
int csno;
double grades[100];
char StName[20];

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;
}

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;
100
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬
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;
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‬‬

‫وبالطريقة نفسها‪ ،‬نستطيع التعامل مع الكائنات ‪ objects‬عبر املؤشرات‪ .‬فمثالً‪ ،‬نستطيع‬


‫تعريف الكائن ‪ St1‬على أنه من الصنف ‪ student‬والذي سبق تعريفه كما يلي‬
‫;‪student St1‬‬
‫ثم نستطيع تخزين عنوان املتغير ‪ St1‬في املتغير املؤشر ‪ p1‬كما يلي‬
‫;‪student *p1=&St1‬‬

‫الحظ أن هذه اجلملة تعرف املتغير ‪ 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‬‬

‫للصنف ‪ student‬فإننا نستخدم الدالة ‪ new‬كما يلي‬


‫;)‪q=new student(96100,”Ahmad Ali”,10‬‬

‫مما يؤدي إلى إعطاء املتغيرات املنتمية للكائن احملجوز ‪ 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;
}

‫ إدخال املعلومات واحلصول على‬،‫ عزيزي الدارس‬,‫وبعد تنفيذ البرنامج ميكنك‬


:‫النتائج كما هو موضح أدناه‬
112
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬

)8( ‫تدريب‬
:‫ادرس البرنامج التالي جيد ًا وبني نتيجة التنفيذ‬
#include <iostream>

class CSquare
{
public:
double Side;

CSquare() { Side=0.00;}
CSquare(double side) {Side=side;}
~CSquare() { }

double getSide() const { return Side; }


void setSide(const double s)
{
if( s <= 0 )
Side = 0.00;
else
Side = s;
}
double Perimeter() { return Side * 4; }
double Area() { return Side * Side; }
};

int main()

113
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬
{
CSquare *sqr[4];

sqr[0] = new CSquare;


sqr[0]->setSide(24.55);
sqr[1] = new CSquare;
sqr[1]->setSide(15.08);
sqr[2] = new CSquare;
sqr[2]->setSide(8.212);
sqr[3] = new CSquare;
sqr[3]->setSide(202.24);

cout << «Squares Characteristics» << endl;


cout << «Square 1» << endl;
cout << «Side: « << sqr[0]->getSide() << endl;
cout << «Perimeter: « << sqr[0]->Perimeter() << endl;
cout << «Area: « << sqr[0]->Area() << endl;

cout << «Square 2» << endl;


cout << «Side: « << sqr[1]->getSide() << endl;
cout << «Perimeter: « << sqr[1]->Perimeter() << endl;
cout << «Area: « << sqr[1]->Area() << endl;

cout << «Square 3» << endl;


cout << «Side: « << sqr[2]->getSide() << endl;
cout << «Perimeter: « << sqr[2]->Perimeter() << endl;
cout << «Area: « << sqr[2]->Area() << endl;

cout << «Square 4» << endl;


cout << «Side: « << sqr[3]->getSide() << endl;
cout << «Perimeter: « << sqr[3]->Perimeter() << endl;
cout << «Area: « << sqr[3]->Area() << endl;
int n; cin>> n;
return 0;
}

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‬بطريقة ديناميكية‪ ،‬ومن ثم‪ ،‬قراءة عناصر هذه املصفوفة وطباعتها بشكل مناسب‪.‬‬

‫•تعريف الصنف ‪ section‬بطريقة ديناميكية‪ :‬الطريقة األولى‬


‫واآلن لنعد للصنف ‪ section‬ونعيد تعريفه بطريقة ديناميكية‪ ،‬بحيث نتجنب احلاجة‬
‫إلى حجز مصفوفة أكبر بكثير مما نحتاج‪ .‬وتكمن الفكرة في حجز مصفوفة صغيرة عند‬
‫البدء بطريقة ديناميكية ثم توسعتها عند الضرورة فقط؛ أي عندما نحاول إضافة طالب‬
‫جديد وال نتمكن بسبب صغر حجم املصفوفة احملجوزة‪ .‬وستتم عملية التوسعة هذه‬
‫بوساطة الدالة املنتمية اجلديدة )(‪ expand‬كما سنرى‪.‬‬
‫واآلن لندرس التعريف التالي لـ ‪section‬‬
‫{‪class section‬‬
‫;‪student *sec‬‬
‫;‪int MaxSize‬‬
‫;‪int size‬‬
‫;)‪int bsearch(long stno‬‬
‫;)(‪void expand‬‬
‫‪public:‬‬
‫;]‪section(){sec=new student[20‬‬
‫;‪MaxSize=20‬‬
‫};‪size=0‬‬
‫;)‪void StAdd(student s‬‬
‫;)‪int StDelete(long stno‬‬
‫;)(‪void Stlist‬‬
‫;)‪void StRetrieve(long stno‬‬
‫;}‬

‫‪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‬عندما يصبح عدد الطلبة في املصفوفة أقل من نصف احلجم الكلي للمصفوفة‪.‬‬

‫•تعريف الصنف ‪ section‬بطريقة ديناميكية‪ :‬الطريقة الثانية‬


‫سنعرف اآلن الصنف ‪ section‬بطريقة أكثر ديناميكية بحث تخصص الذاكرة بطريقة‬
‫ديناميكية ليس فقط للمصفوفة التي حتوي الكائنات إمنا أيض ًا لكل كائن (طالب) في املصفوفة‪.‬‬
‫في هذا التعريف سنستخدم املصفوفة ليس لتخزين الكائنات نفسها إمنا لتخزين عناوينها في‬
‫الذاكرة‪ .‬وقد تتساءل‪ ،‬عزيزي الدارس‪ ،‬ما احلكمة من عمل ذلك؟‬
‫ال شك في أن مصفوفة من العناوين ستأخذ حجما أقل من مصفوفة من الكائنات من‬
‫النوع ‪ .student‬فقط عندما نضيف طالب ًا جديد ًا سنحجز له الذاكرة املطلوبة بشكل كامل‬
‫وسنخزن عنوان هذا الكائن في املصفوفة‪ .‬وعند حذف هذا الطالب نستطيع حترير الذاكرة‬
‫اخلاصة به‪ .‬كما أن عملية نقل العناوين أو نسخها عند توسعة املصفوفة تتطلب وقت ًا أقل من‬
‫عملية نقل الكائنات نفسها‪.‬‬
‫لندرس التعريف التالي لـ ‪section‬‬
‫{‪class section‬‬
‫;‪student **sec‬‬
‫;‪int MaxSize‬‬
‫;‪int size‬‬
‫;)‪int bsearch(long stno‬‬
‫;)(‪void expand‬‬
‫‪public:‬‬
‫;]‪section(){sec=new student*[20‬‬
‫;‪MaxSize=20‬‬
‫};‪size=0‬‬
‫;)‪void StAdd(student* s‬‬
‫;)‪int StDelete(long stno‬‬
‫;)(‪void Stlist‬‬
‫;)‪void StRetrieve(long stno‬‬
‫;}‬
‫الشك في أنك الحظت بعض االختالفات عن التعريف السابق لـ ‪ section‬وهي‬
‫‪119‬‬
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬
‫‪ .1‬تالحظ أن تعريف املتغير ‪ sec‬أصبح‬
‫;‪student **sec‬‬
‫وذلك ألن ‪ sec‬اآلن متغير يحوي عنوانا ملوقع من النوع ‪ student *sec‬والذي يحتوي‬
‫بدوره على عنوان لكائن من النوع ‪.student‬‬
‫‪ .2‬الحظ أيض ًا أن البناء اخلاص بالصنف ‪ section‬يحجز مصفوفة من النوع *‪student‬‬
‫ألننا سنستخدم هذه املصفوفة لتخزين عناوين لكائنات من الصنف ‪ student‬وليس‬
‫الكائنات نفسها‪.‬‬
‫‪ .3‬وبطريقة مشابهة‪ ،‬نحتاج لتعديل الدالة ‪ expand‬بحيث تصبح‬
‫)(‪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‬‬

‫}‬

‫‪ .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++;
}

‫ بحيث‬StRetrieve ‫ و‬bsearch ‫ و‬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;
}

// 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::StRetrieve(long stno)


121
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬
{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»;
}

‫ بحيث حترر الذاكرة احملجوزة للطالب‬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‬لإلشارة إلى انتهاء القائمة املتصلة‪ .‬ومتثل كل‬
‫��������� )�� ����� ����( ����� ���� ��� ���� ���� �� ���� ����� ������ �����‬

‫العقدة ��� ً‬ ‫����� �� ً‬


‫كائن ًا أو‬
‫������ �������� ��� ����� �� ���� ������ ������ ‪�������� ������� ��� ������ NULL‬‬
‫نوع ‪Book‬‬ ‫أو من‬
‫����‬ ‫���‬ ‫نوع� ������‬
‫‪student‬‬ ‫من�����‬
‫كائنا����‬ ‫متثل��� ���‬
‫فقد� ��‬
‫ما؛�����‬
‫نوع ��‬
‫من �����‬
‫تركيبا����‬ ‫عقدة‬
‫�������‪.‬‬
‫افتراضية‪.�������� ����� .‬‬
‫متصلة ������ �����‬
‫قائمة)‪���� (5‬‬ ‫بالرسم‬
‫������‬ ‫… ���‪.‬‬‫‪Book‬يبني‬
‫��� (‪)5‬‬
‫والشكل‬
‫إلخ‪�� ��.‬‬ ‫…‬
‫‪student‬‬

‫الشكل (‪ :)5‬يظهر في الشكل قائمة متصلة من أربع عقد‪ ،‬حيث حتمل كل عقدة عنوان العقدة‬
‫����� )‪����� ���� �� ���� ��� ���� ���� �� ����� ����� ����� �� ���� :(5‬‬

‫التي تليها‪ .‬وميثل ذلك بالرسم باستخدام السهم‪ .‬بينما حتمل العقدة األخيرة القيمة اخلاصة‬
‫������ ���� �����‪������ ������ ������ ���� ����� .����� �������� ������ ��� ����� .‬‬
‫������ ‪.������ ���� �������� ������ ����� NULL‬‬
‫‪ LLUN‬ومتثل بالرسم باستخدام اخلط املائل‪.‬‬
‫����� ������� ������� ����� ����� ��������� �� ���� �� ������ ���� ����‬
‫ال للمصفوفات في كثير من األحيان وذلك لعدة‬ ‫ال مفض ً‬‫وتشكل القوائم املتصلة بدي ً‬
‫����� ����‪:‬‬
‫منها‪��� ���� �������� ����� ����� ����� � ���� �� (����) ���� �����:‬‬ ‫أسباب �����‬
‫‪�� .1‬‬

‫للبيانات كما هي‬ ‫‪ .1‬إن عملية إضافة عنصر (عقدة) أو شطبه ال تتطلب عملية إزاحة‬
‫����� ��� ������� ��������� )���� ������ ‪������� ��� StDelete � StAdd‬‬

‫احلال عند استخدام املصفوفات (انظر تعريف ‪ StAdd‬و ‪ StDelete‬في األقسام‬


‫�������(‪.‬‬

‫السابقة)‪.‬‬
‫‪����� ����� �������� ������ ������� ������� �� ������� ������� ������ ���� .2‬‬
‫������� ������ ������ ��� ��� ������ ����� ������� ��� ������ ������ ����� ������‬
‫استخدام الذاكرة بطريقة اقتصادية بحيث نحجز‬ ‫املتصلة من‬
‫‪.delete‬‬ ‫��������القوائم‬
‫������ ‪� new‬‬ ‫��������� متكننا‬
‫‪ .2‬كذلك‬
‫الذاكرة اخلاصة بالعقد فقط عند احلاجة إليها ونحررها فور انتهاء احلاجة إليها بطريقة‬
‫‪.delete‬‬
‫������� ��� �������� ���������� ����‪:‬‬ ‫‪ new‬و‬
‫�������‬ ‫الدوال‬
‫�������‬ ‫باستخدام���‬
‫ديناميكية���� �����‬
‫���‬
‫‪�������� �������� ������� �� �� � �� ���� ��� ������ �� :��������� �������� .1‬‬
‫باملصفوفات منها‪:‬‬
‫مقارنتها ����� ����‬
‫��������‪.‬‬‫������عند‬
‫للقوائم���املتصلة‬
‫املساوئ�� ���‬ ‫بعض‬
‫������� ���‬ ‫هنالك�����‬ ‫�����ذلك‪،‬‬
‫����� ��‬ ‫ومع‬
‫القائمة املتصلة‬
‫استعراض�������‬
‫من�� ����‬ ‫������ال بد‬
‫������‬ ‫عنصر ما‬
‫إلى�� ����‬ ‫������للوصول‬
‫�����������‬ ‫التتابعية‪ :‬إذ‬
‫املعاجلة��������‬
‫‪��������� .1‬‬
‫���قارن هذا‬‫املطلوب‪.‬‬
‫العنصر� �����‬ ‫��������� إلى‬
‫��� ����� )‬ ‫إلى أن نصل‬ ‫القائمة‬
‫�������‬ ‫��� بداية‬
‫������ ��‬ ‫عنصر ًا عنصر ًا‬
‫�������� من‬ ‫������ ��‬
‫باملصفوفات؛ إذ إننا نستطيع معاجلة أي عنصر مبعرفة‬ ‫اخلاصة‬
‫����� �������‪.‬‬ ‫املباشرة‬
‫��������‬ ‫باملعاجلة ���‬
‫����� �����(‬

‫ترتيبه في املصفوفة مما ميكننا من استخدام خوارزميات بحث فعالة (ال تتطلب‬
‫‪.������� ������ ��� ��������� ���� �� ���� ������� ������� ��������� ��� ����� .2‬‬

‫وقت تنفيذ كبيراً) مثل خوارزمية‪115‬البحث الثنائي‪.‬‬


‫��������� �������‬ ‫������ �������‬

‫‪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‬‬
‫;}‬

‫الحظ‪ ،‬عزيزي الدارس‪ ،‬وجود حقلني في التركيب ‪:node‬‬


‫‪ ،data .1‬يستخدم لتخزين كائن من نوع ‪.student‬‬
‫‪ ،next .2‬يستخدم لتخزين عنوان العقدة ‪ node‬التالية ولذا فهو من النوع *‪.node‬‬

‫وسنعرف القائمة املتصلة باعتبارها صنف ًا سندعوه ‪ LinkedList‬كما يلي‬


‫{‪class LinkedList‬‬
‫;‪node* head‬‬
‫‪public:‬‬
‫};‪LinkedList(){head=NULL‬‬
‫;)‪void ListAdd(student s‬‬
‫;)‪int ListDelete(long stno‬‬
‫;)(‪void ListTraverse‬‬
‫;}‬

‫حيث يحتوي تعريف هذا الصنف على متغير منتم واحد هو ‪ head‬ويستخدم‬
‫لالحتفاظ بعنوان أول عقدة في القائمة املتصلة‪ ،‬ولذا فهو من النوع*‪ .node‬وهنالك‬
‫أيضا أربع دوال منتمية هي‪:‬‬
‫‪ .1‬البناء ‪ :LinkedList‬كل ما يعمله هو جعل ‪ head‬مساوي ًا لقيمة املؤشر اخلاصة‬
‫‪ NULL‬لإلشارة إلى أن القائمة فارغة في البداية‪.‬‬
‫‪ :ListAdd .2‬تقوم بإضافة طالب جديد إلى القائمة املتصلة‪ ،‬وفي املوقع املناسب‬

‫‪126‬‬
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬
‫بحيث تبقى القائمة مرتبة تصاعدي ًا حسب رقم الطالب‪.‬‬
‫‪ :ListDelete .3‬تقوم بحذف الطالب ذي الرقم ‪ stno‬من القائمة املتصلة‪.‬‬
‫‪ :ListTraverse .4‬تقوم هذه الدالة بزيارة كل عقدة في القائمة على الترتيب وتعرض‬
‫بعض املعلومات عن الطالب املخزن في تلك العقدة‪.‬‬

‫إضافة عنصر إلى قائمة متصلة ‪ListAdd‬‬


‫تقوم الدالة ‪ ListAdd‬بإضافة طالب جديد إلى قائمة متصلة كما يلي‪:‬‬
‫•تقوم بحجز ذاكرة للعقدة اجلديدة باستخدام الدالة ‪ new‬ويحتفظ بعنوان العقدة‬
‫اجلديدة في املتغير ‪ ptr‬وهذا يتم عبر اجلملة‬
‫;‪node* ptr=new node‬‬

‫•تقوم بالتأكد من أن الذاكرة حجزت بطريقة صحيحة؛ إذ في بعض األحيان‪ ،‬ال‬


‫تكفي الذاكرة املتوافرة‪ ،‬عقدة جديدة‪ .‬في هذه احلالة تعيد الدالة ‪ new‬القيمة اخلاصة‬
‫‪ NULL‬لإلشارة إلى أن الذاكرة املطلوبة لم حتجز كما ينبغي‪ .‬وفي هذه احلالة سنجعل‬
‫‪ ListAdd‬يقوم بطباعة رسالة تبنب املشكلة للمستخدم‪.‬‬
‫•في حالة حجز الذاكرة بطريقة صحيحة‪ ،‬تبدأ عملية البحث عن املوقع الصحيح‬
‫إلدخال العقدة اجلديدة وربطها بالقائمة‪ .‬وتستخدم في هذه العملية متغيرين محليني‬
‫هما ‪ prev‬و ‪ .Q‬تستخدم ‪ prev‬لإلشارة إلى العقدة التي تسبق موقع العقدة اجلديدة‬
‫وذلك عند انتهاء عملية البحث‪ .‬وتستخدم ‪ Q‬في عملية مقارنة رقم الطالب اجلديد مع‬
‫رقم الطالب املوجود في كل عقدة حتى نصل إلى عقدة رقم الطالب املخزن بها أكبر من‬
‫رقم الطالب اجلديد‪ ،‬عندها نكون قد وصلنا إلى مكان اإلضافة الصحيح‪ ،‬وتتوقف‬
‫عملية البحث‪ .‬وتتم عملية البحث بوساطة اجلمل‬
‫;‪node* prev=NULL‬‬
‫;‪node* Q=head‬‬
‫))(‪while(Q!=NULL && s.get_stno()>(*Q).data.get_stno‬‬
‫;‪{prev=Q‬‬
‫;‪Q=Q->next‬‬
‫}‬

‫‪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‬‬

‫وفيما يلي نعرض ‪ ListAdd‬بشكل متكامل‬


‫)‪void LinkedList::ListAdd(student s‬‬
‫‪{ // allocate memory‬‬
‫;‪node* ptr=new node‬‬
‫‪// check if the memory was not sufficient‬‬
‫)‪if(ptr==NULL‬‬
‫;”‪cout<<”Error : insufficient memory \n‬‬
‫‪else // sufficient memory‬‬
‫;‪{ ptr->data=s‬‬
‫‪// find insertion position‬‬

‫‪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‬للتأكد من أنه يعمل بطريقة‬
‫صحيحة عند إضافة عقدة في نهاية القائمة املتصلة‪.‬‬

‫حذف عنصر من القائمة املتصلة ‪ListDelete‬‬


‫تقوم الدالة ‪ ListDelete‬بحذف العقدة الذي متثل الطالب صاحب الرقم ‪،stno‬‬
‫حيث ‪ stno‬هو عامل لهذه الدالة يستقبل قيمته الفعلية من الدالة املستدعية (‪ ، main‬على‬
‫سبيل املثال)‪ .‬تقوم الدالة ‪ ListDelete‬بحذف العقدة املطلوبة حسب اخلطوات التالية‪:‬‬
‫‪ .1‬تقوم بالبحث عن العقدة املطلوبة‪ ،‬وتستخدم املتغيرين ‪ prev‬و ‪ p‬من النوع‬
‫*‪ .node‬تستخدم ‪ prev‬كما في حالة ‪ 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‬لإلشارة إلى أن عملية احلذف متت بنجاح‪.‬‬

‫واليك اآلن‪ ،‬عزيزي الدارس‪ ،‬الدالة ‪ ListDelete‬بشكل كامل‬


‫)‪int LinkedList::ListDelete(long stno‬‬
‫;‪{ node* prev=NULL‬‬
‫;‪node* p=head‬‬
‫)‪while(p && p->data.get_stno()!=stno‬‬
‫;‪{ prev=p‬‬
‫;‪p=p->next‬‬
‫}‬
‫;‪if(!p) return 0‬‬
‫‪if(!prev) // i.e. prev=NULL‬‬
‫;‪head=head->next‬‬
‫‪else‬‬
‫;‪prev->next=p->next‬‬
‫;‪delete p‬‬
‫;‪return 1‬‬
‫‪} // listDelete‬‬
‫‪133‬‬
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬
‫ ‬
‫نشاط (‪)10‬‬
‫قم‪ ،‬عزيزي الدارس‪ ،‬بتتبع خطوات عمل ‪ ListDelete‬للتأكد من أنه يعمل بطريقة‬
‫صحيحة عند حذف عقدة في نهاية القائمة املتصلة‪.‬‬

‫ ‬

‫ ‬
‫ ‬

‫الشكل (‪ :)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( ‫تدريب‬
‫ برنامج ًا يستخدم املؤشرات لتعريف قائمة متصلة‬،‫ عزيزي الدارس‬،‫اكتب‬
.‫حتتوي على معلومات عن رقم الدارس كما هي موضحة في الشكل‬

‫ لكتابة برنامج يقوم بإدخال البيانات‬linklist ‫استخدم اإلعالن التالي عن الصنف‬


.‫السابقة وطباعة النتائج مستخدما املؤشرات‬

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
void additem(int d); //add data item (one link)
void display(); //display all links
};

136
‫املصفوفات واألصناف‬ ‫الوحدة الثالثة‬
‫‪ .8‬اخلالصة‬
‫هنالك طريقتان رئيستان لتمثيل قائمة من الكائنات‪:‬‬
‫باستخدام املصفوفات‪ :‬حيث ميثل كل عنصر من املصفوفة كائن ًا من صنف ما‪ .‬وميكن‬ ‫‪.1‬‬
‫احتواء هذه املصفوفة من الكائنات مع الدوال الالزمة ملعاجلتها في صنف واحد‪.‬‬
‫‪ .2‬باستخدام القوائم املتصلة‪ :‬تعرف القائمة املتصلة على أنها مجموعة من العقد‪،‬‬
‫حيث حتمل كل عقدة‪ ،‬باستثناء آخر عقدة‪ ،‬عنوان العقدة التي تليها‪ .‬ومتثل كل‬
‫عقدة إما تركيب ًا وإما كائن ًا من صنف ما‪.‬‬

‫كما أن هنالك طريقتني حلجز الذاكرة‪:‬‬


‫‪ .1‬الطريقة الستاتيكية‪:‬‬
‫هي الطريقة األكثر استخداماً؛ حيث حتجز الذاكرة من جانب املترجم ‪compiler‬‬
‫وتبقى محجوزة أثناء التنفيذ حتى وإن لم تستخدم‪.‬‬
‫‪ .2‬الطريقة الديناميكية‪:‬‬
‫متكننا هذه الطريقة من استخدام الذاكرة بشكل أفضل‪ .‬حيث حتجز الذاكرة أثناء‬
‫ثم‪ ،‬ليس هنالك ضرورة حلجز ذاكرة أكبر مما نحتاج‪ .‬وليس هذا‬ ‫تنفيذ البرنامج‪ ،‬ومن َّ‬
‫فحسب‪ ،‬بل نستطيع حترير الذاكرة عند انتهاء احلاجة لها أثناء تنفيذ البرنامج؛ مما ميكن‬
‫البرنامج من استخدام الذاكرة احملررة ألمور أخرى‪ .‬يتم حجز الذاكرة باستخدام الدالة‬
‫‪ new‬وحتريرها باستخدام الدالة ‪.delete‬‬
‫ميكن حجز املصفوفات إما باستخدام الطريقة الستاتيكية وإما باستخدام الطريقة‬
‫الديناميكية‪ .‬ولكننا نفضل عادة استخدام الطريقة الديناميكية ألن حجزها بالطريقة‬
‫الستاتيكية يتطلب حتديد حجم املصفوفة عند كتابة البرنامج‪ ،‬مما يجعلنا نبالغ‪ ،‬في كثير‬
‫من األحيان‪ ،‬في حجمها حتسب ًا ألسوأ الظروف‪ .‬كذلك تبقى الذاكرة محجوزة طوال‬
‫فترة تنفيذ البرنامج حتى وإن انتهت احلاجة إليها‪ .‬بينما متكننا الطريقة الديناميكية من‬
‫حجز املصفوفة أثناء تنفيذ البرنامج وحتديد حجمها عند التنفيذ‪ .‬وليس هذا فحسب‪،‬‬
‫بل نستطيع زيادة حجمها أو إنقاصه إذا دعت الضرورة‪ .‬ونستطيع أيض ًا حترير الذاكرة‬
‫املخصصة لها عند انتهاء احلاجة لها‪.‬‬

‫‪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

1. Bronson, Gary, “Program Development and Design Using


C++”, PWS Publishing Company, 1997.
2. Cannon, Scott, “Understanding Programming: An Introduc-
tion Using C++”. West Publishing Company, 1997.
3. Davis SR. S., “C++ for Dummies”, 5th edition, Wiley Publisher ,
Inc, 2004.
4. Lafore R., “Object-Oriented Programming in C++”, Fourth
Edition, Sams Publishing , 2002.
5. Savitch, Walter, Problem Solving with C++”, Addison-Wesley
Publishing Company, Inc 1996.
6. Weiss M. A., “Data Structures and Problem Solving Using
C++”, 2ed edition, Pearson Addison Wesley, 2002.

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.‬‬

‫‪ 5.1‬ما حتتاج إليه لدراسة الوحدة‬


‫حتتاج لدراسة هذه الوحدة إلى جو هادئ وإلى بعض األوراق وقلم رصاص‪ .‬كما‬
‫حتتاج حلل التمارين والقيام باألنشطة إلى جهاز حاسوب مزود مبترجم للغة ‪C++ .‬‬

‫‪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‬‬
‫قم‪ ،‬عزيزي الدارس‪ ،‬بتنفيذ املثال أعاله والحظ النتيجة‪.‬‬

‫‪ 2.2‬مترير العوامل باإلشارة‬


‫‪Parameter Passing by Reference‬‬
‫وهنــالك طريقة أخرى لتحقيق النتيجـ ــة نفسها تسمـ ــح لنا باستخدام ع ــدد أقل من‬
‫عمليات * و &‪ .‬تدعى هذه الطريقة بطريقة مترير العوامل باإلشارة ‪parameter passing‬‬
‫‪ .by reference‬ولتوضيح هذه الطريقة سنعيد كتابة الدالة ‪ swap2‬كما يلي ‪:‬‬
‫)‪void swap3(int &X, int &Y‬‬
‫;‪{ int T=X‬‬
‫;‪X=Y‬‬
‫;‪Y=X‬‬
‫}‬

‫الحظ وجود العملية & قبل كل من العوامل الرسمية لإلشارة إلى أن العوامل احلقيقية‬
‫ستمرر باإلشارة )‪ (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;
}

‫واجلدول التالي يلخص األمثلة السابقة ويبني تعريف االقتران وطريقة االستدعاء‬
.‫والنتيجة‬

‫مترير العوامل بالقيمة‬ ‫مترير العوامل باإلشارة‬


parameter passing by value. parameter passing by reference.

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; }
}

main() main() main()


{int A=10; {int A=10; {int A=10;
int B=20; int B=20; int B=20;

cout<<”A=”<<A<<” cout<<” cout<<”A=”<<A<<”


B=”<<B<<endl; A=”<<A<<” B=”<<B<<endl;
swap1(A,B); B=”<<B<<endl; swap3(A,B);
swap2(&A,&B);
cout<<”A=”<<A<<” cout<<”A=”<<A<<”
B=”<<B<<endl; cout<<”A= B=”<<B<<endl;
} “<<A<<” }
B=”<<B<<endl;
}

A=10 B=20 A=10 B=20 A=10 B=20


A=10 B=20 A=20 B=10 A=20 B=10

)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

‫البرنامج الثاني‬ ‫البرنامج األول‬


#include <iostream> #include <iostream>
void addNumbers(int, int, void doubleIt(int&);
int&);
int main ()
int main () {
{ int num;
int firstNum, secondNum, num = 3;
sum = 0; doubleIt(num);
firstNum = 3; cout << «The number
secondNum = 6; doubled in main is « << num
addNumbers (firstNum, << endl;
secondNum, sum); return 0;
cout << firstNum << “ + }
“ << secondNum << “ = “ void doubleIt (int& x)
<< sum; {
return 0; cout << «The number to be
} doubled is « << x << endl;
void addNumbers (int x, int x *= 2;
y, int& z) cout << «The number
{ doubled in doubleIt is « << x
z = x + y; << endl;
} }

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‬‬
‫}‬

‫هـ‪3 0 0 0 0 0 -‬‬ ‫د‪3 0 2 2 0 2 -‬‬ ‫ب‪ 0 0 3 0 0 3 -‬ج‪0 1 2 3 0 0-‬‬ ‫أ‪0 1 2 0 1 2 -‬‬

‫‪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‬‬
‫;}‬

‫مما يعني أن الدالة ‪ average‬احلق في معاجلة األجزاء اخلاصة للصنف ‪ student‬كما‬


‫لو كانت دالة منتمية للصنف ‪ .student‬وتختلف طريقة استدعاء الدالة الصديقة عن‬
‫طريقة استدعاء الدالة املنتمية‪ .‬فمثالً‪ ،‬الستدعاء الدالة الصديقة ‪ average‬حلساب معدل‬
‫عالمات الطالب (الكائن‪ ) S‬فإننا نستدعيها كما يلي‪:‬‬
‫;)‪average(S‬‬
‫بينما لو كانت الدالة ‪ average‬دالة منتمية للصنف ‪ student‬فإننا نستدعيها كما يلي‪:‬‬
‫;)(‪S.average‬‬

‫‪162‬‬
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
‫تكتب الدالة الصديقة مثل أي دالة أخرى ليس لها عالقة بالصنف‪ .‬فمثالً‪ ،‬تعرف‬
‫الدالة ‪ average‬كما يلي‪:‬‬

‫)‪double average(student S‬‬


‫;‪{ double sum=0.0‬‬
‫)‪for(int i=0;i<S.csno;i++‬‬
‫;]‪sum=sum+S.grades[i‬‬
‫;‪return sum/S.csno‬‬
‫}‬
‫الحظ‪ ،‬عزيزي الدارس‪ ،‬ما يلي‪:‬‬
‫‪1 .1‬أننا لم نستخدم اسم الصنف وال عملية تقرير املجال ‪ :‬في ترويسة هذه الدالة‪.‬‬
‫‪2 .2‬لهذه الدالة عامل واحد هو ‪ student S‬وذلك لتمرير اسم الكائن الذي نود‬
‫حساب املعدل له من خالله‪ ،‬ولو كانت هذه الدالة دالة منتمية ملا احتجنا إلى‬
‫هذا العامل‪.‬‬
‫‪3 .3‬تستخدم املتغيرات املنتمية داخل الدوال الصديقة بكتابة اسم الكائن ثم عملية‬
‫النقطة‪ ،‬ثم اسم املتغير املنتمي (مثل املتغيرات ‪ S.grades‬و ‪ S.csno‬في مثالنا)‪.‬‬
‫بينما تستخدم هذه املتغيرات مباشرة دون كتابة اسم الكائن واستخدام النقطة)‬
‫داخل الدوال املنتمية)‪.‬‬

‫ ‬
‫نشاط (‪)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‬‬

‫‪// Make the Reset() function a friend of this class‬‬


‫;)‪friend void Reset(Accumulator &cAccumulator‬‬
‫;}‬

‫‪// Reset() is now a friend of the Accumulator class‬‬


‫)‪void Reset(Accumulator &cAccumulator‬‬
‫{‬
‫‪164‬‬
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
‫‪// And can access the private data of Accumulator objects‬‬
‫;‪cAccumulator.m_nValue = 0‬‬
‫}‬

‫ويجب أن تالحظ‪ ،‬عزيزي الدارس‪ ،‬أننا يجب أن منرر الكينونة املركم للدالة ()‬
‫‪ .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‬‬
‫;}‬

‫)‪boolean IsEqual(const Value &cValue1, const Value &cValue2‬‬


‫{‬
‫;)‪return (cValue1.m_nValue == cValue2.m_nValue‬‬
‫}‬

‫الدالة ميكن أن تكون صديقة ألكثر من صنف واحد في الوقت نفسه‪ .‬على سبيل‬
‫املثال‪ ،‬ادرس املثال التالي‪:‬‬
‫‪class Temperature‬‬
‫{‬

‫‪165‬‬
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
private:
int m_nTemp;
public:
Temperature(int nTemp) { m_nTemp = nTemp; }

friend void PrintWeather(Temperature &cTemperature, Humidity


&cHumidity);
};

class Humidity
{
private:
int m_nHumidity;
public:
Humidity(int nHumidity) { m_nHumidity = nHumidity; }

friend void PrintWeather(Temperature &cTemperature, Humidity


&cHumidity);
};

void PrintWeather(Temperature &cTemperature, Humidity &cHumid-


ity)
{
cout << «The temperature is « << cTemperature.m_nTemp <<
« and the humidity is « << cHumidity.m_nHumidity << endl;
}

‫ وميكنها‬،‫ صديقة لكال الصنفني‬PrintWeather ‫ أن الدالة‬،‫ عزيزي الدارس‬،‫الحظ‬


‫ وميكنك أن تالحظ اجلملة‬،‫اخلاصة من الكينونات من كال الصنفني‬
ّ ‫معاجلة البيانات‬
:‫األولى في البرنامج‬
class Humidity;
‫سنعرف‬
ّ ‫ أ ّننا‬compiler ‫املجمع‬
ّ ‫ لكي نخبر‬class prototype ‫التي تعد منوذج ًا للصنف‬
‫ أننا قمنا باإلعالن‬،‫ عزيزي الدارس‬،‫ الحظ‬.‫ فيما بعد‬Class Humidity ‫الصنف الرطوبة‬
‫ في بداية البرنامج حتى نتمكن من استخدامها داخل الصنف‬Humidity ‫عن الصنف‬
Temperature
166
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
#include <iostream>
// ‫;نحتاجها لتعريف الدالة الصديقة‬class Humidity
class Temperature
{
private:
int m_nTemp;
public:
Temperature(int nTemp) { m_nTemp = nTemp; }

friend void PrintWeather(Temperature&cTemperature,Humidity &cHu-


midity);
};

class Humidity
{
private:
int m_nHumidity;
public:
Humidity(int nHumidity) { m_nHumidity = nHumidity; }

friend void PrintWeather(Temperature&cTemperature,Humidity &cHu-


midity);
};

void PrintWeather(Temperature &cTemperature, Humidity &cHumid-


ity)
{
cout << «The temperature is « << cTemperature.m_nTemp <<
« and the humidity is « << cHumidity.m_nHumidity << endl;
}
Int main()
{
Humidity h(70);
Temperature t(29);
PrintWeather(t, h);
Return 0;
}

167
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
‫تدريب (‪)3‬‬
‫اكتب‪ ،‬عزيزي الدارس‪ ،‬الدالة ‪ transfer‬التي تقوم بنقل طالب من شعبة ما إلى‬
‫شعبة أخ��رى بحيث تكون دال��ة صديقة للصنف شعبة ‪ section‬املعرف في الوحدة‬
‫السابقة‪.‬‬

‫‪ 2.3‬األصناف الصديقة ‪Friend Classes‬‬


‫من املمكن أن تكون دالة ما منتمية إلى صنف ما‪ ،‬وصديقة إلى صنف آخر‪ .‬وأحيانا قد‬
‫نضطر إلى تعريف عدد كبير من الدوال املنتمية إلى صنف كدوال صديقة إلى صنف آخر‪،‬‬
‫مما قد يؤدي إلى وضع عدد كبير من النماذج داخل تعريف الصنف‪ .‬ولتجنب وضع هذا‬
‫العدد الكبير من النماذج داخل تعريف الصنف نستطيع تعريف الصنف بأكمله كصنف‬
‫صديق للصنف اآلخر؛ مما يعطي جميع الدوال املنتمية إلى الصنف األول القدرة على‬
‫معاجلة األجزاء اخلاصة في الصنف اآلخر‪ .‬على سبيل املثال‪ ،‬لنفرض أن لدينا الصنفني‬
‫‪ manager‬مدير والصنف ‪ employee‬موظف‪ ،‬وأننا نود إعطاء جميع الدوال املنتمية‬
‫للصنف ‪ manager‬إمكانية معاجلة جميع املتغيرات والدوال اخلاصة بالصنف ‪employee‬؛‬
‫ال من أن نعرف كلً من هذه الدوال كدالة صديقة نستطيع تعريف الصنف ‪manager‬‬ ‫فبد ً‬
‫كصديق للصنف ‪ employee‬مما يحقق الغاية نفسها‪ .‬ويتم ذلك بوضع اجلملة‪:‬‬
‫;‪friend manager‬‬
‫داخل تعريف الصنف ‪. employee‬‬
‫ومثال آخر‪ ،‬ادرس‪ ،‬عزيزي الدارس‪ ،‬الصنف ‪ Storage‬الذي يحتوي على املتغيرين‬
‫املنتميني ‪ m_nValue‬و ‪ m_dValue‬والبناء ‪ Storage‬الذي يعالج هذه املتغيرات اخلاصة‬
‫بالصنف ‪ .Storage‬ثم عرفنا الصنف ‪ Display‬كصنف صديق في الصنف ‪Storage‬‬
‫كما يلي‪:‬‬
‫;‪friend class Display‬‬
‫وألن الصنف العرض ‪Display‬صديق للصنف املخزن ‪ ،Storage‬فإن ّأيا من الدوال‬
‫املنتمية للصنف العرض ‪ Display‬الذي يستعمل الكينونة ‪ object‬من الصنف املخزن‬
‫‪ Storage‬ميكن أن يصل إلى املتغيرات املنتمية اخلاصة ‪ private‬بالصنف املخزن ‪Storage‬‬
‫مباشرة‪ .‬البرنامج البسيط التالي يوضح األصناف أعاله قيد االستعمال‪:‬‬

‫‪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; }

void DisplayItem(Storage &cStorage)


{
if (m_bDisplayIntFirst)
cout << cStorage.m_nValue << « « << cStorage.m_dValue <<
endl;
else // display double first
cout << cStorage.m_dValue << « « << cStorage.m_nValue <<
endl;
}
};
169
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
‫)(‪int main‬‬
‫{‬
‫;)‪Storage cStorage(5, 6.7‬‬
‫;)‪Display cDisplay(false‬‬

‫;)‪cDisplay.DisplayItem(cStorage‬‬

‫;‪return 0‬‬
‫}‬

‫النتيجة التي نحصل عليها بعد تنفيذ البرنامج هي كما يلي‪:‬‬


‫‪6.7 5‬‬

‫هناك بعض املالحظات املتعلقة باألصناف الصديقة‪ ،‬إن عالقة األصناف الصديقة‬
‫ليست تبادلية وال عالقة تعدية؛ أي أن كون الصنف العرض ‪ Display‬صديق للصنف‬
‫املخزن ‪ ،Storage‬ال يعني أن الصنف املخزن ‪ Storage‬أيضا صديق للصنف العرض‬
‫‪ .Display‬فإذا أردت أن جتعل صنفني صديقني لبعضها بعضاً‪ ،‬فيجب على االثنني أن‬
‫يعلنا على أن الصنف اآلخر صديق‪ .‬أخيرا‪ ،‬إذا كان الصنف ‪ A‬صديق ‪ ، B‬و ‪ B‬صديق‬
‫‪ ،C‬فهذا ال يعني أن الصنف ‪ A‬صديق للصنف ‪. C‬‬

‫ ‬‫نشاط (‪)5‬‬
‫قم‪ ،‬عزيزي ال��دارس‪ ،‬باستخدام احلاسوب إلع��ادة تنفيذ الصنف ‪LinkedList‬‬
‫املعرف في الوحدة السابقة مستخدما التعريف اجلديد لـ ‪ . node‬في الواقع لن تضطر‪،‬‬
‫عزيزي الدارس‪ ،‬لتغيير أي شيء باستثناء تعريف ‪. node‬‬

‫‪170‬‬
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
‫‪Operators‬‬ ‫‪ .4‬العمليات‬
‫هناك العديد من العمليات املعرفة في لغة ‪ C++‬التي متلك خاصية التحويل التلقائي؛‬
‫أي بصور مباشرة بني أنواع البيانات املعرفة في لغة‪ .++ C‬هذه التحويالت تسمح للمبرمج‬
‫بكتابة مجموعة من التعليمات والعمليات التي حتتوي على مجموعة مخلوطة من أنواع‬
‫البيانات في لغة ‪C++.‬‬
‫لغة ‪ C++‬جتعلنا نستطيع إعادة تعريف ملعنى العمليات عند تطبيقها على الكينونات‬
‫املختلفة لألصناف‪ .‬من خالل هذا املفهوم (إعادة التعريف ‪ )Overloading‬متكنا لغة‬
‫‪ C++‬من إعادة تعريف العمليات لتعمل مع الكينونات املشتقة من األصناف املختلفة‪.‬‬
‫في هذا القسم سوف نناقش هذا املفهوم وإعطاء أمثلة على ذلك‪.‬‬

‫‪ 1.4‬إعادة حتميل العمليات ‪Operators Overloading‬‬


‫من املميزات اجلميلة للغة‪ C ++‬أنها متكننا من إعادة تعريف العمليات ‪operator‬‬
‫ال نستطيع تعريف العملية‪ +‬كعملية منتمية للصنف‬ ‫املختلفة مبا يتناسب مع تطبيقاتنا‪ .‬فمث ً‬
‫‪ section‬لتعني إضافة طالب معني إلى الشعبة والعملية – لتعني شطب طالب معني من‬
‫الشعبة‪ .‬إن هذه التعريفات اجلديدة لـ ‪ +‬و– ما هي إال تعريفات إضافية لهذه العمليات؛‬
‫أي أننا نحمل هذه العمليات تعريفات جديدة‪ operator overloading‬وهذا ممكن‬
‫ألن ما يحدد تعريف العملية املطلوب تنفيذها هو عدد العوامل ونوعها وليس فقط رمز‬
‫العملية‪ .‬في الواقع‪ ،‬للكثير من العمليات أصالً‪ ،‬أكثر من تعريف في لغة ‪ .C++‬فمثالً‪،‬‬
‫العملية ‪ +‬لها أكثر من تعريف واحد؛ فهي تعني اإلشارة املوجبة عندما تأتي مع عامل‬
‫واحد‪ ،‬وتعني عملية جمع صحيحة وعملية جمع حقيقية وغير ذلك‪ .‬ويقوم مترجم‬
‫‪ C++‬بتحديد التعريف املطلوب من عدد العوامل ونوعها‪.‬‬
‫إن تعريف العمليات ال يختلف كثير ًا عن تعريف الدوال‪ ،‬وفي الواقع‪ُ ،‬ت ّ‬
‫عد العمليات‬
‫نوع ًا خاص ًا من الدوال في لغة ‪ C++ .‬يختلف تعريف العملية عن تعريف الدالة فقط في‬
‫أن اسم العملية يتكون من الكلمة ‪ operator‬متبوعا برمز العملية‪ ،‬على سبيل املثال‪،‬‬
‫‪. operator+‬‬
‫وكما نضع منوذج ًا للدالة املنتمية داخل تعريف الصنف يجب أيض ًا أن نضيف منوذجا‬
‫للعملية املنتمية داخل تعريف الصنف‪ .‬فمثالً‪ ،‬لتعريف العملية ‪ +‬كعملية منتمية للصنف‬

‫‪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‬فإذا كان ترتيب تنفيذ العملية من اليسار إلى‬
‫اليمني فال ميكن تغيير هذا الترتيب وجعله من اليمني إلى اليسار‪.‬‬

‫‪ 2.4‬أشكال إعادة التحميل للعمليات املختلفة‬


‫كما ميكننا أن نعرف الدالة كدالة منتمية أو صديقة فإنه ميكننا أيض ًا أن نعرف العملية‬
‫كعملية منتمية أو عملية صديقة‪ .‬وكما أن تعريف العملية املنتمية ال يختلف كثيرا عن‬
‫تعريف الدالة املنتمية فال يختلف تعريف العملية الصديقة كثيرا عن تعريف الدالة‬
‫ومثال على تعريف الدوال الصديقة سنعرف اآلن دالة صديقة للصنف ‪time‬‬ ‫ٌ‬ ‫الصديقة‪.‬‬
‫الذي ميثل وقت ًا معين ًا يتكون من الساعة والدقيقة والثانية‪ .‬ميكننا أن نعرف هذا الصنف‬
‫باستخدام ثالثة متغيرات منتمية‪ hour‬و‪ min‬و‪ sec‬لتمثيل الساعة والدقيقة والثانية‬
‫على الترتيب‪.‬‬
‫لنفرض أننا نريد تعريف العملية << بحيث تعرض لنا الوقت على الشاشة بطريقة‬
‫مناسبة‪ ،‬فمث ً‬
‫ال عند تنفيذ العملية‪،‬‬
‫;‪cout<<T‬‬
‫تكون النتيجة عرض الوقت كما يلي‪:‬‬
‫‪The time is 12:23:30‬‬

‫على افتراض أن قيم املتغيرات املنتمية لـ ‪ 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‬‬
‫;}‬

‫أما العملية نفسها فتعرف كما يلي‪:‬‬


‫)‪ostream& operator<<(ostream& C, time t‬‬
‫{ ‬
‫;‪ C<<”the time is “<<t.hour<<’:’<<t.min<<’:’<<t.sec<<endl‬‬
‫;‪return C‬‬
‫} ‬
‫الحظ‪ ،‬عزيزي الدارس‪ ،‬أن للعملية << عاملني هما‪:‬‬
‫‪1 .1‬العامل ‪ C‬وهو من النوع &‪ ostream‬وميثل الكائن الذي نود عرض القيمة‬
‫عليه‪ .‬الحظ أيضاً‪ ،‬عزيزي الدارس‪ ،‬أن هذا العامل سيمرر باإلشارة ألنه من‬
‫النوع &‪.ostream‬‬
‫‪2 .2‬والعامل ‪ t‬وهو كائن من الصنف ‪ time‬نود عرض قيمته‪.‬‬

‫واآلن نستطيع استخدام هذه العملية كما يلي‪:‬‬


‫;)‪time tt(12,30,23‬‬
‫;‪cout<<tt‬‬

‫‪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‬‬
‫;}‬

‫في تعريف الصنف ‪ ,ConstFuncClass‬تستطيع الدالة )(‪ normalFunc‬تعديل‬


‫عنصر البيانات ‪ .age‬أما الدالة األخرى )(‪ conFunc‬فهي ال تستطيع‪ ،‬وسبب ذلك أنها‬
‫من النوع‪ const‬لذا‪ ،‬يقوم املترجم باالعتراض وإظهار رسالة اخلطأ املناسبة عند محاولة‬
‫هذه الدالة تعديل البيانات املنتمية ‪ .age‬وكما ترى‪ ،‬عزيزي الدارس‪ ،‬يتم تعريف الدالة‬
‫املنتمية الثابت بوضع الكلمة املفتاحية ‪ const‬بعد اإلعالن عن الدالة مباشرة وقبل جسم‬
‫الدالة‪.‬‬

‫ ‬ ‫تدريب (‪)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‬‬
‫إذا لم يكن وهكذا‪.‬‬

‫يبني الشكل (‪ )1‬بالرسم مصفوفة متثل املجموعة }سبت‪ ،‬اثنني‪ ،‬خميس{‬

‫جمعة‬ ‫خميس‬ ‫أربعاء‬ ‫ثالثاء‬ ‫اثنني‬ ‫أحد‬ ‫سبت‬


‫‪0‬‬ ‫‪1‬‬ ‫‪0‬‬ ‫‪0‬‬ ‫‪1‬‬ ‫‪0‬‬ ‫‪1‬‬
‫الشكل (‪ :)1‬يبني مصفوفة متثل املجموعة }سبت‪ ،‬اثنني‪ ،‬خميس{‬

‫وسنستخدم الصنف التالي لتمثيل مجموعة من أيام األسبوع‪:‬‬


‫{‪class Set‬‬
‫;]‪int weekdays[7‬‬
‫‪public:‬‬
‫;)(‪Set‬‬
‫;)‪void operator+(int i‬‬
‫;)‪void operator-(int i‬‬
‫;)‪friend Set operator*(Set S, Set T‬‬
‫;)‪friend Set operator+(Set S, Set T‬‬
‫;)‪int IN(int i‬‬
‫;)‪void operator=(Set S‬‬
‫;)(‪int SetEmpty‬‬
‫;)‪friend Set operator-(Set S, Set T‬‬
‫;)‪friend ostream& operator<<(ostream& C, Set S‬‬
‫;}‬

‫الحظ‪ ،‬عزيزي الدارس‪ ،‬أن هذا الصنف يحتوي على متغير منتم واحد هو ‪weekdays‬‬

‫‪184‬‬
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
‫هو‪ ،‬في احلقيقة‪ ،‬مصفوفة من سبعة مواقع كل موقع مخصص ليوم من أيام األسبوع‪ .‬كما‬
‫يحتوي على العديد من مناذج الدوال والعمليات املنتمية والصديقة‪ .‬وفيما يلي سنناقش ك ً‬
‫ال من‬
‫هذه الدوال والعمليات‪:‬‬
‫البناء ‪: Set‬‬
‫)‪Set::Set(){for(int i=0;i<7;i++‬‬
‫};‪weekdays[i]=0‬‬

‫يقوم هذا البناء بإنشاء مجموعة فارغة بوضع القيمة ‪ 0‬في كل موقع من مواقع‬
‫املصفوفة‪.‬‬

‫إضافة عنصر للمجموعة ‪: operator+‬‬


‫)‪void Set::operator+(int i‬‬
‫)‪{if(i>=1 && i<=7‬‬
‫};‪weekdays[i-1]=1‬‬

‫تقوم هذه العملية بإضافة عنصر جديد للمجموعة‪ ،‬وذلك بتخزين الرقم ‪ 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‬وميكننا تعريف العملية إما كعملية منتمية‬
‫وإما كعملية صديقة‪.‬‬

‫‪ .7‬حملة عن الوحدة الدراسية اخلامسة‬


‫تناقش ال��وح��دة التالية أه��م مواضيع البرمجة الكينونية ومنها األص��ن��اف املشتقة‬
‫‪ derived classes‬والوراثة والقوالب‪ .‬وهي كلها مواضيع متكننا من إعادة استخدام‬
‫برامجنا بشكل كبير‪ .‬وهذه هي أبرز فوائد لغات البرمجة الكينونية‪.‬‬

‫‪189‬‬
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
‫ إجابات التدريبات‬.8
)1( ‫تدريب‬
-1
‫البرنامج الثاني‬ ‫البرنامج األول‬
3+6=9
The number to be doubled is 3

The number doubled in doubleIt is 6

The number doubled in main is 6

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

// initialize static member at file scope; one classwide copy


const int Date::days[] =
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

// 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;

// test for a leap year


if ( month == 2 && leapYear( year ) )
day = ( dd >= 1 && dd <= 29 ) ? dd : 1;
else
day = ( dd >= 1 && dd <= days[ month ] ) ? dd : 1;
} // end function setDate

// overloaded prefix increment operator


Date &Date::operator++()
{
helpIncrement(); // increment date
return *this; // reference return to create an lvalue
// end function operator++

// overloaded postfix increment operator; note that the


// dummy integer parameter does not have a parameter name
Date Date::operator++( int )
{
Date temp = *this; // hold current state of object
helpIncrement();

// return unincremented, saved, temporary object


return temp; // value return; not a reference return
} // end function operator++

// add specified number of days to date


4const Date &Date::operator+=( int additionalDays )
194
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
4{
for ( int i = 0; i < additionalDays; i++ )
helpIncrement();

return *this; // enables cascading


} // end function operator+=

// if the year is a leap year, return true; otherwise, return false


bool Date::leapYear( int testYear ) const
{
if ( testYear % 400 == 0 ||
6 ( testYear % 100 != 0 && testYear % 4 == 0 ) )
return true; // a leap year
else
return false; // not a leap year
} // end function leapYear

// determine whether the day is the last day of the month


bool Date::endOfMonth( int testDay ) const
{
if ( month == 2 && leapYear( year ) )
return testDay == 29; // last day of Feb. in leap year
else
return testDay == days[ month ];
} // end function endOfMonth

// function to help increment the date


void Date::helpIncrement()
{
// day is not end of month
if ( !endOfMonth( day ) )
day++; // increment day
else
if ( month < 12 ) // day is end of month and month < 12
195
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
{
month++; // increment month
day = 1; // first day of new month
} // end if
else // last day of year
{
year++; // increment year
month = 1; // first month of new year
day = 1; // first day of new month
} // end else
} // end function helpIncrement

// overloaded output operator


ostream &operator<<( ostream &output, const Date &d )
{
static char *monthName[ 13 ] = { “”, “January”, “February”,
“March”, “April”, “May”, “June”, “July”, “August”,
“September”, “October”, “November”, “December” };
output << monthName[ d.month ] << ‘ ‘ << d.day << “, “ << d.year;
return output; // enables cascading
} // end function operator<<

// Date class test program.


#include <iostream>
using std::cout;
using std::endl;

#include “Date.h” // Date class definition

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 );

8 d3.setDate( 2, 28, 1992 );


cout << “\n\n d3 is “ << d3;
cout << “\n++d3 is “ << ++d3 << “ (leap year allows 29th)”;

Date d4( 7, 13, 2002 );

cout << “\n\nTesting the prefix increment operator:\n”


<< “ d4 is “ << d4 << endl;
cout << “++d4 is “ << ++d4 << endl;
cout << “ d4 is “ << d4;

cout << “\n\nTesting the postfix increment operator:\n”


<< “ d4 is “ << d4 << endl;
cout << “d4++ is “ << d4++ << endl;
cout << “ d4 is “ << d4 << endl;
return 0;
} // end main

:‫ناجت التنفيذ‬
d1 is January 1, 1900
d2 is December 27, 1992
d3 is January 1, 1900

d2 += 7 is January 3, 1993

d3 is February 28, 1992


++d3 is February 29, 1992 (leap year allows 29th)

Testing the prefix increment operator:

197
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
d4 is July 13, 2002
++d4 is July 14, 2002
d4 is July 14, 2002

Testing the postfix increment operator:


d4 is July 14, 2002
d4++ is July 14, 2002
d4 is July 15, 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;}

void getdist() //get length from user


{
cout << “\nEnter feet: “; cin >> feet;
cout << “Enter inches: “; cin >> inches;
}

198
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
void showdist() const //display distance
{ cout << feet << “\’-” << inches << ‘\”’; }

Distance add_dist(const Distance&) const; //add


};
//--------------------------------------------------------------

//add this distance to d2, return the sum


Distance Distance::add_dist(const Distance& d2) const
{
Distance temp; //temporary variable

// feet = 0; //ERROR: can’t modify this


// d2.feet = 0; //ERROR: can’t modify d2
temp.inches = inches + d2.inches; //add the inches
if(temp.inches >= 12.0) //if total exceeds 12.0,
{ //then decrease inches
temp.inches -= 12.0; //by 12.0 and
temp.feet = 1; //increase feet
} //by 1
temp.feet += feet + d2.feet; //add the feet
return temp;
}
////////////////////////////////////////////////////////////////
int main()
{
Distance dist1, dist3; //define two lengths
Distance dist2(11, 6.25); //define, initialize dist2

dist1.getdist(); //get dist1 from user


dist3 = dist1.add_dist(dist2); //dist3 = dist1 + dist2

//display all lengths


cout << “\ndist1 = “; dist1.showdist();
199
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
cout << “\ndist2 = “; dist2.showdist();
cout << “\ndist3 = “; dist3.showdist();
cout << endl;
return 0;
}

)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
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
};

Bool Set::Member (const int elem)


{
for (register i = 0; i < card; ++i)
if (elems[i] == elem)
return true;
return false;
}
void Set::AddElem (const int elem)
{
if (Member(elem))
return;
if (card < maxCard)
elems[card++] = elem;
else
cout << “Set overflow\n”;
}
void Set::RmvElem (const int elem)
{
for (register i = 0; i < card; ++i)
if (elems[i] == elem) {
for (; i < card-1; ++i) // shift elements left
elems[i] = elems[i+1];
--card;
}
}
void Set::Copy (Set &set)
{
for (register i = 0; i < card; ++i)
set.elems[i] = elems[i];
set.card = card;
}

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”;
}

int main (void)


{
203
‫الدوال الصديقة والعمليات‬ ‫الوحدة الرابعة‬
‫;‪Set s1, s2, s3‬‬
‫;)(‪s1.EmptySet(); s2.EmptySet(); s3.EmptySet‬‬
‫;)‪s1.AddElem(10); s1.AddElem(20); s1.AddElem(30); s1.AddElem(40‬‬
‫;)‪s2.AddElem(30); s2.AddElem(50); s2.AddElem(10); s2.AddElem(60‬‬
‫;)(‪cout << “s1 = “; s1.Print‬‬
‫;)(‪cout << “s2 = “; s2.Print‬‬
‫;)(‪s2.RmvElem(50); cout << “s2 - {50} = “; s2.Print‬‬
‫;”‪if (s1.Member(20)) cout << “20 is in s1\n‬‬
‫;)(‪s1.Intersect(s2,s3); cout << “s1 intsec s2 = “; s3.Print‬‬
‫;)(‪s1.Union(s2,s3); cout << “s1 union s2 = “; s3.Print‬‬
‫;”‪if (!s1.Equal(s2)) cout << “s1 /= s2\n‬‬
‫;‪return 0‬‬
‫}‬

‫‪ .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‬‬

‫‪236‬‬ ‫‪ .3‬الدوال واألصناف القالبية ‪Template Functions and Classes‬‬


‫‪236‬‬ ‫‪ 1.3‬الدوال القالبية ‪Template Functions‬‬
‫‪237‬‬ ‫‪ 2.3‬استخدام الدوال القالبية‬
‫‪242‬‬ ‫‪ 3.3‬األصناف القالبية ‪Template Classes‬‬

‫‪249‬‬ ‫‪ .4‬تعدد األوجه ‪Polymorphism‬‬


‫‪249‬‬ ‫‪ 1.4‬مفهوم تعدد األوجه وخواصه‬
‫‪258‬‬ ‫‪ 2.4‬استخدامات تعدد األوجه‬
‫‪261‬‬ ‫‪ .5‬اخلالصة‬
‫‪262‬‬ ‫‪ .6‬حملة عن الوحدة الدراسية السادسة‬
‫‪262‬‬ ‫‪ .7‬إجابات التدريبات‬
‫‪286‬‬ ‫‪ .8‬مسرد املصطلحات‬
‫‪287‬‬ ‫‪ .9‬املراجع‬

‫‪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.

5. Deitel P. J. and Deitel H. M., “C++ for Programmers”, Pren-


tice Hall, 2009.
6. Moo B. E., “C++ Primer”, 4th Edition, Addison-Wesley,
2005.
7. Prata S., “C++ Primer Plus”, 5th Edition, Sams, 2004.
8. Savitch, Walter, “Problem Solving with C++”, Addison-Wes-
ley Publishing Company, Inc 1996.
9. Savitch W., “Absolute C++”, 2nd Edition, Addison-Wesley,
2005.

‫ ما حتتاج إليه لدراسة الوحدة‬5.1


.‫حتتاج لدراسة هذه الوحدة إلى جو هادئ وإلى بعض األوراق وقلم رصاص‬
‫كما حتتاج حلل التمارين والقيام باألنشطة إلى جهاز حاسوب مزود مبترجم للغة‬
C++ .

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‬هذا التنظيم الهرمي على شكل شجرة الهرمية‪.‬‬

‫الشكل (‪ :)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‬‬
‫اخلاص بالصنف املشتق) مما يعني إمكانية معاجلتها من طرف أي دالة‪.‬‬

‫وهنالك أنواع أخرى من الوراثة سنناقشها الحق ًا في هذا القسم‪.‬‬


‫لنعرف اآلن الدوال املنتمية للصنف املشتق ‪ house‬ولنبدأ بالبناء‪ .‬الحظ‪ ،‬عزيزي‬
‫الدارس‪ ،‬أن لهذا البناء قيما تلقائية‪.‬‬
‫)‪house::house(float P,float Rent,float B,float G‬‬
‫ ‬ ‫)‪: property(P,Rent‬‬

‫‪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‬وحاول ترجمة البرنامج‬
‫اجلديد والحظ النتيجة‪.‬‬

‫واآلن‪ ،‬نستطيع إنشاء كائن من الصنف ‪ house‬واستدعاء بعض الدوال عليه‪.‬‬


‫‪;house h‬‬
‫تقوم هذه اجلملة بإنشاء الكائن ‪ h‬من الصنف ‪ house‬واستدعاء البناء‪ house‬إلعطاء‬
‫متغيراته املنتمية القيم التلقائية‪ .‬أما اجلملة‪:‬‬
‫;)‪cout<<”\n Rent after increment “<<h.increase_rent(100.0‬‬

‫فتقوم باستدعاء الدالة ‪ increase_rent‬لزيادة األجرة الشهرية اخلاصة بالكائن ‪h‬‬


‫مبقدار‪ 100.0‬وطباعة القيمة اجلديدة لإليجار‪ .‬الحظ‪ ،‬عزيزي الدارس‪ ،‬كيف أننا‬
‫استخدمنا الدالة ‪ increase_Rent‬كما لو كانت معرفة داخل الصنف ‪. house‬‬
‫واجلملة‪:‬‬
‫;)(‪h.print_info‬‬
‫تقوم باستدعاء الدالة ‪ print_info‬املنتمية للصنف ‪ house‬لطباعة البيانات اخلاصة‬
‫بالكائن ‪ h‬كافة‪.‬‬
‫لنعرف اآلن الصنف املشتق ‪ flat‬الذي هو أيض ًا مشتق من الصنف ‪.Property‬‬
‫‪class flat:public property‬‬
‫‪{ protected:‬‬
‫;‪float Area‬‬
‫‪public:‬‬
‫;)‪flat(float P=5000.0,float Rent=50.0,float A=120.0‬‬
‫;)(‪void print_info‬‬
‫;}‬

‫الحظ‪ ،‬عزيزي الدارس‪ ،‬أن هذا الصنف يضم‪ ،‬باإلضافة إلى املتغيرين و ‪price‬‬

‫‪220‬‬
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
‫‪ ، Monthly_Rent‬املتغير ‪ Area‬الذي ميثل مساحة الشقة‪ .‬ولذا فإن البناء اخلاص‬
‫بهذا الصنف يقوم بعد استدعاء البناء اخلاص بالصنف األساس بإعطاء هذا املتغير قيمة‬
‫ابتدائية‪.‬‬
‫)‪flat::flat(float P,float Rent,float A): property(P,Rent‬‬
‫;‪{ Area=A‬‬
‫}‬

‫وتقوم الدالة ‪ print_info‬بطباعة البيانات اخلاصة بكائن من الصنف ‪ flat‬كافة‪.‬‬


‫)(‪void flat::print_info‬‬
‫‪{cout<<endl<<”The Price is “<<Price‬‬
‫‪<<”\n The Montly Rent is “<<Monthly_Rent‬‬
‫;‪<<”\n The flat area is “<<Area<<endl‬‬
‫;}‬

‫وسنعرف اآلن الصنف ‪ Multi_Building‬الذي ميثل عمارة متعددة الطوابق‪ .‬وسنعرف‬


‫هذا الصنف كصنف مشتق من الصنف ‪ flat‬وذلك حتى نورث املتغير‪( Area‬الذي ميثل‬
‫مساحة كل شقة لهذا الصنف) باإلضافة إلى املتغيرين ‪ Price‬و‪. Monthly_Rent‬‬
‫‪class Multi_Building: public flat‬‬
‫‪{private:‬‬
‫;‪int NoStory‬‬
‫;‪int NoFlats‬‬
‫‪public:‬‬

‫‪Multi_Building(float P=20000.0,float Rent=100.0,‬‬


‫;)‪float A=120.0,int S=1,int F=2‬‬
‫};‪float area(){return NoStory*NoFlats*Area‬‬
‫;)(‪void print_info‬‬
‫;}‬
‫الحظ‪ ،‬عزيزي الدارس‪ ،‬أن لهذا الصنف‪ ،‬باإلضافة إلى املتغيرات ‪ Price‬و‬
‫‪ Monthly_Rent‬و ‪ ،Area‬متغيرين جديدين هما ‪ ،NoStory‬وميثل عدد الطوابق في‬
‫املبنى‪ ،‬واملتغير‪ ، NoFlats‬وميثل عدد الشقق في كل طابق‪ .‬ويقوم البناء بإعطاء هذين‬
‫املتغيرين القيم التلقائية ‪ 1‬و‪ 2‬على الترتيب‪.‬‬
‫‪221‬‬
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
‫)‪Multi_Building::Multi_Building(float P,float Rent,float A,int S,int F‬‬
‫ ‬ ‫)‪: flat(P,Rent,A‬‬
‫;‪{NoStory=S‬‬
‫;‪NoFlats=F‬‬
‫}‬

‫الحظ أيضا‪ ،‬عزيزي الدارس‪ ،‬أننا عرفنا الدالة السطرية ‪ 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‬اخلاصة‬


‫بالصنف ‪ property‬داخل الدالة ‪ print_info‬اخلاصة بالصنف ‪ .house‬فقد استخدمنا‬
‫اجلملة‪:‬‬
‫;)(‪property::print_info‬‬
‫حتدد هذه اجلملة اسم الصنف الذي تنتمي له الدالة ‪ print_info‬التي نود استدعاءها‬
‫أي ‪ property‬ثم استخدمنا عملية حتديد املجال ‪ ::‬اسم الدالة ‪ .print_info‬ولو أننا‬
‫كتبنا اسم الدالة فقط ألصبح ذلك استدعاء ذاتي ًا ‪ recursive call‬للدالة ‪print_info‬‬
‫اخلاصة بالصنف ‪ house‬وهو بالطبع‪ ،‬ما ال نريد‪.‬‬
‫وبطريقة مشابهة‪ ،‬نكتب الدالة ‪ print_info‬اخلاصة بالصنف ‪flat‬؛ إذ تستدعي‬
‫هذه الدالة الدالة ‪ print_info‬اخلاصة بالصنف ‪ property‬لطباعة قيمة ‪ Price‬وقيمة‬
‫‪ Monthly_Rent‬ثم تقوم بطباعة قيمة املتغير ‪.Area‬‬
‫)(‪void flat::print_info‬‬
‫;)(‪{ property::print_info‬‬
‫;‪cout<<»\n The flat area 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‬‬

‫قيم جلميع متغيراته‬


‫ع��رف األصناف أع�لاه واكتب لكل صنف دال��ة لقراءة ٍ‬
‫املنتمية؛ باإلضافة إلى دالة لطباعة جميع البيانات عن كل صنف‪.‬‬
‫‪224‬‬
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
‫‪ 3.2‬مستوى الوصول للعناصر املوروثة (أنواع الوراثة)‬
‫ذكرنا فيما سبق أن ما يحدد كيفية التعامل مع املتغيرات والدوال املنتمية يعتمد على‬
‫نوع الوراثة (الذي يحدد بالكلمة التي تسبق اسم الصنف األساس عند تعريف الصنف‬
‫املشتق)‪ .‬واستخدمنا في مثالنا أعاله نوع ًا واحد ًا من أنواع الوراثة وهو الوراثة العامة‬
‫‪ .public‬وهنالك أنواع أخرى من الوراثة هي الوراثة اخلاصة ‪ private‬والوراثة احملمية‬
‫‪ .protected‬وتعرف كل منهما باستخدام الكلمة ‪ private‬والكلمة ‪ protected‬على‬
‫الترتيب قبل اسم الصنف األساس في تعريف الصنف املشتق‪.‬‬
‫نرى ان لغة ‪ C++‬توفر ثالثة أنواع من الوراثة حيث يحدد نوع الوراثة درجة الوصول‬
‫للبيانات والدوال املوجودة في صنف األساس من خالل الصنف املشتق‪ .‬واألنواع هي ‪:‬‬
‫‪1 .1‬الوراثة اخلاصة ‪ :Private Inheritance‬عندما يرث صنف مشتق صنف‬
‫األساس وكان نوع الوراثة خاص ًا فإن كل البيانات والدوال املوجودة في صنف‬
‫األساس تصبح خاصة بالصنف املشتق‪ ،‬ما عدا املعرفة في القسم اخلاص للصنف‬
‫األساسي فإنها ال تورث أبداً‪.‬‬
‫‪2 .2‬الوراثة احملمية ‪ :Protected Inheritance‬في هذا النوع تصبح كل بيانات‬
‫محمية بالصنف املشتق‪ ،‬ما عدا املعرفة في القسم اخلاص‬ ‫ّ‬ ‫صنف األساس ودواله‬
‫للصنف األساسي فإنها ال تورث أبداً‪.‬‬
‫‪3 .3‬الوراثة العامة ‪ :Public Inheritance‬في هذا النوع يتم توزيع البيانات والدوال‬
‫كما يلي‪:‬‬
‫•احملمي ‪ Protected‬في صنف األساس ‪ Base Class‬يصبح محمي ًا في‬
‫الصنف املشتق ‪.‬‬
‫•العام ‪ Public‬في صنف األساس يصبح عام ًا في الصنف املشتق ‪.‬‬
‫تورث أبداً)‪.‬‬
‫يورث أبدا (البيانات اخلاصة ال ّ‬ ‫•اخلاص ال ّ‬
‫وميكن توضيح ما سبق من خالل اجلدول (‪.)1‬‬

‫نوع الوراثة‬ ‫نوع البيانات في‬


‫عام ‪Public‬‬ ‫محمي ‪Protected‬‬ ‫خاص ‪Private‬‬ ‫الصنف االساس‬
‫ال يورث‬ ‫ال يورث‬ ‫ال يورث‬ ‫خاص ‪Private‬‬

‫‪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‬‬
‫;}‬

‫‪ 4.2‬الوراثة املتعددة ‪Multiple Inheritance‬‬


‫تسمح لغة ‪ C++‬لصنف ما أن يرث أكثر من صنف‪ ،‬وهذا النوع من التوارث يتحقق‬
‫بطريقتني‪:‬‬
‫‪ - 1‬الصنف الوارث ميكن أن يرث أكثر من صنف موروث‪.‬‬
‫‪ - 2‬الصنف الوارث ميكن أن ُيستخدم كـصنف موروث من طرف صنف وارث آخر‪.‬‬

‫وميكن توضيح ذلك بالهرمية املوجودة في شكل (‪:)3‬‬

‫‪228‬‬
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
‫الشكل (‪ :)3‬الوراثة املتعددة‬

‫والصيغة العامة للتوارث في احلالة األولى هي‪:‬‬


‫‪Class derived :inheritanceType NameOf BaseClass1, inheritanceType‬‬
‫‪NameOfBaseClass2, . . . , inheritanceType NameOfBaseClassN‬‬
‫{‬
‫;} ‪Class Body‬‬

‫لتوضيح ذلك دعنا‪ ,‬عزيزي الدارس‪ ,‬ندرس املثالني التاليني‪:‬‬


‫املثال األول ميثل النوع الثاني للوراثة املتعددة – احلالة رقم ‪( 2‬انظر الشكل ‪)3‬‬
‫>‪#include <iostream.h‬‬
‫>‪#include <cstdlib‬‬
‫‪class B1‬‬

‫‪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‬‬

‫عند تنفيذ البرنامج‪ ،‬إذا كانت املدخالت كما يلي‪:‬‬


‫‪1‬‬
‫‪2‬‬
‫‪3‬‬
‫سيكون الناجت أيض ًا كما هو في املدخالت‪:‬‬
‫‪1‬‬
‫‪2‬‬
‫‪3‬‬
‫وهذا مثال آخر على التوارث ميثل الوراثة من أكثر من صنف (احلالة رقم ‪ 1‬من الشكل‬
‫السابق ‪)3‬‬
‫>‪#include <iostream.h‬‬
‫>‪#include <cstdlib‬‬
‫‪class B1‬‬
‫{‬
‫;‪int a‬‬
‫‪231‬‬
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
public:
B1(int x)
{
a=x;}
int get_a()
{return a;}};
class D1
{
int b;
public:
D1(int x)
{
b=x;}
int get_b()
{return b;}};
class D2:public D1,public B1
{
Int c;
public:
D2(int x,int y,int z):D1(y),B1(z)
{c=x;}
void show()
{
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;
}
232
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
‫عدة أصناف‬
ّ ‫وهذا املثال يوضح كيفية استدعاء دوال البناء والهدم في حال وجود‬
:‫موروثة‬
#include <iostream.h>
#include <cstdlib>
using namespace std ;
class B1
{
public:
B1()
{
cout<<»B1 Constructor\n»;
}
~B1()
{
cout<<»B1 Destructor\n»;
system(«PAUSE»);
}
};
class B2
{
public:
B2()
{
cout<<»B2 Constructor\n»;
}
~B2()
{
cout<<»B2 Destructor\n»;
system(«PAUSE»);
}
};
class D:public B1,public B2
{

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;
}

:‫وستكون مخرجات هذا البرنامج بالشكل التالي‬


B1 Constructor
B2 Constructor
B2 Destructor
B1 Constructor
B2 Constructor
D Constructor
D Destructor
B2 Destructor
B1 Destructor
B1 Destructor

234
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
‫ويجب عليك‪ ،‬عزيزي الدارس‪ ,‬أن تتذكر‪:‬‬
‫عدة أصناف موروثة فإن دوال البناء يتم تنفيذها من اليسار إلى‬ ‫‪ - 1‬أنه في حالة وراثة ّ‬
‫اليمني والعكس بالنسبة لدوال الهدم‪.‬‬

‫الشكل (‪ :)4‬الوراثة املتعددة وترتيب تنفيذ البناء والهدام‬

‫‪ - 2‬يتم إرسال املعامالت لدالة بناء الصنف املوروث ( األصناف املوروثة ) عبر دالة بناء‬
‫الصنف الوارث‪.‬‬

‫‪235‬‬
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
‫‪ .3‬الدوال واألصناف القالبية‬
‫‪Template Functions and Classes‬‬
‫من املميزات املهمة للغة‪ C++‬التي جتنبنا احلاجة إلى كتابة دوال متشابهة إلى حد كبير‬
‫هي أنها متنحنا إمكانية تعريف دوال وأصناف قالبية‪ ،‬تسمح لنا هذه الدوال واألصناف‬
‫القالبية القدرة على تأجيل حتديد نوع البيانات إلى حني احلاجة إلى هذه القوالب؛ مما‬
‫يسمح لنا بإعادة استخدام هذه القوالب مرة بعد مرة وألنواع مختلفة من البيانات‪.‬‬
‫وبذلك تعطينا لغة ‪ C++‬طريقة أخرى إلعادة استخدام البرامج ‪ code reuse‬ال تقل‬
‫أهمية عن الوراثة واألصناف املشتقة‪ .‬وهنالك نوعان من القوالب‪ :‬الدوال القالبية‬
‫واألصناف القالبية‪ .‬وفيما يلي سنناقش ك ً‬
‫ال منها‪.‬‬

‫‪ 1.3‬الدوال القالبية ‪Template Functions‬‬


‫يواجه مبرمجو اللغات اإلجرائية ‪ procedural languages‬مثل الباسكال في كثير‬
‫من األحيان مشكلة احلاجة إلى تكرار كتابة الدالة نفسها ألنواع مختلفة من البيانات‪.‬‬
‫فمثالً‪ ،‬نضطر أحيانا إلى كتابة دالة لترتيب قائمة من الطلبة ودالة أخرى لترتيب قائمة من‬
‫املوظفني‪ ،‬وال تختلف هاتان الدالتان إال في نوع البيانات التي يتعامالن معها‪ .‬متنحنا لغة‬
‫‪ C++‬إمكانية تعريف الدالة كدالة قالبية ال نحدد بها نوع البيانات بل نوع البيانات كعامل‬
‫يحدد من جانب املبرمج عند احلاجة إلى تلك الدالة‪ .‬وعليه ميكن مترير نوع البيانات على‬
‫أنه قائمة من الطلبة أو قائمة من املوظفني واستخدام الدالة القالبية نفسها من دون احلاجة‬
‫إلى إعادة كتابتها مرة أخرى‪.‬‬
‫وعلى سبيل املثال على كيفية تعريف دالة قالبية‪ ،‬سنعرف الدالة ‪ IsGreater‬التي‬
‫تأخذ عاملني من أي نوع من البيانات وتقارن بينهما‪ .‬فإذا كان العامل األول هو األكبر‬
‫فإنها تعيد القيمة ‪ 1‬وإال فإنها تعيد القيمة ‪.0‬‬
‫)‪template<class T> int IsGreater(T x,T y‬‬
‫)‪{ if (x>y‬‬
‫;‪return 1‬‬
‫‪else‬‬
‫;‪return 0‬‬
‫}‬

‫‪236‬‬
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
‫إن تعريف الدوال القالبية ال يختلف عن تعريف الدوال االعتيادية إال في أول جملة‬
‫أو ما يسمى بترويسة الدالة؛ إذ تبدأ الدالة القالبية بالكلمة ‪ template‬لإلشارة إلى أنها دالة‬
‫قالبية‪ ،‬ثم بقائمة من العوامل تبدأ بالرمز< و تنتهي بالرمز > ‪ .‬وفي مثالنا أعاله هنالك‬
‫عامل واحد لهذه الدالة القالبية وهو‪ class T‬وتعني الكلمة ‪ class‬أن نوع العامل ‪ T‬هو‬
‫اسم لنوع من أنواع البيانات مثل ‪ int‬و ‪ float‬و… إلخ‪ .‬وقد استخدم هذا العامل في‬
‫تعريف ‪ x‬و ‪ y‬إذ إنها من النوع ‪. T‬‬
‫أما طريقة استدعاء الدالة القالبية فال تختلف عن طريقة استدعاء الدالة االعتيادية‪،‬‬
‫واملثال التالي يوضح كيفية استدعاء الدالية القالبية أعاله للمقارنة بني رقمني صحيحني‪.‬‬
‫)(‪main‬‬
‫;‪{ int a=2,b=1‬‬
‫;)‪cout<<IsGreater(a,b‬‬
‫}‬

‫‪ 2.3‬استخدام الدوال القالبية‬


‫نستطيع استخدام هذه الدالة القالبية للمقارنة بني أي قيمتني مهما كان نوعهما شريطة‬
‫أن تكون العملية> معرفة لهذا النوع من البيانات‪ .‬فمثالً‪ ،‬لو أردنا استخدام الدالة أعاله‬
‫للمقارنة بني موظفني من الصنف ‪ employee‬فال بد من أن نعرف أو ً‬
‫ال العملية > لهذا‬
‫الصنف كما درسنا في الوحدة السابقة‪.‬‬
‫وعلى سبيل املثال آخر على الدوال القالبية سنعرف الدالة القالبية ‪ sort‬التي تقوم‬
‫بترتيب مصفوفة من الكائنات من النوع العام ‪ ،T‬حيث ‪ T‬هو عامل يحدد وفق احلاجة‪،‬‬
‫فقد يكون النوع ‪ student‬أو النوع ‪employee‬أو ‪ int‬أو ‪ … float‬إلخ‪ .‬تستخدم هذه‬
‫الدالة خوارزمية الفرز االنتقائي ‪ selection sort‬التي سبق مناقشتها‪.‬‬
‫)‪template<class T> void sort(T* st, int m‬‬
‫)‪{ for(int i=0;i<m-1;i++‬‬
‫;‪{ int pos=i‬‬
‫;]‪T min=st[i‬‬
‫)‪for(int j=i+1; j<m;j++‬‬
‫)‪if (st[j]<min‬‬
‫;]‪{ min=st[j‬‬
‫;‪ pos=j‬‬

‫‪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;
}

template<class T> void sort(T* st, int m)


{ for(int i=0;i<m-1;i++)
{ int pos=i;
T min=st[i];
for(int j=i+1; j<m;j++)
if (st[j]<min)
{ min=st[j];
pos=j;
}
T a=st[pos];
st[pos]=st[i];
st[i]=a;
} //for i
} //sort

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++‬‬
‫}‬

‫‪ ،Stlist .2‬تقوم هذه بعرض البيانات اخلاصة بكل طالب في الشعبة‪.‬‬


‫قد تختلف البيانات اخلاصة بالطالب التي نود عرضها حسب نوع الطالب؛ فإذا كان‬
‫من النوع ‪( PostGrad‬طالب دراسات عليا‪ ،‬على سبيل املثال) فقد نحتاج إلى طباعة‬
‫اسم املشرف‪ .‬وهذا األمر غير وارد لطالب من نوع النوع ‪ .student‬وحلل هذه املشكلة‬
‫علينا تعريف العملية << للصنف‪ PostGrad‬والعملية نفسها للصنف ‪student‬؛ بحيث‬
‫تقوم هذه العملية بطباعة البيانات املطلوبة لكل نوع من الطلبة‪ .‬راجع‪ ،‬عزيزي الدارس‪،‬‬
‫الوحدة السابقة ملعرفة كيف تعرف العمليات‪.‬‬
‫>‪template<class T, int MaxSize‬‬
‫)(‪void section<T,MaxSize>::Stlist‬‬
‫)‪{ for(int i=0;i<size;i++‬‬
‫} ;]‪cout<<sec[i‬‬

‫‪ ،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;
}

.‫ تقوم هذه الدالة بشطب طالب ذي رقم معني من الشعبة‬،StDelete .4


template<class T, int MaxSize>
int section<T,MaxSize>::StDelete(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;
}

‫ تقوم هذه الدالة بالبحث عن طالب ذي رقم معني وعرض البيانات‬،StRetrieve 5.


<< ‫ أن هذه الدالة أيض ًا تفترض أن العملية‬،‫ عزيزي الدارس‬،‫ الحظ‬.‫اخلاصة به‬
.‫معرفة لنوع الطلبة‬
template<class T, int MaxSize>
void section<T,MaxSize>::StRetrieve(long stno)
{int i=bsearch(stno);
if(i>-1)
cout<<sec[i];

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++‬‬

‫‪ 1.4‬مفهوم تعدد األوجه وخواصه‬


‫تتكون الكلمة اليونانية ‪ Polymorphism‬من مقطعني األول ‪ poly‬ويعني متعدد‪،‬‬
‫والثاني ‪ morphism‬ويعني األوجه أو الصيغ‪.‬‬
‫وتعدد األوجه من مزايا لغة ‪ C++‬املهمة جداً‪ ،‬إذ إنها متنحنا القدرة على تأجيل حتديد‬
‫هوية الدالة املراد استدعاؤها من وقت الترجمة ‪ compile time‬كما هي العادة‪ ،‬إلى وقت‬
‫تنفيذ البرنامج ‪.run time‬‬
‫كما متكننا لغة ‪ C++‬من وضع عنوان كائن من صنف مشتق في متغير مؤشر معرف‬
‫على أنه مؤشر للصنف األساس‪ .‬فمثالً‪ ،‬لو عرفنا الصنف األساس ‪ person‬واألصناف‬
‫املشتقة منه ‪ student‬و ‪ ،professor‬وعرفنا املتغير املؤشر ‪ p‬على أنه مؤشر للصنف‬
‫األساس ‪ person‬كما يلي‪:‬‬
‫;‪person *p‬‬
‫فإننا نستطيع تخزين عنوان كائن من الصنف ‪ student‬أو الصنف‪ professor‬في‬
‫املتغير ‪ . p‬على سبيل املثال‪ ،‬اجلمل التالية تنشئ كائن ًا من الصنف ‪ professor‬وتخزن‬
‫عنوانه في املتغير املؤشر ‪. p‬‬
‫;‪professor p1‬‬
‫;‪p=&p1‬‬
‫هاتان اخلاصيتان معا متكنانا من كتابة برامج من الصعب كتابتها في لغات البرمجة‬
‫اإلجرائية مثالً‪ ،‬لغة باسكال‪ .‬فمثال لو عرفنا املصفوفة ‪ persPtr‬من املؤشرات للصنف‬
‫األساس ‪ person‬وخزنا فيها عناوين كائنات مختلفة من األصناف ‪ student‬و ‪،professor‬‬
‫نستطيع‪ ،‬عندئذ‪ ،‬أن نقوم بإدخال البيانات اخلاصة بكل من هذه الكائنات مستخدمني‬
‫الدالة )(‪ getData‬اخلاصة بكل من هذه األصناف كما يلي‪:‬‬
‫‪persPtr[n++]->getData(); //get data for person‬‬

‫‪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; }
};

‫ ومن‬person ‫والدالة الرئيسة التالية تقوم بتعريف مصفوفة من املؤشرات للصنف‬


.‫ تقوم بإنشاء مجموعة من الكائنات بشكل ديناميكي وتخزن عناوينها في املصفوفة‬،‫ثم‬
.‫ثم تستخدم خاصية تعدد األوجه الدخال البيانات اخلاصة بهذه الكائنات‬
int main()

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‬‬

‫الشكل (‪ : )5‬ناجت تنفيذ برنامج ميثل تعدد األوجه في لغة ‪C++‬‬

‫ ‬
‫تدريب (‪)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()
{

// declare an array of pointers to the base class property


property *A[10];
int choice;
for(int i=0;i<10;i++)
{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 proper-
ties «
<<endl;
cin>>choice;
switch(choice){
case 1:
A[i]=new flat;
break;
case 2:
A[i]=new house;
break;
case 3:
A[i]=new Multi_Building;
break;
default: cout<<»invalid choice try again «;
256
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
‫‪} //switch‬‬
‫‪} // for i‬‬
‫‪// use polymorphism to print all data about all allocated objects‬‬
‫)‪for(int j=0;j<10;j++‬‬
‫;)(‪A[j]->print_info‬‬

‫‪} //main‬‬

‫تدريب (‪)7‬‬
‫قم‪ ،‬عزيزي الدارس‪ ،‬بدراسة البرنامج التالي وتنفيذه على احلاسوب‪ ,‬البرنامج‬
‫يقوم بإيجاد مساحة الصنف مثلث ‪ CTriangle‬والصنف مستطيل ‪ CRectangle‬املشتق‬
‫من الشكل‪ CPolygon‬كما هو موضح في البرنامج‪ .‬أعد كتابة البرنامج باستخدام الدالة‬
‫الوهمية إليجاد مساحة أي من األشكال السابقة‪ .‬الحظ أنك يجب أن تستخدم الدالة‬
‫الوهمية ‪ )(area‬والتي تعيد مساحة الكائن‪ .‬يجب أن تكون هذه الدالة وهمية‪ ،‬ألنها‬
‫حتسب املساحة بطريقة مختلفة حسب نوعية الكائن‪.‬‬
‫>‪#include <iostream‬‬
‫{ ‪class CPolygon‬‬
‫‪protected:‬‬
‫;‪int width, height‬‬
‫‪public:‬‬

‫)‪void set_values (int a, int b‬‬


‫} ;‪{ width=a; height=b‬‬
‫;}‬

‫{ ‪class CRectangle: public CPolygon‬‬


‫‪public:‬‬
‫)( ‪int area‬‬
‫} ;)‪{ return (width * height‬‬
‫;}‬

‫‪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‬‬
‫}‬

‫‪ 2.4‬استخدامات تعدد األوجه‬


‫تعدد األوجه في لغات البرمجة يعني أن بعض العمليات أو الرموز أو األشياء‬
‫تتصرف بطريقة مختلفة في سياقات مختلفة‪ .‬على سبيل املثال‪ ،‬إشارة عملية اجلمع ‪+‬‬
‫(زائد) تعمل أو ميكن استخدامها للقيام بالعمليات التالية‪:‬‬
‫‪ --< 5 + 4‬عملية جمع لألعداد الصحيحة ‪.integer‬‬
‫‪ --< 2.0 + 3.14‬عملية جمع لألعداد احلقيقة (النقطة العائمة)‪.‬‬
‫‪ --< ”s1 + “qou‬عملية إضافة سلسلة رمزية إلى أخرى‪.‬‬
‫في لغة ‪ ،C++‬هذا النوع من تعدد األشكال يسمى ‪.Overloading‬‬

‫مفهوم تعدد األوجه من أهم مفاهيم البرمجة الكينونية ‪Object-Oriented‬‬

‫‪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‬‬
‫;}‬

‫‪class Rectangle:public Shape‬‬


‫{‬
‫‪public:‬‬
‫};»‪void draw(){cout<<»Rectangle Drawn‬‬

‫‪259‬‬
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
private:
int height;
int width;
};

class Circle:public Shape


{
public:
void draw(){cout<<»Circle Drawn»;};
private:
int radius;
};

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‬‬

‫وفي احلالة الثانية فإن الشكل سيصبح دائرة‪:‬‬


‫;‪shape=new Circle‬‬

‫اآلن انظر إلى تعدد الشكل هنا ‪:‬‬


‫;)(‪shape->draw‬‬

‫فقبل تنفيذ البرنامج‪ ،‬ال نستطيع معرفة النتيجة من استدعاء الدالة‪ ، 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;
}

istream& operator>>(istream& c, student& e)


{
cout<<»enter student›s number»;
c>>e.stno;
cout<<»enter student›s name «;
c>>e.stName;
266
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
cout<<»enter no. of courses taken by the student»;
c>>e.csno;
for(int i=0;i<e.csno;i++)
{cout<<”enter grade no.”<<i+1;
c>>e.courses[i];
}
return c;
}
ostream& operator<<(ostream& c, student e)
{
cout<<”student’s number:”;
c<<e.stno<<endl;
cout<<”student’s name ”;
c<<e.stName<<endl;
cout<<”no. of courses taken by the student:”;
c<<e.csno<<endl;
c<<e.average()<<endl;
c<<”----------”<<endl;
return c;
}
template<class T> void print_list(T* L, int n)
{for(int i=0;i<n;i++)
cout<<L[i];
}
main()
{ student stu[100];
int n;
cout<<”Enter no. of students”;
cin>> n;
for(int i=0;i<n;i++)
cin>>stu[i];
print_list(stu,n);
employee emp[100];
cout<<”enter number of employees”<<endl;
267
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
cin>>n;
for(i=0;i<n;i++)
cin>>emp[i];
print_list(emp,n);
}

)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];
}
}

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);
269
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
void Stlist();
void StRetrieve(long stno);
};
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++;
// shift elements one position up
for(int i=size-1;i>=pos;i--)
sec[i+1]=sec[i];
sec[pos]=s;
size++;
}
template<class T, int MaxSize>
void section<T,MaxSize>::Stlist()
{ for(int i=0;i<size;i++)
cout<<sec[i];
}
// binary search
template<class T, int MaxSize>
int section<T,MaxSize>::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;
270
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
else
hi = mid;
} //while
return -1;
}
template<class T, int MaxSize>
int section<T,MaxSize>::StDelete(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;
}
template<class T, int MaxSize>
void section<T,MaxSize>::StRetrieve(long stno)
{int i=bsearch(stno);
if(i>-1)
cout<<sec[i];
else
cout<<”Student no ”<<stno<<” was not found \n”;
}

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;

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];
273
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
}
}
template<class T> class section{
T *sec;
int MaxSize;
int size;
int bsearch(long stno);
void expand();
void shrink();
public:
section(){sec=new T[1];
MaxSize=2;
size=0;}
void StAdd(T s);
int StDelete(long stno);
void Stlist();
void StRetrieve(long stno);
};
template<class T>
void section<T>::expand()
{student *p=new student[2*MaxSize];
for(int i=0;i<size;i++)
p[i]=sec[i];
delete sec;
sec=p;
MaxSize *=2;
}
template<class T>
void section<T>::shrink()
{student *p=new student[MaxSize/2];
for(int i=0;i<size;i++)
p[i]=sec[i];
delete sec;
sec=p;
274
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
MaxSize /=2;
}
template<class T>
void section<T>::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++;
}
template<class T>
void section<T>::Stlist()
{ for(int i=0;i<size;i++)
cout<<sec[i].get_stname()<<” ”<<sec[i].get_stno()<<” ”<<sec[i].
average()<<endl;
}
// binary search
template<class T>
int section<T>::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
275
‫األصناف املشتقة والقوالب‬ ‫الوحدة اخلامسة‬
if (sn>r)
lo=mid+1;
else
hi = mid;
} //while
return -1;
}
template<class T>
int section<T>::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;
}
template<class T>
void section<T>::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<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;
}

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;}
virtual void print_info();
virtual double get_area();
virtual void read_info();
};

house::house(float P,float Rent,float B,float G)


: property(P,Rent)
{BArea=B;
GArea=G;}
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;
};

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;
};

class Multi_Building: public flat


{private:
int NoStory;
int NoFlats;
public:
Multi_Building(float P=20000.0,float Rent=100.0,float
A=120.0,int S=1,int F=2);
float area(){return NoStory*NoFlats*Area;}
void print_info();

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;
}

void sort(property** st, int m)


{ for(int i=0;i<m-1;i++)
{ int pos=i;
double min=st[i]->get_area();
for(int j=i+1; j<m;j++)
if (st[j]->get_area()<min)
{min=st[j]->get_area();
pos=j;}
property* a=st[pos];
st[pos]=st[i];

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); }
};

class CRectangle: public CPolygon {


public:
int area ()
{ return (width * height); }
};

class CTriangle: public CPolygon {


public:
int area ()
{ return (width * height / 2); }
};

int main () {
CRectangle rect;
CTriangle trgl;
CPolygon poly;
CPolygon * ppoly1 = &rect;
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

1. Bronson, Gary, “Program Development and Design Using


C++”, PWS Publishing Company, 1997.
2. Cannon, Scott, “Understanding Programming: An Introduc-
tion Using C++”, West Publishing Company, 1997.
3. Davis S.,R., “C++ for Dummies”, 5th Edition, Wiley Publish-
er,2005.
4. Hekmat Sh., “C++ Essentials”, PragSoft Corporation, 2005.
5. Hubbard J., “Schaum’s Outline of The Ory and Problems of
Programming with C++”, Second Edition, SCHAUM’S OUT-
LINE SERIES, McGRAW-HILL, 2000.
6. Kent, J., “C++ Demystified: A Self-Teaching Guide”, McGraw-
Hill/Osborne © 2004.
7. Lippman S. B., Lajoie J., Moo B. E., “C++ Primer”, Fourth
Edition, Addison Wesley Professional, 2005.
8. Meyers S., “Effective C++”, 55 Specific Ways to Improve Your
Programs and Designs, Third Edition, Addison Wesley Pro-
fessional, 2005
8. Savitch, Walter, “Problem Solving with C++”, Addison-Wesley
Publishing Company, Inc 1996.
8. Schildt H., “C/C++ Programmer’s Reference”, Third Edition,
McGraw-Hill/Osborne, 2003
9. Schildt H.,”C++ from the Ground Up”, Third Edition, Mc-
Graw-Hill/Osborne, 2003
10. Schildt H., “C++: The Complete Reference”, Third Edition,
McGraw-Hill Osborne, 1998.

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‬القراءات املساعدة‬

‫لقد قدمنا الكثير من املفاهيم واملواضيع األساسية املرتبطة بامللفات في هذه‬


‫الوحدة‪ .‬ولذلك من املفيد جدا‪ ،‬عزيزي الدارس‪ ،‬أن تعود إلى املصادر التالية ملا‬
‫تتضمنه من مادة علمية إضافية وأمثلة وإيضاحات ستساعدك على استيعاب املادة‬
‫التي تضمنتها هذه الوحدة‪ ،‬وهذه املصادر هي‪:‬‬
‫‪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 Funda-‬‬
‫‪mentals”, Dreamtech Press, 2004.‬‬
‫‪4.‬‬ ‫‪Skansholm, J. “C++ From the Beginning”, Addison Wesley, 2nd Edi-‬‬
‫‪tion, 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.‬‬

‫‪294‬‬
‫معاجلة امللفات بلغة ‪C++‬‬ ‫الوحدة السادسة‬
‫‪ 5.1‬ما حتتاج إليه لدراسة الوحدة‬
‫لقد تضمنت هذه الوحدة العديد من األمثلة والبرامج‪ ،‬وبذلك فإنك ستحتاج‪،‬‬
‫عزيزي الدارس‪ ،‬إلى استخدام جهاز احلاسوب للقيام بكتابة البرامج وتنفيذها‪:‬‬
‫فاملادة املعروضة في هذه الوحدة ال تعطي الفائدة املرجوة منها إال بتطبيق البرامج‬
‫التوضيحية ال���واردة فيها‪ .‬وم��ن هنا‪ ،‬من املهم وج��ود جهاز احلاسوب بجانبك‬
‫إضافة إلى مترجم لغة س��ي‪ ++‬وبعض القرطاسية كقلم رص��اص وورق أبيض‬
‫لإلجابة عن األسئلة والتدريبات املعطاة في ثنايا الوحدة ولكتابة مسودات بعض‬
‫البرامج الالزمة‪ .‬وباإلضافة إلى ذلك‪ ،‬فإنك ستحتاج‪ ،‬عزيزي ال��دارس‪ ،‬إلى‬
‫املكان الهادئ واملناسب‪.‬‬

‫‪295‬‬
‫معاجلة امللفات بلغة ‪C++‬‬ ‫الوحدة السادسة‬
‫‪ .2‬هيكلية البيانات ‪Data Hierarchy‬‬
‫إن البيانات التي تتعامل معها احلواسيب هي مجموعة من األصفار والواحدات التي‬
‫البايت‬
‫تسمى اخلانات الثنائية)‪ . (Bits‬إن مجموعة مكونة من ثماني خانات ثنائية تشكل ْ‬
‫الذي بدوره ميثل رمز ًا في مجموعة الرموز املسمى اآلسكي )‪ (ASCII‬حيث يحتل كل رمز‬
‫في الذاكرة الرئيسة للحاسوب بايت ًا واحداً‪ .‬تشكل مجموعة البايتات حقو ً‬
‫ال )‪(Fields‬معينة‬
‫يكون لها معنى معني حسب طبيعة التطبيق والبرنامج‪ .‬فمثالً‪ ،‬مجموعة الرموز املكونة‬
‫من أحرف أبجدية صغيرة وكبيرة ميكن أن متثل اسم دارس في جامعة القدس املفتوحة‪.‬‬
‫إن مجموعة احلقول املترابطة ترابط ًا منطقي ًا ميكن أن متثل سجالً؛ فمثالً‪ ،‬ميكن اعتبار اسم‬
‫الدارس‪ ،‬رقمه‪ ،‬عنوانه‪ ،‬تاريخ ميالده سجالً‪ .‬وفي أغلب احلاالت‪ ،‬يكون هناك حقل‬
‫خاص يأخذ قيم ًا منفردة للسجالت املختلفة يسمى مفتاح السجل(‪)Record Key‬؛ بحيث‬
‫ال يكون هناك أي سجلني لهما القيمة نفسها لهذا املفتاح مثل حقل رقم الدارس الذي ميكن‬
‫اعتباره مفتاحا لسجل الدارس‪ ،‬حيث إن لكل دارس رقم ًا خاص ًا يختلف عن أرقام بقية‬
‫الدارسني‪ .‬إن مجموعة سجالت الدارسني ميكن أن متثل ملفاً‪ .‬يوضح الشكل (‪ )1‬رسم ًا‬
‫توضيحي ًا لهيكلية البيانات‪ .‬وبصورة عامة‪ ،‬فإن األنظمة املختلفة تتعامل مع مجموعة‬
‫من امللفات؛ فعلى سبيل املثال‪ ،‬فإن نظام التسجيل في جامعة القدس املفتوحة ميكن أن‬
‫يشتمل على ملف خاص بالدارسني‪ ،‬وملف خاص باملشرفني األكادمييني‪ ،‬وملف خاص‬
‫باملقررات‪ ،... ،‬وهكذا‪ .‬فإن مجموعة هذه امللفات املترابطة منطقي ًا ميثل قاعدة بيانات‬
‫)‪ (Database‬حتتاج إلى مجموعة من برامج النظم إلنشائها ومعاجلتها تسمى نظام إدارة‬
‫قواعد البيانات )‪.(Database Management System‬‬

‫‪296‬‬
‫معاجلة امللفات بلغة ‪C++‬‬ ‫الوحدة السادسة‬
‫الشكل (‪ :)1‬هيكلية البيانات‬

‫أسئلة التقومي الذاتي (‪)1‬‬


‫‪ .1‬وضح بالرسم مكان امللفات ضمن هيكلية البيانات‪.‬‬
‫‪ .2‬ما املقصود مبفتاح السجل؟‬

‫‪297‬‬
‫معاجلة امللفات بلغة ‪C++‬‬ ‫الوحدة السادسة‬
‫‪ .3‬تنظيم امللفات ‪File Organization‬‬
‫هناك عدة طرق مختلفة لتنظيم السجالت في امللفات تعتمد بصورة أساسية على‬
‫كيفية تخزين هذه السجالت في امللفات واحلاجة السترجاعها بصورة معينة‪ .‬هناك‬
‫طريقتان أساسيتان في تنظيم امللفات‪:‬‬
‫‪ )1‬التنظيم التتابعي للملفات‬
‫يتم‪ ،‬في هذه الطريقة تخزين السجالت بالترتيب حسب قيم مفتاح السجل‪ ،‬فعلى‬
‫سبيل املثال‪ ،‬ميكن أن تتم عملية تخزين سجالت الدارسني في جامعة القدس املفتوحة‬
‫حسب رقم الدارس بحيث يحتوي السجل األول على سجل الدارس الذي يحمل أصغر‬
‫رقم ومن ثم‪ ،‬الذي يحمل الرقم الذي يليه وهكذا‪ .‬إن امللف الذي يحتوي على سجالت‬
‫مخزنة بهذه الطريقة يسمى امللف التتابعي (‪ .)Sequential File‬إن من سيئات استخدام‬
‫امللفات التتابعية أنها تضع قيود ًا على كيفية استرجاع السجالت والوصول إليها؛ إذ‬
‫ال ميكن الوصول إلى السجل رقم ‪ n‬إال بعد الوصول إلى كل السجالت التي تسبقه‬
‫في التخزين‪ ،‬أي السجالت من ‪ 1‬وحتى ‪ ،n-1‬أي أن امللفات التتابعية غير مالئمة‬
‫للتطبيقات والبرامج التي حتتاج الوصول إلى سجل معني مباشرة‪.‬‬

‫مثال (‪)1‬‬
‫إذا كانت السجالت ‪ R1,R2,…Rn‬منظمة بصورة تتابعية كما يلي‪:‬‬

‫‪R1‬‬ ‫‪R2‬‬ ‫‪R3‬‬ ‫……‬ ‫‪R99‬‬ ‫‪R100‬‬ ‫……‬ ‫‪Rn‬‬

‫فإنه للوصول إل��ى ‪ R100‬يجب الوصول إل��ى جميع السجالت التي تسبق‬
‫‪( R100‬أي السجالت من ‪ R1‬إلى ‪.)R99‬‬

‫‪ )2‬التنظيم العشوائي للملفات‬


‫ميكن‪ ،‬في هذه الطريقة الوصول إلى سجل معني مباشرة وبصورة سريعة دون احلاجة‬
‫للبحث أو الوصول إلى السجالت التي سبقته في التخزين‪ .‬إن امللف الذي يحتوي على‬
‫‪298‬‬
‫معاجلة امللفات بلغة ‪C++‬‬ ‫الوحدة السادسة‬
‫سجالت ميكن الوصول إليها بصورة حلظية ومباشرة يسمى امللف العشوائي ‪(Random‬‬
‫)‪ .File‬إن معظم التطبيقات في احلياة العملية حتتاج للتعامل مع السجالت املنظمة‬
‫باستخدام التنظيم العشوائي وذلك للحاجة املاسة للوصول للبيانات والسجالت بصورة‬
‫سريعة جداً‪ ،‬دون إضاعة الوقت في املرور على السجالت التي تسبق السجل املطلوب كما‬
‫في حالة التنظيم التتابعي‪ ،‬فأنت‪ ،‬عزيزي الدارس‪ ،‬عندما تذهب إلى البنك لالستفسار‬
‫عن رصيد حسابك فإنك تتوقع أن يعلمك املوظف املعني مباشرة باملعلومات املطلوبة بعد‬
‫إدخاله رقم حسابك وال تقبل بحال من األحوال أن يخبرك أن عليك االنتظار حتى يتم‬
‫استعراض كل السجالت‪/‬احلسابات التي تسبق رقم حسابك‪.‬‬

‫مثال (‪)2‬‬
‫إذا كانت السجالت ‪ R1,R2,…Rn‬منظمة بصورة عشوائية كما يلي‪:‬‬

‫‪R1‬‬ ‫‪R2‬‬ ‫‪R3‬‬ ‫……‬ ‫‪R99‬‬ ‫‪R100‬‬ ‫……‬ ‫‪Rn‬‬

‫فإنه ميكن الوصول إلى ‪ R100‬دون احلاجة للوصول إلى جميع السجالت‬
‫التي تسبق ‪( R100‬أي السجالت من ‪ R1‬إلى ‪.)R99‬‬

‫أسئلة التقومي الذاتي (‪)2‬‬

‫‪ .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‬تستخدم هذه امللفات للحصول على األصناف التي يتم فتح‬
‫امللفات املختلفة من خالل إنشاء كائنات خاصة باالعتماد عليها‪ ،‬كما سيتم توضيح ذلك‬
‫في األقسام التالية من هذه الوحدة‪.‬‬

‫أسئلة التقومي الذاتي (‪)3‬‬

‫‪ .1‬ما العالقة بني امللفات والينابيع في لغة سي‪++‬؟‬

‫‪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‬‬

‫‪ 2.5‬القراءة من امللفات التتابعية‬


‫تتم عملية تخزين البيانات في امللفات ليتم االحتفاظ بها بصورة دائمة وليتم‬
‫استرجاعها وقراءتها عند احلاجة‪ .‬لقد أوضحنا‪ ،‬عزيزي الدارس‪ ،‬كيفية إنشاء امللفات‬
‫التتابعية في القسم السابق من هذه الوحدة‪ ،‬وفي هذا القسم نوضح كيفية قراءة البيانات‬
‫من امللفات التتابعية‪.‬‬
‫يوضح البرنامج التالي كيفية قراءة البيانات من ملف تتابعي خاص بالدارسني في‬
‫جامعة القدس املفتوحة‪ ،‬حيث تشتمل البيانات اخلاصة بكل دارس رقمه‪ ،‬اسمه‪ ،‬معدله‬
‫حيث ميكن اعتبار هذه احلقول بأنها متثل سجل الدارس ويكون رقم الدارس‪ ،‬في هذه‬
‫احلالة‪ ،‬هو مفتاح السجل‪.‬‬
‫>‪#include <iostream.h‬‬
‫>‪#include <fstream.h‬‬
‫>‪#include <stdlib.h‬‬

‫)‪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‬‬
‫}‬

‫;‪cout << «Student no, name, and Average» << endl‬‬


‫)‪while(inStudentFile >> sno >> sname >> saverage‬‬
‫{‬
‫;‪cout << sno <<› ‹ << sname << ‹ ‹ << saverage <<endl‬‬
‫}‬
‫;)(‪inStudentFile.close‬‬
‫;‪return 0‬‬
‫}‬

‫وكما تالحظ‪ ،‬عزيزي الدارس‪ ،‬فإن عملية فتح هذا امللف التتابعي قد متت من خالل‬
‫إنشاء كائن باسم ‪ 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‬ال تتم عملية كتابة السجل (أي يتم‬
‫حذفه)‪.‬‬

‫‪ .6‬امللفات العشوائية ‪Random Files‬‬


‫أوضحنا‪ ،‬عزيزي الدارس‪ ،‬في القسم السابق من هذه الوحدة‪ ،‬كيفية التعامل مع‬
‫امللفات التتابعية من حيث اإلنشاء والقراءة والتحديث‪ .‬إال أن هناك بعض احلاالت‬
‫التي نحتاج فيها للوصول إلى سجل معني داخل امللف بصورة مباشرة دون املرور أو‬
‫الوصول إلى السجالت التي سبقته‪ ،‬مما يستدعي استخدام طريقة ثانية في تنظيم امللفات‬
‫وهي الطريقة العشوائية‪ ،‬حيث متكننا هذه الطريقة من الوصول إلى سجل معني مباشرة‬
‫وبصورة سريعة دون احلاجة للبحث أو الوصول إلى السجالت التي سبقته في التخزين‪.‬‬
‫إن امللفات التي حتتوي على سجالت ميكن الوصول إليها بصورة حلظية ومباشرة تسمى‬
‫امللفات العشوائية (‪.)random files‬‬
‫وكما أوضحنا سابقا‪ ،‬فإن لغة سي‪ ++‬ال تفرض أي تركيب على امللفات‪ ،‬ولذلك‬
‫فإن البرامج التي حتتاج إلى امللفات العشوائية يجب أن تقوم بإنشائها والتعامل معها حسب‬
‫احلاجة‪ .‬إال أن أسهل طريقة إلنشاء امللفات هي افتراض أن جميع السجالت متساوية‬
‫في احلجم أو الطول‪ .‬إن أي تركيب بياني مكون من مجموعة من العناصر املتساوية في‬
‫احلجم أو الطول ميكن النظر إليه بطريقة مشابهة للطريقة التي يتم فيها النظر إلى املصفوفة‬
‫أحادية البعد حيث إن جميع العناصر لها احلجم نفسه أو الطول نفسه‪ .‬يوضح الشكل‬
‫(‪ )2‬الكيفية التي تنظر إليها لغة سي‪ ++‬إلى امللفات العشوائية على افتراض أن حجم كل‬
‫سجل هو ‪ 10‬بايتات‪.‬‬

‫‪307‬‬
‫معاجلة امللفات بلغة ‪C++‬‬ ‫الوحدة السادسة‬
‫‪0‬‬ ‫‪10‬‬ ‫‪20‬‬ ‫‪30‬‬ ‫‪40‬‬
‫‪10 bytes‬‬ ‫‪10 bytes‬‬ ‫‪10 bytes‬‬ ‫‪10 bytes‬‬ ‫‪10 bytes‬‬

‫الشكل (‪ :)2‬امللف العشوائي كما تنظر إليه لغة سي‪++‬‬

‫فكما تالحظ‪ ،‬عزيزي الدارس‪ ،‬لكل سجل طول ثابت هو ‪ 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‬‬
‫;}‬

‫وللكتابة في امللف العشوائي‪ ،‬يجب أن نقوم بكتابة مجموعة من السجالت الفارغة‬


‫فيه؛ حيث يجب أن تكون جميع هذه السجالت متساوية في احلجم حتى يتسنى لنا‬
‫االستفادة من إمكانية حتديد السجل املطلوب باالعتماد على العالقة التي وضحت‬
‫سابقا‪ .‬البرنامج التالي يقوم بكتابة مجموعة متساوية في احلجم ملجموعة من السجالت‬
‫الفارغة‪.‬‬
‫>‪#include <iostream.h‬‬
‫>‪#include <fstream.h‬‬
‫>‪#include <stdlib.h‬‬
‫;‪const int No_Of_Records =100‬‬

‫)‪int main(void‬‬
‫{‬
‫‪struct student‬‬
‫{‬
‫‪309‬‬
‫معاجلة امللفات بلغة ‪C++‬‬ ‫الوحدة السادسة‬
‫;‪int no‬‬
‫;]‪char name[32‬‬
‫;‪float average‬‬
‫;}‬

‫;}‪struct student blankrecord={0,»»,0‬‬

‫;)‪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‬‬

‫حيث ميثل املعامل الثاني للدالة ‪ ios::in|ios::out|ios::binary‬وذلك ليكون امللف‬


‫ملف ًا للقراءة ولذلك قمنا باستخدام ‪ ios::in‬والكتابة ولذلك قمنا باستخدام ‪.ios::out‬‬
‫كما أن عملية التخزين ستكون بالصورة الثنائية ولذلك قمنا باستخدام ‪.ios::binary‬‬
‫لقد قمنا باستخدام الرمز | ليدل على أننا نريد أن ندمج كل هذه املعامالت واستخدامها‬
‫في آن واحد معاً‪ .‬إن عملية الكتابة بالصورة الثنائية ضرورية جد ًا وذلك لتجنب املشاكل‬
‫واحلاالت التي وضحت سابق ًا فيما يتعلق بتعديل بعض البيانات‪ .‬فهنا‪ ،‬على سبيل‬
‫املثال‪ ،‬عند تعديل العدد ‪ 12‬ليصبح ‪ ،1234‬لن يحدث ذلك أي مشكلة ألن العددين‬

‫‪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;
};

struct student studentrecord;

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);
}

cout <<»\nEnter student no (1 to 100), any number else to end input: «;


cin >> sno;
while(sno >0 && sno<=No_Of_Records)
{
cout << «\nEnter student name: «;
cin >> sname;

cout << «\nEnter student average: «;


cin >> saverage;

studentrecord.no=sno;
strcpy(studentrecord.name,sname);
312
C++ ‫معاجلة امللفات بلغة‬ ‫الوحدة السادسة‬
‫;‪studentrecord.average=saverage‬‬

‫;))‪outStudentFile.seekp((studentrecord.no-1)* sizeof(struct student‬‬

‫‪outStudentFile.write(reinterpret_cast‬‬ ‫‪<const‬‬ ‫‪char‬‬ ‫>*‬


‫;))‪(&studentrecord),sizeof(struct student‬‬

‫‪cout <<»\n\nEnter student no (1 to 100), any number else to end‬‬


‫;« ‪input:‬‬
‫;‪cin >> sno‬‬

‫‪} // end while‬‬


‫;)(‪outStudentFile.close‬‬
‫;‪return 0‬‬
‫}‬

‫‪ 2.6‬القراءة من امللفات العشوائية‬


‫أما للقراءة من ملف عشوائي فيجب أن نقوم بفتحه باستخدام اجلملة التالية‪:‬‬
‫;)‪ifstream inStudentFile(«studrf.dat»,ios::in‬‬

‫التي تقوم بإنشاء كائن باسم ‪ inStudentFile‬حيث يوضح املعامل الثاني ‪ ios::in‬أن‬
‫هذا امللف سيتم فتحه للقراءة منه‪ .‬بعد ذلك‪ ،‬يجب التأكد من أن عملية فتح امللف قد‬
‫متت بنجاح‪ ،‬وإال يجب إيقاف البرنامج مباشرة ألننا لن نتمكن من قراءة أي سجل في‬
‫هذه احلالة‪.‬‬
‫ميكن استخدام الدالة املنتمية ‪ read‬للقراءة من موقع معني يتم حتديده باستخدام الدالة‬
‫املنتمية ‪ .seekg‬ولتحدد موقع السجل املنوي قراءته‪ ،‬يجب في البداية أن نقوم بقراءة‬
‫قيمة مفتاح السجل أي رقم الدارس ومن ثم استخدام العالقة التي مت توضيحها سابق ًا‬
‫لتقوم الدالة ‪ seekg‬باستخدام القيمة الناجتة من هذه العالقة في حتديد املوقع الفعلي الذي‬
‫ستتم منه قراءة السجل املطلوب‪.‬‬
‫البرنامج التالي يقوم بقراءة عدد من السجالت من ملف عشوائي باستخدام الدالة‬
‫‪ read‬والدالة ‪ seekg‬كما أوضحنا سابقاً‪.‬‬

‫‪313‬‬
‫معاجلة امللفات بلغة ‪C++‬‬ ‫الوحدة السادسة‬
#include <iostream.h>
#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;
};

struct student studentrecord;

int sno;

ifstream inStudentFile(«studrf.dat»,ios::in);
if(!inStudentFile)
{
cerr << «File could not be opened» << endl;
exit(1);
}

cout <<»\nEnter student no (1 to 100), any number else to end display-


ing records: «;
cin >> sno;
while (sno >0 && sno<=No_Of_Records)
{

inStudentFile.seekg((sno-1)* sizeof(struct student));


inStudentFile.read(reinterpret_cast <char *>
(&studentrecord),sizeof(struct student));

314
C++ ‫معاجلة امللفات بلغة‬ ‫الوحدة السادسة‬
‫;‪cout << «\nStudent Data for student no « << studentrecord.no‬‬
‫;‪cout << «\nName: « << studentrecord.name‬‬
‫;‪cout << «\nAverage: « << studentrecord.average‬‬

‫‪cout <<»\n\nEnter student no (1 to 100), any number else to end‬‬


‫;« ‪displaying records:‬‬
‫;‪cin >> sno‬‬

‫‪} // end while‬‬


‫;)(‪inStudentFile.close‬‬
‫;‪return 0‬‬
‫}‬

‫أسئلة التقومي الذاتي (‪)4‬‬


‫‪ .1‬وضح بالرسم شكل امللف العشوائي املكون من ‪ 10‬سجالت حجم كل منها‬
‫‪ 50‬بايتاً‪.‬‬
‫‪ .2‬إذا كان حجم السجل ‪ 100‬بايت‪ ،‬فما هو موقع السجل ذو املفتاح ‪9‬؟‬

‫‪ 3.6‬حتديث امللفات العشوائية‬


‫إن عملية حتديث امللفات العشوائية‪ ،‬عزيزي الدارس‪ ،‬أسهل بكثير من عملية حتديث‬
‫امللفات التتابعية ألن جميع السجالت في امللفات العشوائية تكون بالطول نفسه أو احلجم‬
‫نفسه وأن إجراء أي عملية حتديث على حقل معني في السجل سيؤدي إلى كتابة العدد‬
‫نفسه من البايتات نتيجة تخزين البيانات بالصورة الثنائية‪ .‬كما أن عملية إضافة سجل‬
‫جديد ستتم عبر استخدام الدالة املنتمية ‪ seekp‬وذلك لتحديد املوقع املناسب لتخزين‬
‫هذا السجل دون أن يؤثر ذلك على السجالت األخرى في امللف العشوائي‪ .‬أما عملية‬
‫احلذف لسجل معني فيمكن أن تتم بكتابة البيانات األولية املستخدمة في عملية إنشاء‬
‫السجل الفارغ كما مت توضيح ذلك سابقاً‪ .‬البرنامج التالي يقوم بتحديث سجل الطالب‬
‫بعد إدخال رقمه ومن ثم‪ ،‬تتم عملية التحديث على بقية البيانات‪.‬‬
‫>‪#include <iostream.h‬‬

‫‪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;
};

struct student studentrecord;

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);
}

cout <<»\nEnter student no (1 to 100) whose record to be updated, any


number else to end input: «;
cin >> sno;
while(sno >0 && sno<=No_Of_Records)
{
cout << «\nEnter new student name: «;
cin >> sname;
316
C++ ‫معاجلة امللفات بلغة‬ ‫الوحدة السادسة‬
cout << «\nEnter new student average: «;
cin >> saverage;

studentrecord.no=sno;
strcpy(studentrecord.name,sname);
studentrecord.average=saverage;

outStudentFile.seekp((studentrecord.no-1)* sizeof(struct student));

outStudentFile.write(reinterpret_cast <const char *>


(&studentrecord),sizeof(struct student));

cout <<»\nEnter student no (1 to 100) whose record to be updated,


any number else to end input: «;
cin >> sno;

} // end while
outStudentFile.close();
return 0;
}

)5( ‫أسئلة التقومي الذاتي‬


.‫ وضح كيف تتم عملية البحث في امللفات التتابعية‬.1
.‫ وضح كيف تتم عملية البحث في امللفات العشوائية‬.2

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);
}

cout <<»\nEnter student no (1 to 100), any number else to end input: «;


cin >> sno;
while(sno >0 && sno<=No_Of_Objects)
{
cout << «\nEnter student name: «;
319
C++ ‫معاجلة امللفات بلغة‬ ‫الوحدة السادسة‬
cin >> sname;

cout << «\nEnter student average: «;


cin >> saverage;

studentobject.no=sno;
strcpy(studentobject.name,sname);
studentobject.average=saverage;

outStudentFile.seekp((studentobject.no-1)* sizeof(Student));

outStudentFile.write(reinterpret_cast <const char *> (&studentobje


ct),sizeof(Student));

cout <<»\n\nEnter student no (1 to 100), any number else to end


input: «;
cin >> sno;

} // 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);
}

cout <<»\nEnter student no (1 to 100), any number else to end display-


ing objects: «;
cin >> sno;
while (sno >0 && sno<=No_Of_Objects)
{

321
C++ ‫معاجلة امللفات بلغة‬ ‫الوحدة السادسة‬
‫;))‪inStudentFile.seekg((sno-1)* sizeof(Student‬‬

‫‪inStudentFile.read(reinterpret_cast <char *> (&studentobject),size‬‬


‫;))‪of(Student‬‬

‫;‪cout << «\nStudent Data for student no « << studentobject.no‬‬


‫;‪cout << «\nName: « << studentobject.name‬‬
‫;‪cout << «\nAverage: « << studentobject.average‬‬

‫‪cout <<»\n\nEnter student no (1 to 100), any number else to end‬‬


‫;« ‪displaying objects:‬‬
‫;‪cin >> sno‬‬

‫‪} // end while‬‬


‫;)(‪inStudentFile.close‬‬
‫;‪return 0‬‬
‫}‬

‫وكما تالحظ‪ ،‬عزيزي الدارس‪ ،‬فقد متت عملية فتح امللف والتعامل معه باألسلوب‬
‫نفسه وبالطريقة نفسها التي تعاملنا بها مع السجالت‪ ،‬والفارق الوحيد أننا اآلن نقوم‬
‫بقراءة الكائن من امللف العشوائي بعد حتديد موقع تخزينه مستخدمني الدالة ‪،seekg‬‬
‫باستخدام الدالة ‪ .read‬أما لعرض محتويات أو مكونات هذا الكائن‪ ،‬فإننا نقوم بعرض‬
‫محتويات أعضاء البيانات التي يحتويها هذا الكائن‪.‬‬

‫أسئلة التقومي الذاتي (‪)6‬‬


‫‪ .1‬هل هناك تشابه في عملية التعامل مع األصناف والسجالت في قراءة امللفات‬
‫العشوائية؟‬
‫‪ .2‬هل هناك أفضلية معينة في استخدام األصناف على استخدام السجالت؟‬

‫‪322‬‬
‫معاجلة امللفات بلغة ‪C++‬‬ ‫الوحدة السادسة‬
‫‪ .8‬اخلالصة‬
‫ناقشنا‪ ،‬عزيزي الدارس‪ ،‬في هذه الوحدة معاجلة امللفات بلغة ‪ ++C‬وأهم‬
‫العمليات الضرورية عليها‪ ،‬حيث عرضنا هيكلية البيانات وال��ط��رق املختلفة‬
‫لتنظيم امللفات‪ .‬كما أوضحنا املفاهيم املتعلقة بامللفات والينابيع وكيفية إنشاء‬
‫امللفات التتابعية وقراءتها وحتديثها وكذلك كيفية إنشاء امللفات العشوائية وقراءتها‬
‫وحتديثها‪ .‬وأخير ًا ناقشنا كيفية إدخال الكائنات وإخراجها من امللفات‪.‬‬

‫‪ .9‬إجابات التدريبات‬
‫تدريب (‪)1‬‬
‫أ‪ -‬البايت رقم ‪ n‬من امللف‪.‬‬
‫;)‪inEmployeeFile.seekg(n‬‬

‫ب‪ -‬البايت رقم ‪ n‬نسبة إلى املوقع احلالي‪.‬‬


‫;)‪inEmployeeFile.seekg(n,ios::cur‬‬

‫تدريب (‪)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++ ‫معاجلة امللفات بلغة‬ ‫الوحدة السادسة‬

You might also like