الدوال الاستاتيكية في ++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 أحصنة . ويمثل كل حصان بمستطيل ذي رقم بمنتصفه . (أنظر الشكل )
شكل برنامج سباق الخيل
تصميم البرنامج
كيف نبدأ تفكيرنا الكائني لبرنامج كهذا ؟ السؤال المبدئي هو : هل هناك كينونة ما يمكن نمذجتها والرد نعم الأحصنة . ولذا فيبدو أمرا منطقيا أن نجعل من كينونة الحصان كائنا وسوف ننشئ بالتالي نسميها 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() , وترسل "دقاتها" للمضمار ككل ترسل هذه الدقات لكافة الكائنات , من خلال دولها الخاصة , لكي يرسم كل حصان في موضعه الجديد .
تعليقات
إرسال تعليق