القوائم و الدوال في ++C

c

القوائم

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

و يضم جدول دوال الفئة TListImp التي تتيح استخدامها .

 

جدول دوال الفئة TListImp

الدالة

الغرض منها

Add

إضافة عنصر لمقدمة القائمة

Detach

محو عنصر من مقدمة القائمة

FirstThat

تعيد مؤشرا لأول كائن يحقق شرطا

Flush

إزالة كافة العناصر

ForEach

تنفيذ عملية ما علي كافة العناصر

IsEmpty

تعيد 1 في حالة الخلو

LastThat

تبحث عن كافة العناصر التي تحقق شرطا ما

PeekHead

تعيد العنصر في المقدمة دون إزالته

وإليك المثال التطبيقي الذي سوف نستغله لتقديم الدالة الشائعة في كافة هياكل الحاويات ؛ ForEach()

 

Listint.cpp

// listint.cpp


// demonstrates TListImp class


// also shows ForEach() member function


#include <classlib\listimp.h>        // for list containers


#include <iostream.h>


 


void Display(int&, void*);           // prototype


 


void main()


   {


   TListImp<int> listint;            // list of ints


 


   listint.Add(101);                 // add ints to list


   listint.Add(102);


   listint.Add(103);


 


   listint.ForEach(Display, 0);      // display all ints


   cout << endl;


 


   listint.Detach(102);              // remove an int


   listint.ForEach(Display, 0);      // display remaining ints


   }


 


void Display(int& i, void*)          // function displays an int


   {                                 // passed as argument


   cout << i << " ";


   }




 



الدالة ForEach()



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



والدالة التي طبقتا لها هذه الإمكانية هي الدالة Display() التي تظهر محتويات القائمة . وكما تري في المثال , كان اسم هذه الدالة هو المعامل الأول للدالة ForEach() باعتبار أن هذا الاسم هو متغير يختزن عنوان الدالة المستدعاة . أما المعامل الثاني للدالة ForEach فيمكنك أن تستغله كمؤشر لمعلومة إضافية لها , وهي إمكانية لم نستغلها في مثالنا ] راجع المثال dlistint.cpp حيث استخدمت هذه الإمكانية لدالة شبيهة , هي الدالة [LastThat() .



والدوال التي تقبل أن تستدعي بواسطة الدالة ForEach لها نمط محدد يأخذ الشكل التالي :



Void Interfune(aClass&, void*);



فهي كما تري من النوع الذي لا يعيد شيئا , ومعاملها الأول هو نوع البيان الذي طبقت به الدالة , ويمكن أن يكون فئة , ولكنة في مثالنا مجرد عدد صحيح int , وكما تري , يجب استخدام أسلوب الترحيل المرجعي . أما المعامل الثاني مؤشر لا نوعي  فهو يمرر نفس المؤشر الذي يوجد في المعامل الثاني للدالة ForEach() والذي لم نستغله .



القوائم المزدوجة



هي القوائم التي يمكن التعامل مع جهتها



وإليك الجدول الذي يضم دوال الفئة TDoubleListlmp يتلوه المثال التطبيقي عليها , والذي سوف نقدم فيه الدالة الهامة LastThat() .



جدول ودوال الفئة TDoubleListlmp













































































الدالة



الغرض منها



Add



إضافة عنصر لمقدمة القائمة



AddHead



مثل Add



AddTail



إضافة عنصر لمؤخرة القائمة



Detach



محو عنصر من مقدمة القائمة



FirstThat



تعيد مؤشرا لأول كائن يحقق شرطا



Flush



إزالة كافة العناصر



ForEach



تنفيذ عملية ما علي كافة العناصر



IsEmpty



تعيد 1 في حالة الخلو



LastThat



تبحث عم كافة العناصر التي تحقق شرطا ما



PeekHead



تعيد العنصر في المقدمة دون إزالته



PeekTail



تعيد العنصر في المؤخرة دون إزالته







Dlistint.cpp




// dlistint.cpp



// demonstrates TDoubleListImp class




// also shows LastThat() function




// UCS Laboratories




 




#include <classlib\dlistimp.h>     // for double list




#include <iostream.h>




 




void Display(int&, void*);         // prototypes




int FindDisplay(const int&, void*);




 




void main()




   {




   TDoubleListImp<int> dlistint;   // double list of ints




   int Range;                      // 200 means 200-299, etc.




 




   dlistint.AddAtHead(101);        // add ints to head of list




   dlistint.AddAtHead(102);




   dlistint.AddAtHead(103);




   dlistint.AddAtTail(201);        // add ints to tail of list




   dlistint.AddAtTail(202);




   dlistint.AddAtTail(203);




 




   cout << "\nContents of list: ";




   dlistint.ForEach(Display, 0);   // display all ints




   cout << endl;




 




   cout << "\nEnter start of range (100, 200, etc): ";




   cin >> Range;




   cout << "\nData in range "




        << Range << '-' << (Range+99) << ": ";




   if( !dlistint.LastThat(FindDisplay, (void*)&Range) )




      cout << "No such values";




   }  // end main()




 




// function displays an int passed as argument




void Display(int& i, void*)




   {




   cout << i << " ";




   }




 




// function displays int passed as argument, if within range




int FindDisplay(const int& i, void* ptrRange)




   {




   if(i>=(*(int*)ptrRange) && i<(*(int*)ptrRange)+100)




      {                            // if(range <= i < range+100)




      cout << i << " ";            // then display int




      return 1;                    // and return 1




      }                            // otherwise,



   return 0;                       // return 0


   }




 



استنسخنا في هذا المثال كائنا للفئة TDoubleListlmp بنوع العدد الصحيح وأدخلنا إليه ثلاث قيم من رأسه ومثلها من ذيله ثم أظهرناها باستخدام الدالة المنتمية ForEach() ودالتا Display() .



الدالة LastThat()



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



وفي مثالنا نطلب من المستخدم أن يحدد نطاق البحث فإذا أدخل 100 مثلا كان نطاق البحث من 100 إلي 199 وإذا أدخل 200 كان النطاق 200 إلي 299 , وهكذا .



وعن طريق المعامل الثاني للدالة [(void*)&Range] LastYhat() نمرر للدالة FindDisplay() مؤشر المتغير Range , لتضعه في الشرط الذي تبحث فيه . ] تتلقاه الدالة FindDisplay في معاملها الثاني – void* ptrRange- فتكون العبارة *(int*)ptrRange معبرة عن قيمة المتغير Range [ .



وكما في حالة الدالة ForEach() يأخذ تعريف الدالة التي تستدعيها LastThat() شكلا نمطيا علي النحو التالي :



Int Condfunc(const aClass&, void*);



وكون المعامل الأول موصفا بأنه ثابت onst يعني ضمنا أن الدالة LastThat() لا يمكنها أن تغير في العنصر المرحل إليها , ويجب أن تكون القيمة المعادة من النوع int لكي تخبر الدالة LastThat() بوجود عنصر موافق للشرط أو لا يوجد ما يوافق الشرط فتعيد 0 فتقوم الأخيرة بإخراج الرسالة التي تعني ذلك .



الدالة FirstThat()



تشابه هذه الدالة السابقة عدا أنها لا تستأنف البحث حينما تجد أول عنصر موافق للشرط .ii



القواميس والروابط



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



وعائلة فئات الروابط مختلفة إلي حد ما عن غيرها من فئات حاويات البيانات , ففي الروابط يمكن أن يخزن القيم (المفتاح أو التعريف) أو مؤشرات لها ويبين الجدول هذه العائلة , وفيه يعبر عن التخزين المباشر direct وهو مميز بالحرف D وعن طريق هذه العائلة وفيه يعبر عن التخزين المباشر direct وهو مميز بالحرف D وعن طريق المؤشرات indirect وهو مميز بالحرف I في التسمية وتحتوي التسمية علي أي من الحرفين I,D في تتابع , الأول منهما للمفتاح والثاني للقيمة التي يرتبط بها أما إضافة الحرف M فيعبر عن إدارة الذاكرة كما سبق ذكره .



جدول عائلة الروابط TDDAssiciation



Class Name key value Memory



Management



TDDAssiciat Direc Direc Standard



Ion t t



TDIAssiciat Direc Indir Standard



On t ect



TIDAssiciati Indir Direc Standard



On ect t



TIIAssiciatio Indir Indir Standard



N ect ect



TMDDAssici Direc Direc Custom



Ation t t



TMDIAssici Direc Indir Custom



Ation t ect



TMIDAssici Indir Direc Custom



Ation ect t



TMIIAssicia Indir Indir Custom



Tion ect ect



ويبين الجدول دوال الفئة TDDAssiciation









































الدالة



الغرض منها



HashValue



تعيد قيمة تسكين المفتاح



Key



تعيد قيمة المفتاح



Value



تعيد القيمة المرتبط بها المفتاح



operator



تختبر التساوي بين القيم



==



المفتاحية





العناوين التسكينية



القيمة التسكينية ivhash value هو قيمة لعنوان مشتق من قيمة المفتاح بأسلوب معين , وهي طبقا لتقليد بورلاند عدد من النوع unsigned int يشتق من طبيعة المفتاح . فإن كان المفتاح رقما , يمكن أن يؤخذ هو كقيمة تسكينية , أما إذا كان عبارة نصية , يمكن أن تكون القيمة التسكينية عدد حروفها . ويقوم القاموس بتحويل القيم التسكينية إلي أدلة توضع في جدول يسمي "الجدول التسكيني hash table" توضع فيه العناوين , مما يفيدنا في عملية البحث .



ويعني ذلك اشتراط أن ينتمي المفتاح لفئة تتضمن دالة تقوم بحساب القيم التسكينية وفي نظام بورلاند يشترط أن تكون الدالة بإسم Hash Value() , يجب أن تتضمنها أية فئة تعمل كمفتاح .



وسوف نقدم لك مثالا لقاموس المصطلحات البحرية , حين ندخل المصطلح , يخرج لك البرنامج ما يعنيه , وتخزن القيم الفتاحية والقيم المقابلة لها في مصفوفتين محرفتين , وسوف نتخذ الفئة TDIctionaryAsHashTable لتخزين روابط الفئة TDDAssociation . كما سنستخدم الفئة المكتبية string لكل من أسماء المفاتيح والعبارات النصية المقابلة .



ولكن الفئة string لا تحتوي علي الدالة HashValue() التي يشترط وجودها ولذا فسوف نشتق من تلك الفئة فئة تتضمن دالة منتمية بهذا الاسم , هي hvString هذه الفئة لا تحتاج إلا للدالة المذكورة , بادئتين , ثم تتمتع بعد ذلك بكافة إمكانيات الفئة string , وإليك الصياغة :



 



Dict.cpp




// dict.cpp


// demonstrates TDictionaryAsHashTable


// and TDDAssociation classes


// UCS Laboratories


 


#include <classlib\dict.h>   // for TDictionaryAsHashTable class


#include <classlib\assoc.h>  // for TDDAssociation class


#include <cstring.h>         // for string class


 


// need this class because HashValue() is not a member of string


class hvString : public string


   {


   public:


      hvString() : string()


         { }


      hvString(const char* str) : string(str)


         { }


      unsigned HashValue() const


         { return hash(); }


   };


 


const int ENTRIES = 9;


hvString term[ENTRIES] =


            { "Athwartships",


              "Bight",


              "Chine",


              "Deviation",


              "Eye of the wind",


              "Full and by",


              "Gunwale",


              "To heave to",


              "In irons" };


hvString meaning[ENTRIES] =


            { "At right angles to the boat",


              "A loop in a rope",


              "Transition between a boat's topsides and bottom",


              "Compass error due to metallic objects aboard",


              "The direction exactly upwind",


              "Sailing close-hauled without luffing",


              "Rail where the topsides join the deck",


              "To slow the boat by backwinding the jib",


              "Head to wind and unable to bear off" };


 


typedef TDDAssociation<hvString, hvString> StrAssoc;


 


void main()


//UCS Laboratories


   {


   hvString::set_case_sensitive(0);  // ignore case


                                     // create dictionary


   TDictionaryAsHashTable<StrAssoc> dictionary(ENTRIES);


   void Display(StrAssoc&, void*);   // prototype


   StrAssoc* ptrSA;                  // pointer to associations


   char buffer[80];                  // for input strings


   hvString hvSearch;                // hvString to search for


   char ch;


 


   for(int j=0; j<ENTRIES; j++)      // for every entry,


      {


      StrAssoc sa(term[j], meaning[j]);    // create association


      dictionary.Add(sa);                  // put it in dictionary


      }


 


   dictionary.ForEach(Display, 0);   // display contents


 


   do


      {


      cout << "Enter nautical term: ";


      cin.getline(buffer, 80);       // get term from user


      hvString hvSearch(buffer);     // make an hvString


                                     // find term in dictionary


      StrAssoc tempAssoc(hvSearch, 0);     // create association


      ptrSA = dictionary.Find(tempAssoc);  // look for it


      if( ptrSA==0 )


         cout << "Term not found";


      else                           // display, with definition


         cout << ptrSA->Key() << ": " << ptrSA->Value();


      cout << "\nDo another (y/n)? ";


      cin >> ch;


      cin.ignore(80, '\n');          // eat extra chars


      }


   while(ch != 'n');


   }


 


// function to display term and meaning for one association


void Display(StrAssoc& sa, void*)


   {


   cout << sa.Key() << "\n   " << sa.Value() << endl;


   }




 



الكلمة الحاكمة typedef



تلاحظ أننا استخدمنا هذه الكلمة في الأمر :



Typed TDDAssociation<hvString, hvString> StrAssoc;



فما معني هذا الأمر ؟ تتيح لك لغة السي أن تطلق علي بعض أنواع البيانات أسماء من لدنك , كأن تطلق علي النوع unsigned char الاسم uchar . ويكون ذلك إما لاختصار اسم مطول , أو لإعطائه معني أكثر وضوحا في البرنامج . فلو أنك استخدمت الأمر :



Typedef unsigned char uchar;



فإنه يصبح بإمكانك كتابة عبارة مثل :



Uchar ch1, ch2;



لتعريف المتغيرين بالنوع المذكور . والكلمة الحاكمة التي تتيح لك ذلك هي typedef اختصار لكلمة type definition وعلي هذا الأساس فقد استخدمت هذه الوسيلة لاختصار الاسم المطول TDDAssociation<hvString> إلي StrAssoc . وإليك صياغة البرنامج :



Dict.cpp



استخدام القاموس :




استخدمنا الدالة set_case_senstive() المنتمية للفئة string بمعامل صفر لكي تجعل المستخدم محررا من الانتباه إلي الحروف الكبيرة أو الصغيرة . وقد أنشأنا قاموسا من نوع TDictionaryAsHashTable<StrAssoc> , وبعد ذلك أنشأنا ارتبطا لكل زوج من التعريفات وأضفناه للقاموس .



ويحتوي البرنامج علي أسلوبين لإظهار محتويات القاموس , الأولي باستخدام الدالتين ForEach(), Display() المعتادتين لإظهار كل محتوياته , وهو أسلوب غير عملي , والثانية بالطريقة العادية , إدخال المفتاح وإظهار الترابط باستخدام الدالة Find() وتعيد هذه الدالة صفرا إذا لم تجد المفتاح المطلوب , أو مؤشرا له إذا وجدته , عندئذ تقوم الدالتان Key(), Value() بإظهار الترابط .



الفئات المرتبة



تحتوي فئات الحاويات من مصفوفات وقوائم علي مجموعة تقوم بترتيب محتوياتها أليا وهي المميزة بالحرف S في تسميتها . والبرنامج التالي يقدم مصفوفة من هذا القبيل .



 



Sortary.cpp




// sortaray.cpp


// demonstrates sorted array with TSArrayAsVector class


#include <classlib\arrays.h>          // for TSArrayAsVector


#include <iostream.h>


 


void main()


   {


   TSArrayAsVector<float> arrflo(10); // create array of 10 floats


 


   arrflo.Add(7.2);                   // insert floats


   arrflo.Add(7.4);                   


   arrflo.Add(7.5);


   arrflo.Add(7.3);


   arrflo.Add(7.1);


                                      // display all floats


   for(int j=0; j<arrflo.GetItemsInContainer(); j++)


      cout << "arrflo[" << j << "]=" << arrflo[j] << endl;


   }




 



في هذه المجموعة , يقع عبء إضافي علي الدالة Add() لتقوم بتغيير أوضاع المحتويات في كل مرة . ولا توجد في هذه الفئة الدالة AddAt() حيث لا داعي لها .



التخزين غير المباشر



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



 



Indaray.cpp




// indaray.cpp


// demonstrates indirect array with TIArrayAsVector class


#include <classlib\arrays.h>          // for TIArrayAsVector


#include <iostream.h>


 


void main()


   {


   float f1 = 7.1;


   float f2 = 7.2;


   float f3 = 7.3;


   float f4 = 7.4;


   float f5 = 7.5;


 


   TIArrayAsVector<float> arrflo(10); // create array of 10


                                      //    pointers to float


   arrflo.Add(&f1);                   // insert addresses


   arrflo.Add(&f2);                   //    of floats


   arrflo.Add(&f3);


   arrflo.Add(&f4);


   arrflo.Add(&f5);


                                      // display all floats


   for(int j=0; j<arrflo.GetItemsInContainer(); j++)


      cout << "*arrflo[" << j << "]=" << *arrflo[j] << endl;


   }




 



وطبعا لا نريد إظهار البيان المحتوي في الحاوية , بل ما يشير إليه , ومن ثم كان استخلاص القيم بمؤثر اللامباشرة *.



تخزين أنواع خاصة بالمبرمج



من أهم مزايا فئات حاويات بورلاند المكتبية أنه بإمكانك أن تستخدمها لما تضع من فئات كما تفعل مع البيانات الأولية للنظام سواء بسواء . ومثالنا التالي يطبق هذه الفكرة علي فئة person لتخزين في الحاوية TArrayAsVector .



 



Persaray.cpp




// persaray.cpp




// demonstrates TArrayAsVector class holding user-defined class




// UCS Laboratories




 




#include <classlib\arrays.h>   // for TArrayAsVector class




#include <cstring.h>           // for string class




#include <iostream.h>




 




class person                   // person class




   {




   private:




      string name;




      int age;




   public:




      person()                 // no-arg constructor




         { }




      person(char* n, int a)   // two-arg constructor




         { name = n; age = a; }




 




      void putPerson() const   // display person data




         { cout << "\n   " << name << "\n   " << age; }




                               // overloaded == operator




      int operator == (const person& p) const




         {




         return (p.name==name && p.age==age) ? 1 : 0;




         }




   };




 




void main()




   {




   TArrayAsVector<person> arrpers(10);  // array for 10 persons




 




   person p1("Gloria", 18);             // create 4 persons




   person p2("George", 25);




   person p3("Harry", 47);




   person p4("Lynette", 34);




 




   arrpers.Add(p1);                     // put persons in array




   arrpers.Add(p2);




   arrpers.Add(p3);




   arrpers.Add(p4);




                                        // access persons




   for(int j=0; j<arrpers.GetItemsInContainer(); j++)




      {




      cout << "\nPerson " << (j+1);




      arrpers[j].putPerson();




      }




   }




 



المثير للدهشة في هذا البرنامج هو زيادة تحميل المؤثر = في الفئة person رغم عدم استخدامنا له في الدالة الاصلية main() . السبب هو أن الحاويات المكتبية قد تحتاج ذلك لمؤثرات معينة –وأيضا لدوال معينة –للعمل بها مع الفئة التي تستخدم لها الحاوية , وهو أمر يجب الرجوع لوثائق الشركة لتبيانه , كما يمكن الانتباه إلي عدم مراعاة ذلك مما يصدره محول الصياغة من رسائل أخطاء . فمثلا تحتاج الفئات المميزة بالحرف S (فئات الترتيب) إلي زيادة تحميل المؤثر < .



وفي مثالنا هذا لو أنك حذفت الجزء الخاص بزيادة تحميل المؤثر المذكور , لحصلت علي الرسالة :



\bc4\include\classlib\vectimp.h 577 lllegal structure operatrion



فإذا ما نقرت مزدوجا علي هذه الرسالة , حصلت علي العبارة المسببة للمشكلة , كالتالي :



If( Data[Loc] == t )



وهي العبارة التي حاول المترجم صياغتها , وكان ذلك يتطلب زيادة تحميل المؤثر المبين بها .



تخزين الفئة person في حاوية TListLmp:



تقدم لك الأن مثالا لتخزين كائنات الفئة المبينة في قائمة , وفيه نستخدم إمكانيات الدوال ForEach(), LastThat()





listclas.cpp




// listclas.cpp




// demonstrates TListImp class holding user-defined class




 




#include <classlib\listimp.h>      // for TListImp class




#include <Cstring.h>               // for string class




#include <iostream.h>




 




class person                       // person class




   {




   private:




      string name;




      int age;




   public:




      void getPerson()             // get person data from user




         {




         cout << "   Name: ";




         cin >> name;




         cout << "   Age: ";




         cin >> age;




         }




      void putPerson() const       // display person data




         {




         cout << "   " << name << ", " << age;




         }




                                   // compare with a name




      int isSameName(string* MatchName) const




         {




         return name==*MatchName;




         }




                                   // check for equality




      int operator == (person& p) const




         {




         if( this->name==p.name && this->age==p.age )




            return 1;




         return 0;




         }




   };




 




void Display(person&, void*);      // prototypes




int FindDisplay(const person&, void*);




 




void main()




//UCS Laboratories




   {




   TListImp<person> listpers;      // list of persons




   person tempers;                 // temporary person holder




   string Sname;                   // name to search for




   char ch;




 




   do




      {




      tempers.getPerson();         // get data for one person




      listpers.Add(tempers);       // store copy of on list




      cout << "Do another (y/n)? ";




      cin >> ch;




      }




   while( ch != 'n');




 




   cout << "\nPersons on list: ";




   listpers.ForEach(Display, 0);   // display all persons




   cout << endl;




 




   cout << "\nEnter name to search for: ";




   cin >> Sname;




   cout << "Persons with that name: ";




   if( !listpers.LastThat(FindDisplay, (void*)&Sname) )




      cout << "No such values";




   }  // end main()




 




// function displays a person passed as argument




void Display(person& pers, void*)




   {




   cout << endl;




   pers.putPerson();




   }




 




// function displays person passed as argument, if name matches




int FindDisplay(const person& pers, void* ptrName)




   {




   if( pers.isSameName( (string*)ptrName) )




      {                            // if name matches




      cout << endl;




      pers.putPerson();            // display person




      return 1;                    // and return 1




      }                            // otherwise,




   return 0;                       // return 0




   }







وتري من تجربة البرنامج انه حينما صادفت الدالة LastThat اسمين متشابهين , أخرجتهما معا .



قاعدة بيانات لجدول زمني



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



 



Schlist.cpp





// schlist.cpp


// database for shipping schedule; uses a sorted list


// UCS Laboratories


 


#include <cstring.h>           // for string class


#include <classlib\date.h>     // for TDate class


#include <classlib\time.h>     // for TTime class


#include <classlib\listimp.h>  // for TSListImp class


#include <iostream.h>          // for stream functions


#include <process.h>           // for exit()


 


void getstring(string&);       // prototype


 


class Voyage                   // single port-to-port voyage


   {


   private:


      string embarkation;      // departure port


      string debarkation;      // arrival port


      string shipname;         // name of vessel


      TDate  emdate;           // departure date


      TTime  emtime;           // departure time


   public:


      void putData() const


         {


         emtime.PrintDate(0);  // don't print date with time


         cout << "\nFrom " << embarkation


              << ", To " << debarkation


              << ", on vessel " << shipname << endl


              << "   Departing " << emdate << ", at "


              << emtime.Hour() << ':' << emtime.Minute();


         }


      void getData()


         {


         char dummy;


         int ihours, iminutes;


 


         cout << "\n   Embarkation port: ";


         getstring(embarkation);


         cout << "   Destination port: ";


         getstring(debarkation);


         cout << "   Ship name: ";


         getstring(shipname);


         cout << "   Departure date: ";


         cin >> emdate;


 


         cout << "   Departure time (24-hour format, 23:59): ";


         cin >> ihours >> dummy >> iminutes;


         TTime t(emdate, ihours, iminutes);


         emtime = t;


         cin.ignore(100, '\n');


         }


 


      int isSameDate(TTime* pt) const


         { return (emtime == *pt); }


 


      int isSameEmbark(string* pstr) const


         { return (embarkation == *pstr); }


 


      int isSameDebark(string* pstr) const


         { return (debarkation == *pstr); }


 


      int isSameShip(string* pstr) const


         { return (shipname == *pstr); }


                                           // friends


      friend int operator == (const Voyage&, const Voyage&);


      friend int operator < (const Voyage&, const Voyage&);


   };


 


int operator == (const Voyage& v1, const Voyage& v2)


   {


   if( v1.embarkation == v2.embarkation &&


       v1.debarkation == v2.debarkation &&


       v1.shipname    == v2.shipname &&


       v1.emdate      == v2.emdate &&


       v1.emtime      == v2.emtime )


      return 1;


   else


      return 0;


   }


                                           // overloaded <


int operator < (const Voyage& v1, const Voyage& v2)


   {


   return (v1.emtime < v2.emtime);


   }


 


void getstring(string& s)                  // input a string


   {                                       // with embedded


   char buff[80];                          // blanks;


   cin.ignore(100, '\n');                  // convert to


   cin.get(buff, sizeof(buff), '\n');      // string  object


   s = buff;


   }


 


////////////////////////////////////////////////////////////////


void add_voyage();                         // prototypes


void delete_voyage();


void find_voyage();


void list_voyages();


 


TSListImp<Voyage> VoyageList;              // create master list


////////////////////////////////////////////////////////////////


 


void main()                                // get user's choice


   {                                       // and carry it out


   char ch;


 


   while(1)


      {


      cout << "\n\nEnter a to add a voyage"


                "\n      d to delete a voyage"


                "\n      f to find a voyage"


                "\n      l to list all voyages"


                "\n      x to exit"


                "\nSelection: ";


      cin >> ch;


      switch(ch)


         {


         case 'a': add_voyage();    break;


         case 'd': delete_voyage(); break;


         case 'f': find_voyage();   break;


         case 'l': list_voyages();  break;


         case 'x': exit(0);         break;


         default: cout << "\nNo such command"; break;


         }  // end switch


      }  // end while


   }  // end main


 


void ShowVoyage(Voyage& voy, void*)         // used by ForEach()


   {


   voy.putData();


   }


 


int FindDate(const Voyage& voy, void* pt)   // used by FirstThat()


   {


   return voy.isSameDate( (TTime*)pt );


   }


 


int matchEmbark( const Voyage& voy, void* pstr)


   {


   int i = voy.isSameEmbark( (string*)pstr );


   if( i )


      voy.putData();


   return i;


   }


 


int matchDebark(const Voyage& voy, void* pstr)


   {


   int i = voy.isSameDebark( (string*)pstr );


   if( i )


      voy.putData();


   return i;


   }


 


int matchShip(const Voyage& voy, void* pstr)


   {


   int i = voy.isSameShip( (string*)pstr );


   if( i )


      voy.putData();


   return i;   


   }


 


void add_voyage()                          // add a voyage


   {


   Voyage vtemp;


 


   cout << "\nEnter data for a voyage";


   vtemp.getData();


   if( !VoyageList.Add(vtemp) )


      { cerr << "\nCan't add voyage to list"; }


   }


 


void list_voyages()                        // list all voyages


   {


   cout << "\nSCHEDULE\n--------";


   if( VoyageList.IsEmpty() )


      { cout << "\nNo voyages listed"; return; }


   VoyageList.ForEach(ShowVoyage, 0);


   }


 


void delete_voyage()                       // delete a voyage


   {


   int ihours, iminutes;


   char ch;


   Voyage vtemp;


   Voyage* pVoy;


   TDate vdate;


 


   cout << "\nEnter date and time of voyage to be deleted";


   cout << "\n   Date (format 12/31/91): ";


   cin >> vdate;


   cout <<   "   Time (24-hour format 23:59): ";


   cin >> ihours >> ch >> iminutes;


   TTime vtime(vdate, ihours, iminutes);


   cin.ignore(100, '\n');


                                           // search for voyage


   pVoy = VoyageList.FirstThat(FindDate, (void*)&vtime);


   if(!pVoy)


      { cout << "No voyage with that time and date."; return; }


   cout << "\nVoyage With This Time and Date:";


   pVoy->putData();


   cout << "\nAre you sure you want to delete it (y/n)? ";


   cin >> ch;


   if(ch=='y')


      VoyageList.Detach(*pVoy);


   }


 


void find_voyage()                         // find a voyage


   {


   char ch;


   string s;


 


   cout << "\nEnter e to find embarkation port"


           "\n      d to find debarkation port"


           "\n      s to find name of ship"


           "\nSelection: ";


   cin >> ch;


   switch(ch)                              


      {


      case 'e':


         cout << "\nEnter embarkation port: ";


         getstring(s);


         if( !VoyageList.LastThat(matchEmbark, (void*)&s) )


            cout << "No match for embarkation " << s;


         break;


      case 'd':


         cout << "\nEnter debarkation port: ";


         getstring(s);


         if( !VoyageList.LastThat(matchDebark, (void*)&s) )


            cout << "No match for debarkation " << s;


         break;


      case 's':


         cout << "\nEnter vessel name: ";


         getstring(s);


         if( !VoyageList.LastThat(matchShip, (void*)&s) )


            cout << "No match for vessel name " << s;


         break;


      default: cout << "\nNo such selection";


      }


   }




 






يستخدم البرنامج حاوية القائمة المرتبة TSListlmp , وهي تحتاج كما قدمنا إلي مؤثر مقارنة زائد التحميل لمقارنة رحلتين علي أساس زمن وصولهما . ولتمكين المستخدم من إدخال عبارات نصية تحتوي علي مسافات , أنشأنا فئة خاصة بنا هي getstring() , وهي تتقبل عبارات نصية باستخدام cim.get ثم تحولها إلي كائن من الفئة string .






موجز



تقدم بورلاند في مكتبتها عديدا من الفئات التي تقدم العديد من الوظائف الشائعة في التعامل مع البيانات أو هياكلها التي تقوم بمهام تخزينها . وتعفي هذه الفئات المبرمج من عبء وضع فئات مماثلة لكل موقف بجد نفسه محتاجا لوظيفة من تلك الوظائف .



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

التسميات: