5 - التحميل الزائد للعوامل Overloading Operators

التحميل الزائد للعوامل Overloading Operators


تسمح لنا لغة ++C وبشكل متميز استخدام العوامل (المستخدمة مع الأنماط الأساسية) مع الأصناف. فنعلم مثلا أن التعبير التالي تعبير مقبول ونظامي:
كود:
int a, b, c; a = b + c;
لأن أنماط المعاملات المستخدمة في التعبير أنماط بسيطة (int). ولا يعد بالمقابل تطبيق هذا العامل على بنية مشابهة لما يلي أمراً ذو معنى واضح:
كود:
struct {char product [50]; float price;} a,b,c; a = b + c;
تؤدي ترجمة هذه العبارة إلى توليد خطأ. ولا ينتج هذا الخطأ من عملية الإسناد، إذ تسمح لنا ++C إسناد كائن (من صنف أو بنية) إلى كائن آخر، بل ينتج الخطأ من عملية الجمع التي لا يمكن تطبيقها مبدئياً بين معاملين من أنماط غير أساسية.

لكن لغة ++C مشكورة تسمح لنا بتوسيع مهام العوامل الموجودة فيها عن طريق التحميل الزائد overloading. بالتالي يمكن أن تقبل الكائنات المشكلة من أنماط مركبة (كما في المثال الأخير) العوامل التي لا يمكن تطبيقها عليها لولا التحميل الزائد لها، ونستطيع أيضا تغيير تأثير بعض العوامل التي تقبلها هذه الكائنات لتؤدي عملاً مختلفاً لعملها الافتراضي. نبين فيما يلي قائمة العوامل القابلة للتحميل الزائد:

+ - * / = < > =+ =- =* =/ << >> =>> =<< == =! => =< ++ -- % & ^ ! | ~ =& =^ =| && || =% [] () new delete

نقوم بالتحميل الزائد للعوامل بكتابة منهج في صنف ما ونسمي هذا المنهج بالكلمة المحجوزة operator ثم نتبعه بإشارة العامل الذي نريد كتابة تحميل زائد له، اي يبدو سياق ترويسة تابع التحميل الزائد لعامل ما إشارته sign كما يلي:
كود:
type operator sign (parameters);

ولقد كتبنا في المثال التالي تحميلاً زائداً للعامل +، الذي نحتاجه لجمع شعاعين رياضيين vector (وليس شعاعين برمجيين array) ثنائيي البعد (3,1)a و (1,2)b وحساب ناتج الجمع الشعاعي الذي هو شعاع أيضا. تتحقق عملية الجمع الشعاعي الرياضي للأشعة ثنائية البعد ببساطة بجمع المركبة الأولى x1 من الشعاع الأول مع المركبة الأولى x2 من الشعاع الثاني للحصول على المركبة الأولى x من شعاع النتيجة، وجمع المركبة الثانية y1 من الشعاع الأول مع المركبة الثانية y2 من الشعاع الثاني للحصول على المركبة الثانية y من شعاع النتيجة. أي ستكون مركبتي الشعاع الناتج في مثالنا هي (4,3)=(2+1,1+3).
كود:
// vectors: overloading operators example #include <iostream.h> class CVector { public: int x,y; CVector () {}; CVector (int, int); CVector operators + (CVector); }; CVector::CVector (int a, int b) { x=a; y=b; } CVector CVector:نقره لتكبير أو تصغير الصورة ونقرتين لعرض الصورة في صفحة مستقلة بحجمها الطبيعيperators+ (CVector param) { CVector temp; temp.x=x+param.x; temp.y=y+param.y; return (temp); } int main () { CVector a (3,1); CVector b (1,2); CVector c; c=a+b; cout << c.x << "," << c.y; } Output: 4,3
ستلاحظ في هذا المثال تكرار الكلمة CVector عدة مرات، لكنها كانت تعني أحياناً اسم الصنف CVector، وأحياناً أخرى كانت تستخدم في التصريح عن مناهج في الصنف، فلا تخلط بين الحالتين:
كود:
CVector (int, int); // function name CVector (constructor) CVector operator+ (CVector); // function operator+ that retrurns CVector type
إذاً يلعب المنهج +operator في الصنف CVector دور تحميل زائد للعامل الحسابي +، حيث نستطيع تطبيق هذا العامل وفق أحد الشكلين التاليين:
كود:
c = a+b; c = a.operator+ (b);
ملاحظة_____.......
لقد كتبنا في الصنف السابق بانياً فارغاً (بدون وسطاء) وله كتلة تعليمات فارغة no-op:
كود:
CVector () {};
وهذا الباني ضروري لأننا قد صرحنا في الصنف CVector عن بان آخر له وسطاء وهو:
كود:
CVector (int, int);
مما سيؤدي إلى إهمال البانيين الافتراضيين الذين تحدثنا عنهما سابقاً إلا إذا كتبناهما صراحة بأنفسنا، وإلا لكان التصريح التالي خاطئاً:
كود:
CVector C;
الموجودة في تعليمات التابع ()main.
ولكننا قد نبهنا سابقاً إلى خطر ترك كتلة تعليمات بان ما فارغة no-op، فيجب على الباني ان يحقق على الأقل اهم مهمة له وهي إعداد حقول الصنف بشكل مناسب، ولكننا تركنا الباني الفارغ بدون اي تهيئة للحقلين xوy. ويفضل لذلك ان يكون شكل الباني الأول كما يلي:
كود:
CVector () { x=0; y=0; };
ولكننا اهملنا هذا الشكل في المثال للتسهيل فقط


يحوي كل صنف بشكل افتراضي (إضافة للبانيين الفارغ والناسخ الافتراضيين) منهجاً افتراضياً لتعريف عملية الإسناد بين كائنين ينتميان لنفس الصنف. حيث ينسخ هذا العامل جميع محتويات الكائن الوسيط (الموجود على يمين عملية الإسناد) غير الثابتة إلى الكائن الآخر الموجود على يسار عملية الإسناد. وتستطيع طبعاً إعادة تعريف هذا العامل لتوظيفه بشكل مختلف عما هو عليه كأن تنسخ محتويات بعض الحقول الخاصة مثلاً وإعادة تهيئة الحقول الباقية.

السؤال: هل نحن مضطرون عند إعادة تعريف عامل ما (رياضي مثلاً) أن نشكله بشكل شبيه بطريقة عمله الأصلية، أو بشكل له علاقة بمعنى هذا العامل؟. والجواب:لا، ولكن يفضل ذلك، فليس منطقياً استخدام العامل + لطرح قيم كائنين من بعضهما، أو استخدام العامل == لتصفير الكائن (لملء حقوله أصفاراً) مع أننا نستطيع تحقيق ذلك تماماً ودون أي خطأ لا في الترجمة ولا في التنفيذ.