زيادة تحميل المؤثرات أحادية التأثير في ++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 فإنه يستخدم الدالة المنتمية .



وعدم احتياج هذه الدالة لمعاملات أمر مفهوم , فالدالة الضمنية يمكنها أن تتداول مع بيانات الكائن المستدعاة له , ولا تحتاج أي بيان إضافي يخبرها عن ذلك . ويوضح الشكل هذا المفهوم



c  99





شكل مؤثر أحادي زائد التحميل



إعادة القيم المؤثرات



لنفرض أننا نريد زيادة إمكانية المؤثر , بزيادة تحميله بحيث يستخدم في أمر مثل :



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);



}



وكذلك في المؤثر الأخر .

التسميات: