الدوال الاستاتيكية في ++C

الدوال الاستاتيكية

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

statfunc.cpp


 


// statfunc.cpp


// static functions and ID numbers for objects


// UCS Laboratories


 


#include <iostream.h>


 


class gamma


   {


   private:


      static int total;        // total objects of this class


                   //    (declaration only)


      int id;                  // ID number of this object


   public:


      gamma()                  // no-argument constructor


     {


     total++;              // add another object


     id = total;           // id equals current total


     }


      ~gamma()                 // destructor


     {


     total--;


     cout << "\nDestroying ID number " << id;


     }


      static void showtotal()  // static function


     {


     cout << "\nTotal is " << total;


     }


      void showid()            // non-static function


     {


     cout << "\nID number is " << id;


     }


   };


 


int gamma::total = 0;           // definition of total


 


void main()


   {


   cout << endl << endl;


   gamma g1;


   gamma::showtotal();


 


   gamma g2, g3;


   gamma::showtotal();


 


   g1.showid();


   g2.showid();


   g3.showid();


   cout << "\n----------end of program----------"; //UCS will return.


   }




يتضمن البرنامج بيانا معرفا بأنه استاتيكي في الفئة gamma , وهو total ومهمته أنه يتتبع مجموع ما ينشأ من الفئة من كائنات , ويتزايد عن طريق الدوال البادئة للكائنات , ويفني عن طريق الدوال المنهية لها .



لكي نتوصل لهذا البيان ننشئ دالة showtotal() وهي تعرف استاتيكية , حتي لا تكون متعلقة بأي كائن . فالدالة الاستاتيكية تكون دالة وحيدة لكل الكائنات ] إذا لم تعرف كذلك فسوف يكون لكل كائن دالة من هذا القبيل ويضيع الهدف من إنشائها [ .



لكن كيف تستثيرها , بينما هي غير مرتبطة بأي كائن من كائنات الفئة ؟ بل قد نستثيرها ولم ينشأ بعد أي كائن فتعطينا هذه الحقيقة , وهي أن الكائنات المنشأة عددها صفر .



إن مخاطبة الدوال الاستاتيكية يكون بإسم الفئة لكونها غير منتمية لكائن وذلك يتطلب استخدام مؤثر النطاق "::" وهو ما استخدمناه في البرنامج بالفعل في الأمر :



Gamma::showtotal();



وإليك خرج البرنامج :



Total is 1



Total is 3



ID number is 1



ID number is 2



ID number is 2



………end of program………..



Destroying ID number 3



Destroying ID number 2



Destroying ID number 1



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



مثال سباق الخيل



للدوال الاستاتيكية أهمية كبيرة في البرنامج التي تمثل الحياة الواقعية , كخطوط الإنتاج أو حركة الطيران , وكذلك الألعاب وسوف نعرض فيما يلي تمثيلا لسباق الخيل حيث تبدأ الخيل من أقصي اليسار متسابقة إلي اليمين . وتحدد سرعة كل حصان بطريقة عشوائية , فلا يمكن التكهن بمن له الفوز . وسوف تستخدم دوال المحارف الرسومية لتمثيل الحصان (وإن كان شكله سيبدو فجا إلي حد ما ) . وعليك أن تترجم البرنامج كبرنامج يعمل تحت الدوس حيث سنستخدم دالة الدوس delay() لإبطاء حركة الأحصنة حتي يمكن متابعة حركتها .



يسأل البرنامج في بدايته عن عدد الأحصنة ومسافة السباق . وتحدد مسافات سباق الخيل في النظام الإنجليزي بوحدات تسمي "الفورلونج" , ويساوي ثمن ميل فيقال إن السباق سيكون لمسافة 8 فولونج مثلا . ويرسم البرنامج خطوطا رأسية تتباعد بمقدار يمثل فورلونج واحد ويمكن للمستخدم أن يدخل حتي 10 أحصنة . ويمثل كل حصان بمستطيل ذي رقم بمنتصفه . (أنظر الشكل )



Object-Oriented Programming in C   _Page_0510_Image_0001



شكل برنامج سباق الخيل



تصميم البرنامج



كيف نبدأ تفكيرنا الكائني لبرنامج كهذا ؟ السؤال المبدئي هو : هل هناك كينونة ما يمكن نمذجتها والرد نعم الأحصنة . ولذا فيبدو أمرا منطقيا أن نجعل من كينونة الحصان كائنا وسوف ننشئ بالتالي نسميها horse تحمل الخصائص المشتركة للأحصنة كرقمه والمسافة التي قطعها .



كما يوجد لدينا بيانات عامة لكل الأحصنة , مثل طول المضمار , والزمن المنقضي من السباق والعدد الكلي للأحصنة . يمكن القول بأنها بيانات متعلقة بالسباق (في مقابل البيانات المتعلقة بالأحصنة) . كيف نتعامل إذن مع البيانات المتعلقة بالسباق ؟ أحد أنماط التفكير أن ننشئ فئة أخري تسمي track (المضمار ) , نضمها هذه البيانات وتكون من عناصر بياناتها مصفوفة من كائنات الأحصنة . علي أن التفكير ليس محققا للتبسيط لأن جعل مخفية عن البقية كأصل عام . ماذا لو جعلنا الحصان فئة مشتقة من المضمار ؟ ليس هذا أيضا بالحل المنطقي , لأن الحصان ليس "نوعا" من فئة المضمار.



الإنقاذ : البيانات الإستاتيكية



يكمن الحل الأمثل في البيانات الإستاتيكية , فهي مرئية لكافة كائنات الأحصنة . وبالإضافة لبيانات المضمار التي ذكرناها , سوف نستخدم أيضا المؤثر new لنحصل علي ذاكرة لكافة الأحصنة , حيث لا نعلم مسبقا العدد الذي سيدخله المستخدم . ويجعل المؤشر الذي يعيده بيانا استاتيكيا يصبح متاحا لكافة الكائنات , مع إن كل كائن يحتل موضعا به . ومع إنشاء أي كائن فإن دالته المبتدئة تحمل علي الفور عدد ما سبقه من كائنات حتي يمكنه أن يرقم نفسه . كل هذه البيانات يجب أن تكون استاتيكية حيث إنها مطلوبة لكل الكائنات .



كما سيكون لدينا دالة استاتيكية لاستهلال بيانات المضمار , init_track() لتكون بيانات المضمار بدورها مرئية لكافة الكائنات . وإليك صياغة البرنامج .







horse.cpp


 


// horse.cpp


// models a horse race


// UCS Laboratories


 


#include <iostream.h>


#include <dos.h>                    // for delay()


#include <conio.h>                  // for kbhit()


#include <stdlib.h>                 // for random()


#include <time.h>                   // for randomize()


 


const int CPF = 5;                  // screen columns per furlong


 


class horse


   {


   private:


      // track characteristics (declarations only)


      static horse* hptr;           // pointer to horse memory


      static int total;             // total number of horses


      static int count;             // horses created so far


      static int track_length;      // track length in furlongs


      static float elapsed_time;    // time since start of race


 


      // horse characteristics


      int horse_number;             // this horse's number


      float finish_time;            // this horse's finish time


      float distance_run;           // distance since start


   public:


      static void init_track(float l, int t); // initialize track


      static void create_horses()   // create horses


         {


         hptr = new horse[total];   // get memory for all horses


         }


      static void track_tick();     // time tick for entire track


      horse()                       // constructor for each horse


         {


         horse_number = count++;    // set our horse's number


         distance_run = 0.0;        // haven't moved yet


         }


      void horse_tick();            // time tick for one horse


   };


 


horse* horse::hptr;                 // define static (track) vars


int horse::total;


int horse::count = 0;


int horse::track_length;


float horse::elapsed_time = 0.0;


 


void horse::init_track(float l, int t)  // static (track) function


   {


   total = t;                       // set number of horses


   track_length = l;                // set track length


   randomize();                     // initialize random numbers


   clrscr();                        // clear screen


                                    // display track


   for(int f=0; f<=track_length; f++)    // for each furlong


      for(int r=1; r<=total*2 + 1; r++)  // for each screen row


         {


         gotoxy(f*CPF + 5, r);


         if(f==0 || f==track_length)


            cout << '\xDE';         // draw start or finish line


         else


            cout << '\xB3';         // draw furlong marker


         }


   }


 


void horse::track_tick()            // static (track) function


   {


   elapsed_time += 1.75;            // update time


 


   for(int j=0; j<total; j++)       // for each horse,


      (hptr+j)->horse_tick();       // update horse


   }


 


void horse::horse_tick()            // for each horse


   {                                // display horse & number


   gotoxy( 1 + int(distance_run * CPF), 2 + horse_number*2 );


   cout << " \xDB" << horse_number << "\xDB";


   if(distance_run < track_length + 1.0/CPF)  // until finish,


      {


      if( random(3) % 3 )           // skip about 1 of 3 ticks


         distance_run += 0.2;       // advance 0.2 furlongs


      finish_time = elapsed_time;   // update finish time


      }


   else


      {                             // display finish time


      int mins = int(finish_time)/60;


      int secs = int(finish_time) - mins*60;


      cout << " Time=" << mins << ":" << secs;


      }


   }


 


void main()


// UCS Laboratories


   {


   float length;


   int total;


 


   cout << "\nEnter track length (furlongs): ";


   cin >> length;


   cout << "\nEnter number of horses (1 to 10): ";


   cin >> total;


                             // initialize track


   horse::init_track(length, total);


   horse::create_horses();   // create horses


   while( !kbhit() )         // exit on keypress


      {


      horse::track_tick();   // move and display all horses


      delay(500);            // wait 1/2 second


      }


   }





متابعة الزمن



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



التسميات: