540 likes | 689 Views
C++ Programming. L4 . Functions. عندما نقوم بمعالجة مهمة ما نقوم بتقسيمها إلى مهام جزئية كل مهمة يقوم بتنفيذها تابع ما. functions :. أي تابع بلغة الـ C++ يتألف من قسمين أساسيين : ترويسة التابع function header : وهي توجد في بداية التابع وتتألف من ثلاث أجزاء :
E N D
C++ Programming L4 . Functions
عندما نقوم بمعالجة مهمة ما نقوم بتقسيمها إلى مهام جزئية كل مهمة يقوم بتنفيذها تابع ما
functions : أي تابع بلغة الـ C++ يتألف من قسمين أساسيين : • ترويسة التابع function header : وهي توجد في بداية التابع وتتألف من ثلاث أجزاء : • Return Type of The Function وهذا الجزء يحدد النمط الذي يرده التابع . • Name of The Function وهذا الجزء يحدد اسم التابع . • Types and Names of Parameters وهذا الجزء يحدد أسماء وأنماط متحولات دخل التابع. • جسم التابع Body : وهذا الجزء هو الذي سيعبر عن وظيفة التابع بحيث يعالج الدخل ويقوم بإعطاء خرج يتوافق مع مهمة التابع المكتوب .
functions : يوجد طريقتين لكتابة تابع بلغة c++ هما : • الطريقة الأولى : باستخدام الـ function prototype. • نحتاج إلى function prototype(function declaration) • نحتاج طبعاً إلى function definition . • الطريقة الثانية : باستخدام كتابة التابع بشكل كامل قبل أماكن استدعاءه. • نحتاج طبعاً إلى function definition .
functions : first way : يوجد طريقتين لكتابة تابع بلغة c++ هما : • الطريقة الأولى : باستخدام الـ function prototype . • يسمى الشكل السابق بـ prototype أو declaration للتابع ، وينتهي بفاصلة منقوطة. • يتم وضعه في الـ global section قبل أماكن الاستدعاء ولمرة واحدة . • ومن بعدها يتم وضع الـتابع بشكل كامل function definition في أي مكان يلي الـ prototype .
functions : first way : prototype calling Header + body
functions : first way : • وهذه الطريقة صحيحة أيضاً إذ أننا نقوم باخبار المترجم في سطر الـ prototype أنه لدينا تابع اسمه add يأخذ متحولين كلاهما من النمط int (لا يهمني اسمهم هنا طالما لا يتم استخدامهما أي لا يوجد جسم للتابع). • نهتم باسماء المتحول عند كتابة الـ function definition. prototype calling Header + body
functions : first way : • هنا سيرى المترجم استدعاء تابع غير معرف عنده . calling Header + body
functions : first way : • إذاً في هذه الطريقة يتم في البداية تعريف الـ prototype للتابع ونضعه في الأعلى فوق الـ main حتى يتم رؤيته من جميع التوابع الأخرى . • ثم يتم تعريف كامل التابع function definition (الترويسة + جسم التابع) في أي مكان يلي الـ prototype . prototype calling Header + body
functions : second way : يوجد طريقتين لكتابة تابع بلغة c++ هما : • الطريقة الثانية: باستخدام الطريقة المألوفة . • يتم وضع الـتابع بشكل كامل function definition في أي مكان ضمن الـ global section وقبل مكان الاستدعاء ، كي يتم رؤيته .
functions : second way : • يتم وضع الـتابع بشكل كامل في أي مكان ضمن الـ global section وقبل مكان الاستدعاء ، كي يتم رؤيته . Header + body calling
functions :Function Signature ! • الـ function signature هو جزء من الـ function prototype الذي يتألف من اسم التابع وأنماط الـ parameters list من دون نمط الإرجاع .
functions :Function definition Parameters List : • يتم الفصل بين المتحولات بفاصلة . • المتحولات هي local variables أي تموت هذه المتحولات بعد الخروج من التابع . • لا يمكن تعريف متحول داخل التابع بنفس اسم متحول دخل للتابع . • لكتابة تابع بدون متحولات إما نترك الـ parameter list فارغة أو نكتب الكلمة void لتدل على لا شيء.
functions :Function definition Function name : • يتم وضع اسم للتابع بحيث يعبر عن وظيفته . • يجب أن يكون اسم التابع غير محجوز مسبقاً . • لا يمكن تعريف تابع ضمن تابع آخر ! • يمكن استدعاء تابع ضمن تابع آخر .
functions :Example • عندما يتم تمرير قيمة متحول لتابع فإنه يتم في البداية أخذ نسخة من هذا المتحول وتعطى كدخل للتابع وبالتالي يتم المحافظة على القيمة السابقة كي لا يتم تغييرها من داخل التابع. • هنا تم نسخ المتحول x في التابع f1 وإعطاء النسخة كدخل للتابع f2 فأي تعديل على x داخل f2 لن يعدل على x في f1 لأنها نسخة منها وليست هي نفسها ! 10 5
functions :Math Library • يوجد مكتبة تحوي مجموعة من التوابع الرياضية وتدعى cmath . • نقوم بتضمين المكتبة السابقة في برنامجنا لنتمكن من استدعاء التوابع الموجودة بداخلها . • #include <cmath> • أغلب التوابع الموجودة في المكتبة السابقة يكون دخلها من النمط • double والخرج أيضاً double .
functions :Generating Random Numbers • يوجد تابع يدعى rand( ) يقوم بتوليد أعداد عشوائية ضمن المجال [0 32767] . • التابع السابق يوجد ضمن المكتبة <cstdlib> . • كل مرة يتم توليد رقم جديد ضمن المجال السابق وفق استراتيجية ما • بدءاً من قيمة ما تدعى البذرة seed . • ستنتج نفس القيم المولدة عند إعادة تنفيذ البرنامج من جديد !
functions :Generating Random Numbers • يوجد تابع يدعى srand(unsigned int ) والذي يقوم بتعيين القيمة الأولية للأعداد المولدة عشوائية أي تحديد قيمة البذرة seed . • التابع السابق أيضاً يوجد ضمن المكتبة <cstdlib> . • في حال كانت القيمة المدخلة ثابتة فإنه سيتم توليد كل مرة • تنفيذ نفس سلسلة الأرقام المولدة . • التابع السابق يتم استدعاءه لمرة واحدة فقط .
functions :Generating Random Numbers • إذاً ماذا نفعل إذا كنا نريد في كل مرة تنفيذ توليد قيم مختلفة ؟ • في كل مرة نقوم بإدخال قيمة مختلفة كدخل للتابع • srand( unsigned int ) . • مثلاً ندخل الوقت الحالي بالثواني !!! يوجد تابع يقوم بذلك يدعى time(0) وهو ضمن المكتبة <ctime> . • في كل مرة نقوم فيها بتنفيذ البرنامج ستنتج لدينا قيم عشوائية مختلفة بسبب اختلاف قيمة البذرة .
functions :Generating Random Numbers • ذكرنا أن التابع rand( ) يقوم بتوليد قيمة عشوائية ضمن المجال [0 32767] . • ولكن ........ ماذا سنفعل إذا كنا نريد توليد قيمة ضمن المجال [start end] مثلا توليد قيمة ضمن المجال [2 11] .
functions :Generating Random Numbers • ذكرنا أن التابع rand( ) يقوم بتوليد قيمة عشوائية ضمن المجال [0 32767] . • ولكن ........ ماذا سنفعل إذا كنا نريد توليد قيمة ضمن المجال [start end] مثلا توليد قيمة ضمن المجال [2 11] . • سنقوم بالبداية بإجراء عملية باقي القسمة % لأن x % y سيعطينا قيمة ضمن المجال [0 y-1] • ثم نقوم بعملية إزاحة بقيمة ما ليتم إزاحة كامل المجال . !!
functions :Generating Random Numbers • مثلا توليد قيمتين ضمن المجال [2 11] وبحيث كل مرة يتم فيها تنفيذ البرنامج يتم توليد قيم مختلفة عن القيم السابقة .
Header Files : • أحياناً نقوم بكتابة عدة ملفات تحوي عدد كبير من التوابع ..... لماذا لا نقوم بفرز هذه التوابع بشكل منطقي وفقاً لوظيفتها ، مثلاً التوابع الرياضية نقومبوضعها لوحدها (في ملف منفصل)، والتوابع المسؤولة عن تصميم الواجهات في مكان آخر ، وهكذا .... (هذه الأجزاء هي عبارة عن ملفات منفصلة تدعى مكاتب) • هذا الأمر يساعدنا أيضاً في إعادة استخدام هذه الأجزاء المنفصلة في أكثر من مشروع . • سابقاً كنا نقوم باستخدام مكاتب جاهزة في اللغة ولتضمينها نكتب : • ولكن ماذا لو كنا نريد كتابة مكتبة خاصة بنا !!! • سنقوم الآن بكتابة مكتبة رياضية بسيطة خاصة بنا تحوي تابع وحيد الآن يقوم بجمع عددين ....
Header Files : • نقوم بإنشاء ملف header نسميه باسم المكتبة الذي نريد مثلاً my_math ونضعه ضمن مجلد الـ header في الـ project الحالي . • تكون لاحقة المكتبة .h لأنه سيكون ملف header . • نقوم بكتابة header التوابع التي نريد فقط الترويسة أي prototype من دون كتابة جسم التابع في ملف الـ header ، كما يمكننا أيضاً تعريف متحولات إذا أردنا . • نقوم بكتابة أجسام التوابع كاملة في ملف جديد بالاسم my_math.cpp سيتضمن كود تحقيق تلك التوابع الموجودة في الـ header file ، إذاً سيتم وضع ملف جديد بالاسم السابق ضمن الـ source . • الآن بقي لنا تضمين مكتبتنا الجديدة لنقوم باستخدامها .
Header Files : • الآن نقوم بتضمين مكتبتنا في الملف الأساسي حتى نتمكن من استخدام توابعنا الجديدة . Compiler Error • عملية التضمن #include < …> تستخدم للمكاتب التي أتت مع اللغة وتكون موجودة في مكان خاص في القرص الصلب. • ويكون التضمين لمكاتبنا الجديدة باستخدام #include “filename.h”
Header Files : • الآن نقوم بتضمين مكتبتنا في الملف الأساسي حتى نتمكن من استخدام توابعنا الجديدة . 8.2
Header Files : • إذاً أصبح لدينا ثلاث ملفات الآن : • الأول :
Header Files : • إذاً أصبح لدينا ثلاث ملفات الآن : • الثاني:
Header Files : • إذاً أصبح لدينا ثلاث ملفات الآن : • الثالث:
Scope Rules :Local Scope : • النطاق المحلي local scope هو أي نطاق يبدأ بـ { وينتهي ب } . • {………………………………………..} . • جسم التابع يوجد ضمن نطاق ، والـ switch body أيضاً هو نطاق ، ..... • يمكن تعريف نطاق بداخل نطاق . • جميع المتحولات التي يتم تعريفها ضمن نطاق ما تكون فترة حياتها ضمن هذا النطاق أي أنها تموت بعد الخروج من هذا النطاق .
Scope Rules :Local Scope : Compiler Error
Scope Rules :Global Scope : • المجال الذي لا يقع ضمن أي block هو نطاق عام global block . • فترة حياة المتحولات ضمن الـ global scope (file scope) هي بانتهاء تنفيذ البرنامج. • تمكننا الـ global variable من سهولة الوصول إليها أينما كنا . • ولكن ينبغي تجنبها ..... إذ أنه في حال جعلنا جميع المتحولات لدينا هي global سيصبح هنا عدد هائل من المتحولات وعدم التمييز بينها لاحقاً ، مما يصعّب من صيانة البرنامج في حال أردنا تعديله .
Scope Rules :Global Scope : Global variables Local variable in main Local variable in scope inside main
Scope Rules :Static storage : • يمكن تعريف متحول ما على أنه static . • يمكن تعريف متحول ساكن محلي static local ضمن تابع مثلاً ، عندها فإن هذا المتحول يتم تهيئته لمرة واحدة فقط (بحيث يصبح ساكن ضمن التابع) وتكون فترة حياته بانتهاء البرنامج ولكن لا تتم رؤيته إلا من داخل التابع .
Scope Rules : scope resoultion operator :: • لدينا متحول عام global variableولكننا حتى الآن لم نصل إليه !!!! • كيف سنطبع قيمة الـ x العامة ؟!! • عن طريق المعامل :: والذي يتم وضعه قبل اسم المتحول والذي يقصد به الخروج إلى الـ global scope ثم انتقاء المتحول . • انتقل للمثال التالي .... 9 1 8
Recursion ! • التوابع العودية هي توابع يتم تنفيذها مرة واحدة أو أكثر ويتم الخروج منها عند عدم تحقق شرط ما يدعى شرط التوقف . • التابع العودي هو تابع يقوم باستدعاء نفسه كل مرة إلى أن يصل إلى شرط التوقف فيتوقف عن الاستدعاء ويقوم بتحصيل النتائج المتراكمة عن الاستدعاءات السابقة .
Recursion ! • هذا مثال يقوم بإيجاد عاملي لعدد ما ولكن بشكل تكراري وليس عودي .
Recursion ! • هذا مثال يقوم بإيجاد عاملي لعدد ما ولكن بشكل تكراري وليس عودي .
Recursion ! • يمكننا كتابة المهمة السابقة عن طريق تابع عودي بالاستناد إلى العلاقة التالية : • نقوم بالتوقف عن العودية عندما يصبح العدد المدخل للتابع هو 0 .
Recursion ! • إذاً لدينا قاعدتين : • الحالة الخاصة (شرط التوقف) • الحالة العامة (الحالة التي يحصل فيها استدعاء عودي للتابع نفسه)
Recursion ! • هذا مثال يقوم بإيجاد عاملي لعدد ما ولكن بشكل عودي .
Recursion ! • كل عملية استدعاء لتابع تكون له متحولاته الخاصة والمستقلة عن الاستدعاء السابق . • يجب تنفيذ جميع التعليمات الموجودة في الاستدعاء العودي الحالي . • انتقل للمثال التالي والذي يشرح كامل الفكرة !!