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