زيادة تحميل المؤثرات أحادية التأثير في ++C
زيادة تحميل المؤثرات من أكثر الخصائص التي تتميز بها البرمجة الكائنية إثارة . فهي تحيل الصياغة المعقدة للبرامج إلي صياغة واضحة رشيقة . فمثلا , هناك فرق واضح بين الأمر :
D3.addobjcets(d1, d2);
والأمر :
D3 = d1 + d2;
ويقصد بزيادة تحميل المؤثرات إعطاء المؤثرات مثل +, >=, +=, ... الخ معان إضافية حينما تطبق البيانات الموضوعة بواسطة المبرمج فالمؤثر + مثلا يستخدم للمتغيرات من نوع أعداد الصحيحة أو الكسرية , فإذا حاولت تطبيقه علي كائنات , سوف يتذمر محول الصياغة , ويعطيك رسالة خطأ . وبزيادة تحميل المؤثر سوف يكون استخدامه في مثل المواقف مشروعا تماما .
فزيادة التحميل في الواقع تعطي المبرمج فرصة لإعادة صياغة لغة السي++ ذاتها , يخلق فئات من المتغيرات , وإعطاء المؤثرات معان خاصة لها .
وهناك تكنيك أخر قريب الشبه بزيادة تحميل المؤثرات , وهو " تغيير النوع data type conversion" , وسوف نتناوله في الجزء الثاني من الفصل .
كما سنشير إلي المحاذير المتعلقة بموضوع زيادة التحميل , وهي ليست بالهيئة .
زيادة تحميل المؤثرات أحادية التأثير
المؤثرات أحادية التأثير unary , تعمل علي متأثر operand واحد , ومنها مؤثرات التزايد "++" , والتناقص "—" .
في برنامج العداد بالفصل السابع , أنشأنا فئة counter لكي تتابع العد . وكان تزايد الكائنات المنتمية لها يتم باستدعاء دالة منتمية :
C1.inc_count();
وقد كان ذلك وافيا بالغرض , ولكن الصياغة تكون أوضح لو وضعت علي الصورة المألوفة :
++c1;
ولذا فسوف نعيد صياغة البرنامج لنجعلها علي هذه الصورة :
countpp1.cpp
// countpp1.cpp
// increment counter variable with ++ operator
#include <iostream.h>
#include <conio.h>
class Counter
{
private:
unsigned int count; // count
public:
Counter() { count = 0; } // constructor
int get_count() { return count; } // return count
void operator ++ () { ++count; } // increment (prefix)
};
void main()
{
Counter c1, c2; // define and initialize
cout << "\nc1=" << c1.get_count(); // display
cout << "\nc2=" << c2.get_count();
++c1; // increment c1
++c2; // increment c2
++c2; // increment c2
cout << "\nc1=" << c1.get_count(); // display again
cout << "\nc2=" << c2.get_count();
getche();
}
لنبحث الأن كيف أمكننا أن نستخدم مؤثر التزايد ليتعامل مع الكائنات المنشأة في البرنامج .
الكلمة الحاكمة operator
لقد استخدمنا الكلمة الحاكمة operator متبوعة بالمؤثر المطلوب أن نزيد تحميله , ثم القوسين الخاصين بمعامل الدالة (وهي بدون معامل) . هذه الصياغة تخبر محول الصياغة أن يستدعي هذه الدالة المنتمية كلما استخدم هذا المؤثر , بشرط أن يكون المتأثر (العنصر المنصب عليه التأثير ) كائن من نوع الفئة المعرفة , ألا وهي counter .
وقد رأينا في الفصل السادس أن محول الصياغة يمكنه التمييز بين الدوال زائدة التحميل بالنظر إلي نوع البيان وعدد المعاملات , وهنا أيضا , يميز محول بين المؤثرات زائدة التحميل بالنظر إلي نوع بيان متأثراتها . فإذا كان المتأثر متغيرا عاديا , فإن محول الصياغة يستخدم الروتين العادي في اللغة لمعالجته , ولكن إذا وجده من نوع الفئة counter فإنه يستخدم الدالة المنتمية .
وعدم احتياج هذه الدالة لمعاملات أمر مفهوم , فالدالة الضمنية يمكنها أن تتداول مع بيانات الكائن المستدعاة له , ولا تحتاج أي بيان إضافي يخبرها عن ذلك . ويوضح الشكل هذا المفهوم
شكل مؤثر أحادي زائد التحميل
إعادة القيم المؤثرات
لنفرض أننا نريد زيادة إمكانية المؤثر , بزيادة تحميله بحيث يستخدم في أمر مثل :
C1 = ++c2;
وهو أمر غير ممكن في البرنامج السابق حيث أننا عرفنا الدالة المنتمية الخاصة بالمؤثر علي أنها لا تعيد قيما void , وفي الأمر المبين يجب أن تعيد قيمة الكائن c1 إلي الدالة الأصلية لتخصصه للكائن c2 . ويبين البرنامج التالي التعديل المطلوب :
countpp2.cpp
// countpp2.cpp
// increment counter variable with ++ operator, return value
#include <iostream.h>
#include <conio.h>
class Counter
{
private:
unsigned int count; // count
public:
Counter() { count = 0; } // constructor
int get_count() { return count; } // return count
Counter operator ++ () // increment count
{
++count; // increment count
Counter temp; // make a temporary Counter
temp.count = count; // give it same value as this obj
return temp; // return the copy
}
};
void main()
{
Counter c1, c2; // c1=0, c2=0
cout << "\nc1=" << c1.get_count(); // display
cout << "\nc2=" << c2.get_count();
++c1; // c1=1
c2 = ++c1; // c1=2, c2=2
cout << "\nc1=" << c1.get_count(); // display again
cout << "\nc2=" << c2.get_count();
getche();
}
هنا أنشأت الدالة مؤقتا باسم temp لتخزن فيه القيمة المطلوب إعادتها , ثم يعيدها بالأمر retun .
الكائنات المؤقتة غير المسماة
لقد استلزم إنشاء الكائن temp ثلاثة أوامر :
Couter temp;
Temp.count = count;
Return temp;
وفي البرنامج التالي سوف نقدم صياغة أكثر يسرا :
countpp3.cpp
// countpp3.cpp
// increment counter variable with ++ operator
// uses unnamed temporary object
#include <iostream.h>
#include <conio.h>
class Counter
{
private:
unsigned int count; // count
public:
Counter() { count = 0; } // constructor no args
Counter(int c) { count = c; } // constructor, one arg
int get_count() { return count; } // return count
Counter operator ++ () // increment count
{
++count; // increment count, then return
return Counter(count); // an unnamed temporary object
} // initialized to this count
};
void main()
{
Counter c1, c2; // c1=0, c2=0
cout << "\nc1=" << c1.get_count(); // display
cout << "\nc2=" << c2.get_count();
++c1; // c1=1
c2 = ++c1; // c1=2, c2=2
cout << "\nc1=" << c1.get_count(); // display again
cout << "\nc2=" << c2.get_count();
getche();
}
وفي هذا البرنامج قام أمر واحد :
Return counter(counter);
بما قام به الأوامر الثلاثة في البرنامج السابق . فهو ينشئ كائنا من النوع counter , دون تسمية له , ويستهل بالقيمة counter تمهيدا لإعادتها للدالة الأصلية .
وكما تعلم , تتطلب عملية الاستهلال دالة بادئة , وحيدة المعامل في هذه الحالة , وهو ما قمنا بعمله فعلا بالأمر :
Counter(int c ) { count = c; }
وقد تطلب الأسلوب المتبع في هذا البرنامج والسابق عليه أن ننشئ نسخة من الكائن الأصلي , ونعيد قيمة هذه النسخة , وسوف نري في الفصل الحادي عشر كيف يمكن إعادة القيمة من الكائن الأصلي مباشرة باستخدام المؤثر this .
التأثير السابق واللاحق
في البرنامج السابق كان معامل التزايد سابقا علي المتأثر (++c1) , فهل يمكن أن تستخدم نفس الفكرة لمعامل لاحق (c1++) ؟ الإجابة بالإيجاب , للإصدارات الخاصة ببرولاند سي++ , والإصدارات الخاصة بالتربو يس ++ بدءا من الثالث . ولكن الأمر يتطلب تعديلا طفيفا في الصياغة في حالة المؤثر اللاحق .
سوف نعرف في البرنامج التالي مؤثرين ++ زائدي التحميل للتزايد السابق واللاحق علي عملية التخصيص :
postfix.cpp
// postfix.cpp
// overloaded ++ operator in both prefix and postfix
// UCS Laboratories
#include <iostream.h>
#include <conio.h>
class Counter
{
private:
unsigned int count; // count
public:
Counter() { count = 0; } // constructor no args
Counter(int c) { count = c; } // constructor, one arg
int get_count() { return count; } // return count
Counter operator ++ () // increment count (prefix)
{ // increment count, then return
return Counter(++count); // an unnamed temporary object
} // initialized to this count
Counter operator ++ (int) // increment count (postfix)
{ // return an unnamed temporary
return Counter(count++); // object initialized to this
} // count, then increment count
};
void main()
{
Counter c1, c2; // c1=0, c2=0
cout << "\nc1=" << c1.get_count(); // display
cout << "\nc2=" << c2.get_count();
++c1; // c1=1
c2 = ++c1; // c1=2, c2=2 (prefix)
cout << "\nc1=" << c1.get_count(); // display
cout << "\nc2=" << c2.get_count();
c2 = c1++; // c1=3, c2=2 (postfix)
cout << "\nc1=" << c1.get_count(); // display again
cout << "\nc2=" << c2.get_count();
getche();
}
ونثير الانتباه الي التغيير في صياغة أمر زيادة تحميل المؤثر في حالة التزايد اللاحق :
Counter operator ++ (int)
فما معني int داخل القوسين , وقد سبق أن قلنا أن هذه الدالة لا تحتاج إلي معاملات ؟ إن هذا التعبير لا علاقة له بتعريف الأعداد , وهو ليس معاملا , كل ما في الأمر أنه للتميز بين نوعي الإعلان ؛ السابق واللاحق . إنه من عادة واضعي لغة السي أن يستخدموا نفس الشئ في أكثر من غرض , وهذه إحدي الحالات .
وتلاحظ أنه رغم أن قيمة c1 تزايدت إلي 3 , فإن قيمة c2 الأخيرة ظلت كما هي 2= , حيث إن عملية التخصيص سبقت عملية التزايد . ومن البديهي أن ما طبق في عملية التزايد تطبق في عملية التناقص .
ملاحظة أخري , في الإعادة بأسلوب الكائن المؤقت غير المسمي في البرنامج السابق كانت أوامر دالة المؤثر كالتالي :
{
++count;
Return Counter(count);
}
وقد اختصرت الصيغة هنا إلي :
{
Return counter (++count);
}
وكذلك في المؤثر الأخر .
تعليقات
إرسال تعليق