مفهوم الـ Overloading في C++
Overloading تعني تعريف أكثر من عامل, دالة أو كونستركتور لهم نفس الإسم و لكنهم يختلفون في عدد أو نوع الباراميترات.
عند تعريف أكثر من كونستركتور لهم نفس الإسم يكون الهدف أنه عند إنشاء كائن يكون هناك أكثر من طريقة متاحة لتمرير قيم أولية للخصائص الموجودة فيه.
عند تعريف أكثر من دالة لهم نفس الإسم يكون الهدف منهم إمكانية تنفيذ نفس العملية مع مراعاة عدد و أنواع القيم التي يتم تمريرها لللدالة عند استدعائها.
عند تعريف أكثر من عامل لهم نفس الرمز يكون الهدف منهم تصغير حجم الكود عند التعامل مع الكائنات.
شروط الـ Overloading في C++
يطبق فقط على العوامل, الدوال و الكونستركتورات.
يجب أن يملكوا نفس الإسم.
يجب أن يختلفوا في نوع أو عدد الباراميترات.
بالنسبة للدوال, نوع الإرجاع غير مهم لأن المترجم لا يستطيع التفريق بين الدوال إذا كانوا مختلفين في نوع الإرجاع فقط.
العوامل التي يمكن أن نفعل لها Overload في C++
-
*
/
%
^
|
~
!
,
=
<=
>=
++
--
>>
==
!=
&&
||
-=
/=
%=
^=
&=
*=
<<=
>>=
[]
()
->*
new
new []
delete
delete []
العوامل التي لا يمكن أن نفعل لها Overload في C++
أمثلة شاملة على الـ Overloading في C++
المثال الأول
تعريف دوال لها نفس الإسم و تختلف في نوع الباراميترات.
في المثال التالي قمنا بتعريف دالتين إسمهما sum
و نوعهما void
و لكنهما مختلفتان عن بعضهما بأنواع الباراميترات.
الدالة الأولى مهمتها جمع أي عددين نوعهما int
نمررهما لها عند استدعاءها و من ثم طباعة الناتج.
الدالة الثانية مهمتها جمع أي عددين نوعهما float
نمررهما لها عند استدعاءها و من ثم طباعة الناتج.
مثال تعريف دوال لها نفس الإسم و تختلف في نوع الباراميترات.
#include <iostream> using namespace std; // فتقوم بطباعة ناتج جمعهما int عند استدعاءها نمرر لها قيمتين من النوع sum هنا قمنا بتعريف دالة إسمها void sum(int a, int b) { cout << "First method is called ====> " << a << " + " << b << " = " << (a+b) << endl; } // فتقوم بطباعة ناتج جمعهما double عند استدعاءها نمرر لها قيمتين من النوع sum هنا قمنا بتعريف دالة إسمها void sum(double a, double b) { cout << "Second method is called ===> " << a << " + " << b << " = " << (a+b) << endl; } // main() هنا قمنا بتعريف الدالة int main() { sum(4, 6); // int هنا سيتم إستدعاء الدالة التي تأخذ 2 باراميتر نوعهم sum(2.3, 5.4); // double هنا سيتم إستدعاء الدالة التي تأخذ 2 باراميتر نوعهم return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
First method is called ====> 4 + 6 = 10 Second method is called ===> 2.3 + 5.4 = 7.7
كما لاحظت هنا, في كل مرة قمنا فيها باستدعاء الدالة sum()
وجدنا أن المترجم قام باستدعاء الدالة sum()
المناسبة لنوع الباراميترات التي كنا نمرره لها.
المثال الثاني
تعريف دوال لها نفس الإسم و تختلف في عدد الباراميترات.
في المثال التالي قمنا بتعريف ثلاث دوال إسمهم sum
و نوعهما void
و لكنهم مختلفين عن بعضهم بعدد الباراميترات.
الدالة الأولى مهمتها جمع أي عددين نمررهما لها عند استدعاءها و من ثم طباعة الناتج.
الدالة الثانية مهمتها جمع أي ثلاث أعداد نمررها لها عند استدعاءها و من ثم طباعة الناتج.
مثال تعريف دوال لها نفس الإسم و تختلف في عدد الباراميترات.
#include <iostream> using namespace std; // عند استدعاءها نمرر لها عددين فتقوم بطباعة ناتج جمعهما sum هنا قمنا بتعريف دالة إسمها void sum(float a, float b) { cout << a << " + " << b << " = " << (a + b) << endl; } // عند استدعاءها نمرر لها ثلاث أعداد فتقوم بطباعة ناتج جمعهم sum هنا قمنا بتعريف دالة إسمها void sum(float a, float b, float c) { cout << a << " + " << b << " + " << c << " = " << (a + b + c) << endl; } // main() هنا قمنا بتعريف الدالة int main() { sum(1, 3); // التي تأخذ قيمتين sum() هنا سيتم إستدعاء الدالة sum(1, 3, 7); // التي تأخذ ثلاث قيم sum() هنا سيتم إستدعاء الدالة return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
1 + 3 = 4 1 + 3 + 7 = 11
كما لاحظت هنا, في كل مرة قمنا فيها باستدعاء الدالة sum()
وجدنا أن المترجم قام باستدعاء الدالة sum()
التي تحتوي على نفس عدد الباراميترات الذي كنا نمرره لها.
المثال الثالث
تعريف دوال تعتمد على دوال عندها نفس الإسم.
في المثال التالي قمنا بتعريف ثلاث دوال إسمهم maximum()
و نوعهم double
.
الدالة الأولى تأخذ 2 باراميتر عبارة عن أرقام, و هي تعطينا العدد الأكبر بينهما.
الدالة الثانية تأخذ 3 باراميترات عبارة عن أرقام, و هي تعطينا العدد الأكبر بينهم.
الدالة الثالثة تأخذ 4 باراميترات عبارة عن أرقام, و هي تعطينا العدد الأكبر بينهم.
مثال تعريف دوال تعتمد على دوال عندها نفس الإسم.
#include <iostream> using namespace std; // هذه الدالة تعطيها رقمين فترجع لك العدد الأكبر بينهما double maximum(double a, double b) { if(a>b) return a; else return b; } // هذه الدالة تعطيها ثلاثة أرقام فترجع لك العدد الأكبر بينهم // و هي تعتمد على الدالة السابقة لمقارنة أول عددين مع العدد الثالث double maximum(double a, double b, double c) { if(maximum(a,b)>c) return maximum(a,b); else return c; } // هذه الدالة تعطيها ثلاثة أرقام فترجع لك العدد الأكبر بينهم // و هي تعتمد على الدالتين السابقتين لمقارنة أول ثلاث أعداد مع العدد الرابع double maximum(double a, double b, double c, double d) { if(maximum(a,b,c)>d) return maximum(a,b,c); else return d; } // main() هنا قمنا بتعريف الدالة int main() { cout << "The maximum number is: " << maximum(5, 20) << endl; // هنا سيتم إستدعاء الدالة التي تأخذ 2 باراميتر cout << "The maximum number is: " << maximum(5, 20, 15) << endl; // هنا سيتم إستدعاء الدالة التي تأخذ 3 باراميترات cout << "The maximum number is: " << maximum(5, 20, 15, 30); // هنا سيتم إستدعاء الدالة التي تأخذ 4 باراميترات return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
The maximum number is: 20 The maximum number is: 20 The maximum number is: 30
كما لاحظت هنا, في كل مرة قمنا فيها باستدعاء الدالة maximum()
وجدنا أن المترجم قام باستدعاء الدالة maximum()
التي تحتوي على نفس عدد الباراميترات الذي كنا نمرره لها, و داخلياً ربطنا الدوال ببعضها.
المثال الرابع
تعريف أكثر من كونستركتور في الكلاس.
في المثال التالي قمنا بتعريف كلاس إسمه Country
يمثل المعلومات التي يمكن أن يتضمنها أي بلد, و هو يحتوي على أربعة كونستركتورات.
الكونستركتور الأول لا يأخذ أي باراميتر.
الكونستركتور الثاني يأخذ باراميتر واحد عبارة عن إسم البلد.
الكونستركتور الثالث يأخذ إثنين باراميتر عبارة عن إسم و مساحة البلد.
الكونستركتور الرابع تأخذ ثلاثة باراميترات عبارة عن إسم و مساحة و عدد سكان البلد.
بعد إنشاء هذا الكلاس قمنا بإنشاء أربعة كائنات منه و في كل مرة استخدمنا كونستركتور مختلف لإنشاء الكائن.
مثال تعريف أكثر من كونستركتور في الكلاس.
#include <iostream> using namespace std; // الذي يمثل الأشياء التي يمتلكها أي بلد Country هنا قمنا بتعريف الكلاس class Country { public: // سيملك هذه الخصائص, أي كل بلد سيملك هذه الخصائص Country كل كائن من الكلاس string name; double area; long population; // الكونستركتور الأول لا يأخذ باراميترات Country() { name = ""; area = 0; population = 0; } // الكونستركتور الثاني يتيح لك تحديد إسم البلد مباشرةً عند إنشاء الكائن Country(string n) { name = n; area = 0; population = 0; } // الكونستركتور الثالث يتيح لك تحديد إسم البلد و مساحته مباشرةً عند إنشاء الكائن Country(string n, double a) { name = n; area = a; population = 0; } // الكونستركتور الرابع يتيح لك تحديد إسم البلد و مساحته و عدد السكان مباشرةً عند إنشاء الكائن Country(string n, double a, long p) { name = n; area = a; population = p; } // هذه الدالة تعرض جميع المعلومات التي تم إدخالها في الكائن void printInfo() { cout << "name: " << name << endl; cout << "area: " << area << endl; cout << "population: " << population << endl; cout << "-------------------" << endl; } }; // main() هنا قمنا بتعريف الدالة int main() { // هنا كل كائن يمثل بلد ,Country هنا قمنا بإنشاء 4 كائنات من الكلاس Country c1; // c1 هنا سيتم إستدعاء الكونستركور الأول, أي لن يتم تمرير أي معلومة حول البلد الذي يمثله الكائن Country c2("KSA"); // c2 هنا سيتم إستدعاء الكونستركور الثاني, أي سيتم تحديد إسم البلد الذي يمثله الكائن Country c3("Turkey", 780580); // c3 هنا سيتم إستدعاء الكونستركور الثالث, أي سيتم تحديد إسم و مساحة البلد الذي يمثله الكائن Country c4("Lebanon", 10452, 4467000); // c4 هنا سيتم إستدعاء الكونستركور الربع, أي سيتم تحديد إسم و مساحة و عدد سكان البلد الذي يمثله الكائن // لعرض خصائص جميع الكائنات printInfo() هنا قمنا باستدعاء الدالة c1.printInfo(); c2.printInfo(); c3.printInfo(); c4.printInfo(); return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
name: area: 0 population: 0 ------------------- name: KSA area: 0 population: 0 ------------------- name: Turkey area: 780580 population: 0 ------------------- name: Lebanon area: 10452 population: 4467000 -------------------
كما لاحظت هنا أن الكلاس Country
يملك أربعة كونستركتورات إسمهم Country()
و كل كونستركتور منهم يتيح لك تمرير قيم معينة مباشرةً عند إنشاء كائن من الكلاس Country
.
في كل مرة قمنا فيها باستدعاء الكونستركتور Country()
وجدنا أن المترجم قام باستدعاء الكونستركتور Country()
الذي يحتوي على نفس عدد الباراميترات الذي كنا نمرره له.
المثال الخامس
كيف يمكنك أن تفعل Overload للعامل ++
لجعله يقوم بزيادة قيم الكائن الذي يستدعى منه.
في المثال التالي قمنا بتعريف كلاس إسمه Player
يحتوي على ثلاث متغيرات تمثل المعلومات العامة التي يمكن أن يمتلكها اللاعب مثل رتبته grade
في اللعبة و سرعته speed
و كمية المال money
التي جمعها, بالإضافة إلى دالة إسمها printInfo
تعرض قيم هذه المتغيرات بشكل مرتب.
هنا افترضنا أن أي لاعب جديد ستكون رتبته تساوي 1
و سرعته تساوي 1
و لا يملك أي مال.
عند ترقية اللاعب سيتم زيادة رتبته و سرعته بمقدار 1
و سيتم إعطاؤه مبلغ 1000
أيضاً.
لنريك كيف يمكن أن تفعل Overload لعامل, قمنا بتخصيص العامل ++
لكي نستخدمه كلما أردنا ترقية اللاعب.
في الأخير بإنشاء كائن من الكلاس Player
(أي كأننا قمنا بإنشاء لاعب) و من ثم تجربة ترقيته بواسطة العامل ++
.
مثال
#include <iostream> using namespace std; // الذي يمثل اللاعب Player هنا قمنا بتعريف الكلاس class Player { public: // هنا قمنا بتعريف خصائص الكلاس int grade; int speed; int money; // هنا قمنا بتعريف كونستركتور الكلاس بهدف إعطاء قيم أولية لخصائص الكائن الذي يتم إنشاؤه من الكلاس Player() { grade = 1; speed = 1; money = 0; } // هنا قمنا بتعريف دالة تقوم بطباعة قيم خصائص الكائن بشكل مرتب عندما يتم استدعاؤه void printInfo() { cout << "Grade = " << grade << endl; cout << "Speed = " << speed << endl; cout << "Money = " << money << endl; cout << "----------------\n"; } // هنا قمنا بتعريف ما سيحدث عند استدعاء الرمز ++ من أي كائن ننشئه من الكلاس void operator++ (int) { // سيضاف عليها 1000 money سيضاف عليها 1 و قيمة speed سيضاف عليها 1 و قيمة grade قيمة grade += 1; speed += 1; money += 1000; } }; // main() هنا قمنا بتعريف الدالة int main() { // أي لاعب جديد ,Player هنا قمنا بإنشاء كائن من الكلاس Player player; // player لعرض القيم الإفتراضية التي يملكها الكائن printInfo() هنا قمنا باستدعاء الدالة player.printInfo(); // حتى يتم ترقيته player هنا قمنا باستدعاء الرمز ++ من الكائن player++; // player من جديد لعرض القيم التي أصبحها يملكها الكائن printInfo() هنا قمنا باستدعاء الدالة player.printInfo(); return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
Grade = 1 Speed = 1 Money = 0 ---------------- Grade = 2 Speed = 2 Money = 1000 ----------------
المثال السادس
كيف يمكنك أن تفعل Overload للعامل +
لجعله يقوم بإضافة قيم كائن على قيم كائن آخر.
في المثال التالي سنقوم بتطبيق إحدى قواعد الرياضيات التي نستخدمها لقياس المسافة بين نقطتين.
بالمبدأ كل نقطة سيكون لديها قيمة أفقية نرمز لها بالحرف x
و قيمة عامودية نرمز لها بالحرف y
.
الآن بما أننا سنحتاج لنقطتين و كل نقطة عندها x
و y
فمن المنطقي أن ننشئ كلاس إسمه Point
و نضع فيه متغيرين إسمهمها x
و y
.
عندها كلما أردنا إنشاء نقطة نقوم بإنشاء كائن من الكلاس Point
و نعطيه قيم.
الكلاس Point
وضعنا فيه كونستركتور لتمرير قيم بشكل مباشر للكائن الذي يتم إنشاؤه منه, كما أننا فعل Overload للعامل +
حتى نستطيع استخدامه للحصول على المسافة الموجودة بين أي نقطتين (كائنين) من خلال وضعه بينهما.
مثال
#include <iostream> #include <cmath> using namespace std; // الذي يمثل النقطة Point هنا قمنا بتعريف الكلاس class Point { public: // هنا قمنا بتعريف خصائص الكلاس int x; int y; // هنا قمنا بتعريف كونستركتور الكلاس بهدف إعطاء قيم أولية لخصائص الكائن الذي يتم إنشاؤه من الكلاس Point(int x, int y) { this->x = x; this->y = y; } // و الحصول على المسافة بينهما Point على كائن من الكلاس Point للرمز + حتى نستطيع إضافة كائن من الكلاس overload هنا فعلنا double operator+ (Point point) { return sqrt(pow(point.x - this->x, 2) + pow(point.y - this->y, 2)); } }; // main() هنا قمنا بتعريف الدالة int main() { // أي نقطتين ,Point هنا قمنا بإنشاء كائنين من الكلاس Point p1(1, 5); Point p2(4, 8); // distance و من ثم قمنا بتخزين الناتج في المتغير p2 و النقطة p1 هنا قمنا باستخدام العامل + للحصول على المسافة الموجودة بين النقطة double distance = p1 + p2; // distance هنا قمنا بعرض المسافة بين النقطتين و التي قمنا بتخزينها في المتغير cout << "Distance between p1 and p2 = " << distance; return 0; }
•سنحصل على النتيجة التالية عند التشغيل.
Distance between p1 and p2 = 4.24264