C++ الدوال

مفهوم الدوال في C++

الدالة ( Function ) عبارة عن مجموعة أوامر مجمعة في مكان واحد و تتنفذ عندما نقوم باستدعائها.
في الدروس السابقة تعرفنا على الكثير من العديد من الدوال الجاهزة في C++ و التي تستخدم للتعامل مع النصوص و الأرقام.

في هذا الدرس سنتعلم كيفية إنشاء دوال جديدة و كيفية استخدامها.


أمثلة حول الدوال الجاهزة

أسماء بعض الدوال التي قمنا باستخدامها في الدروس السابقة.

length();
insert();
replace();
fmax();
floor();
	

مصطلحات تقنية

الدوال الجاهزة في C++ يقال لها Built-in Functions.
الدوال التي يقوم المبرمج بتعريفها يقال لها User-defined Functions.

بناء الدوال في C++

عند تعريف أي دالة في C++ عليك إتباع الشكل التالي:

returnType functionName(Parameter)
{
    // Function Body
}

returnType: يحدد النوع الذي سترجعه الدالة عندما تنتهي أو إذا كانت لن ترجع أي قيمة.

functionName: يمثل الإسم الذي نعطيه للدالة, و الذي من خلاله يمكننا استدعاءها.

Parameter: المقصود بها الباراميترات ( وضع الباراميترات إختياري ).

Function Body: تعني جسم الدالة, و المقصود بها الأوامر التي نضعها في الدالة.


نوع الإرجاع ( returnType ) في الدالة يمكن أن يكون أي نوع من أنواع البيانات الموجودة في C++ ( int - double - bool - string إلخ.. ).
و يمكن وضع إسم لكلاس معين, و هنا يكون القصد أن الدالة ترجع كائن من هذا الكلاس ( لا تقلق ستتعلم هذا في دروس لاحقة ).

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

أمثلة حول تعريف دوال جديدة في C++

في المثال التالي قمنا بتعريف دالة إسمها myFunction, نوعها void, و تحتوي على أمر طباعة فقط.
بعدها قمنا باستدعائها في الدالة main() حتى يتم تنفيذ أمر الطباعة الموضوع فيها.

المثال الأول

Main.cpp
#include <iostream>

using namespace std;

// عند استدعاءها تقوم بطباعة جملة myFunction هنا قمنا بتعريف دالة إسمها
void myFunction() {
    cout << "My first function is called";
}

int main()
{
	// حتى يتنفذ الأمر الموضوع فيها myFunction() هنا قمنا باستدعاء الدالة
    myFunction();

    return 0;
}
		

سنحصل على النتيجة التالية عند التشغيل.

My first function is called
		

هنا قمنا بتعريف دالة إسمها greeting, عند إستدعاءها نمرر لها إسم فتطبع رسالة ترحيب للإسم الذي تم تمريره لها.

المثال الثاني

Main.cpp
#include <iostream>

using namespace std;

// عند استدعاءها تقوم بطباعة جملة greeting هنا قمنا بتعريف دالة إسمها
void greeting(string name)
{
    cout << "Hello " << name << ", welcome to our company.";
}

int main()
{
	// حتى يتنفذ الأمر الموضوع فيها greeting() هنا قمنا باستدعاء الدالة
    greeting("Mhamad");

    return 0;
}
		

سنحصل على النتيجة التالية عند التشغيل.

Hello Mhamad, welcome to our company.
		

هنا قمنا بتعريف دالة إسمها getSum, عند إستدعاءها نمرر لها عددين فترجع لنا ناتج جمعهما.

المثال الثالث

Main.cpp
#include <iostream>

using namespace std;

// عند إستدعاءها نمرر لها عددين فتقوم بإرجاع ناتج جمعهما get_sum هنا قمنا بتعريف دالة إسمها
int getSum(int a, int b)
{
    return a + b;
}

int main()
{
	// x في المتغير get_sum() هنا قمنا بتخزين ناتج العددين 3 و 5 الذي سترجعه الدالة
    int result = getSum(3, 7);
	
	// و التي ستساوي 10 result هنا قمنا بعرض قيمة المتغير
	cout << "Result = " << result;
	
    return 0;
}
		

سنحصل على النتيجة التالية عند التشغيل.

Result = 10
		

إعطاء قيمة إفتراضية للباراميترات في C++

C++ تتيح لك وضع قيم إفتراضية للباراميترات مما يجعلك عند إستدعاء الدالة مخيّر على تمرير قيم مكان الباراميترات بدل أن تكون مجبراً على ذلك.


مصطلحات تقنية

القيمة الإفتراضية التي نضعها للباراميتر يقال لها Default Argument.


في المثال التالي قمنا بتعريف دالة إسمها printLanguage.
هذه الدالة فيها باراميتر واحد إسمه language يملك النص "English" كقيمة إفتراضية.
كل ما تفعله هذه الدالة عند إستدعاءها هو طباعة قيمة الباراميتر language.

ملاحظة: بما أن الباراميتر language يملك قيمة بشكل إفتراضية, فهذا يعني أنك لم تعد مجبر على تمرير قيمة له عند إستدعاء الدالة لأنه أصلاً يملك قيمة.

مثال

Test.cpp
#include <iostream>

using namespace std;

// و يمكنك عدم تمرير قيمة لأنه أصلاً يملك قيمة language عند إستدعاءها يمكنك تمرير قيمة لها مكان الباراميتر .printLanguage هنا قمنا بتعريف دالة إسمها 
void printLanguage(string language="English")
{
    cout << "Your language is " << language << endl;
}

int main()
{
	// "English" و بالتالي ستظل قيمته language بدون تمرير قيمة مكان الباراميتر printLanguage() هنا قمنا باستدعاء الدالة
    printLanguage();
	
	// "Arabic" و بالتالي ستصبح قيمته language للباراميتر 'Arabic' مع تمرير القيمة printLanguage() هنا قمنا باستدعاء الدالة
	printLanguage("Arabic");
	
    return 0;
}
		

سنحصل على النتيجة التالية عند التشغيل.

Your language is English
Your language is Arabic
		

إنتبه

إذا كانت الدالة تملك أكثر من باراميتر و تريد وضع قيمة إفتراضية لأحد الباراميترات التي تمكلها فقط فيجب وضع الباراميترات التي تملك قيم إفتراضية في الآخر.
إن لم ترد ذلك ستكون مجبر على وضع قيم إفتراضية لجميع الباراميترات الموجودة بعد أول باراميتر وضعت له قيمة إفتراضية.


هنا وضعنا لك عدة أمثلة حول الأخطاء التي قد تقع فيها عند وضع قيم إفتراضية حتى تتعلم كيف تتجنبها.

شاهد الأمثلة »

أين يجب تعريف الدوال في C++

مترجم لغة C++ يقرأ الكود سطراً سطراً مع تنفيذ الأوامر الموضوعة في كل سطر بشكل مباشر عندما يتم تشغيل البرنامج.
لهذا السبب يجب دائماً أن تكون الدالة التي تريد استدعاءها معرّفة سابقاً حتى لا يظهر لك مشكلة عند تشغيل البرنامج.


في المثال التالي, قمنا بوضع الدالة myFunction() بعد الدالة التي قمنا باستدعائها منها.
المشكلة التي ستحدث عند التشغيل هنا سببها أن المترجم سيكون لا يعرف ما هي myFunction() حيث أنه تم استدعاءها قبل أن يقوم المترجم قد سبق و قرأها.

المثال الأول

Main.cpp
#include <iostream>

using namespace std;

int main()
{
	// myFunction() هنا قمنا باستدعاء الدالة
    myFunction();

    return 0;
}

// التي تحتوي على أمر طباعة فقط myFunction هنا قمنا بتعريف الدالة
void myFunction()
{
    cout << "My first function is called";
}
		

سيظهر الخطأ التالي عند التشغيل و الذي يعني أن المترجم لم يعرف ما هي myFunction التي تحاول استدعاءها في السطر الثامن.

main.cpp|8|error: 'myFunction' was not declared in this scope|


حل مشكلة عدم التعرف على الدالة

لحل مشكلة عدم التعرف على الدالة التي حدثت في المثال السابق عندنا خيارين:

  • إبقاء الدالة myFunction() مكانها و ذكر تعريفها ( Function Declartion ) في أول الملف فقط, و هذه الطريقة تعتبر الأكثر تفضيلاّ.
  • وضع الدالة myFunction() فوق الدالة main() حتى يقوم المترجم بقرائها و التعرف عليها و تصبح قادر على استدعاءها في الدالة main() الموجودة بعدها.


في المثال التالي, قمنا بإبقاء الدالة myFunction() مكانها و ذكر تعريفها ( Function Declartion ) قبل أن يتم استدعاءها في الدالة main().
إذاً لن يحدث أي مشكلة عند استدعاء الدالة myFunction() من الدالة main() لأن المترجم سيكون لديه علم بأن الدالة myFunction() موجودة فعلاً.

المثال الثاني

Main.cpp
#include <iostream>

using namespace std;

// حتى يقوم المترجم بالتعرف عليها و نصبح قادرين على استخدامها myFunction الدالة ( Declartion ) هنا قمنا بوضع تعريف
void myFunction();

int main()
{
	// myFunction() هنا قمنا باستدعاء الدالة
    myFunction();

    return 0;
}

// أو بمعنى آخر تعريف ما سيحدث عندما يتم استدعاءها ,myFunction الدالة ( body ) هنا قمنا بتعريف جسم
void myFunction()
{
    cout << "My first function is called";
}
		

في الدالة main() سيتم استدعاء و تتنفذ الدالة myFunction() بدون أي مشاكل و سنحصل على النتيجة التالية عند التشغيل.

My first function is called
		

نصيحة

الأفضل دائماً وضع تعريفات الدوال ( Functions Declartions ) قبل الدالة main() و تعريف محتوى هذه الدوال بعدها كالتالي لأن قراءة الكود ستصبح أسهل بالنسبة لك.


المثال التالي يريك فقط كيف تقوم بترتيب الكود إذا كنت تنوي تعريف العديد من الدوال في الملف.

المثال الثالث

#include <iostream>

using namespace std;

// نقوم بذكر تعريف جميع الدوال التي سنقوم بإنشائها لاحقاً main() قبل الدالة
void printMessage();
void greeting(string name);

// و فيها يمكننا استدعاء أي دالة قمنا بذكر تعريفها سابقاً بدون أي مشاكل main() هنا نقوم بتعريف الدالة
int main()
{
	// greeting() و printMessage() هنا يمكننا استدعاء
    return 0;
}

// لأنه قمنا بذكر أنها موجودة من قبل printMessage() هنا نقوم بتعريف الدالة
void printMessage()
{
   // هنا نكتب ما سيحدث عند استدعاءها	
}

// لأنه قمنا بذكر أنها موجودة من قبل greeting() هنا نقوم بتعريف الدالة
void greeting(string name)
{
   // هنا نكتب ما سيحدث عند استدعاءها	
}
		

الدورات

أدوات مساعدة

الأقسام

دورات
مقالات أسئلة مشاريع كتب