الكائنات كمعاملات للدوال في ++C
الكائنات كمعاملات للدوال
البرنامج التالي صورة معدلة من البرنامج englobj.cpp يضم عدة مفاهيم جديدة متعلقة بالفئات ؛ زيادة تحميل , وتعريف دوال خارج الفئة , ثم , وربما الأهم , استخدام الكائنات كمعاملات الدوال .
englcon.cpp
// englcon.cpp
// constructors, adds objects using member function
// UCS Laboratories
#include <iostream.h>
#include <conio.h>
class Distance // English Distance class
{
private:
int feet;
float inches;
public:
Distance() // constructor (no args)
{ }
Distance(int ft, float in) // constructor (two args)
{ feet = ft; inches = in; }
void getdist() // get length from user
{
cout << "\nEnter feet: "; cin >> feet;
cout << "Enter inches: "; cin >> inches;
}
void showdist() // display distance
{ cout << feet << "\'-" << inches << '\"'; }
void add_dist( Distance, Distance ); // declaration
};
// add lengths d2 and d3
void Distance::add_dist(Distance d2, Distance d3)
{
inches = d2.inches + d3.inches; // add the inches
feet = 0; // (for possible carry)
if(inches >= 12.0) // if total exceeds 12.0,
{ // then decrease inches
inches -= 12.0; // by 12.0 and
feet++; // increase feet
} // by 1
feet += d2.feet + d3.feet; // add the feet
}
void main()
{
Distance dist1, dist3; // define two lengths
Distance dist2(11, 6.25); // define and initialize dist2
dist1.getdist(); // get dist1 from user
dist3.add_dist(dist1, dist2); // dist3 = dist1 + dist2
// display all lengths
cout << "\ndist1 = "; dist1.showdist();
cout << "\ndist2 = "; dist2.showdist();
cout << "\ndist3 = "; dist3.showdist();
getche();
}
يبدأ البرنامج بمسافة تعطي قيمة ابتدائية يضاف إليها قيمة تدخل من المستخدم , ثم يخرج ناتج جمع القيمتين . ويكون الحوار علي الشكل التالي :
Enter feet: 17
Enter inches: 5.75
Dist1 = 17'-5.75''
Dist2 = 11'-6.25''
Dist3 = 29'-0''
استهلاك الكائنات
قمنا بالأمر :
Distance distance2(11, 6.25);
باستهلاك الكائن distance2 المنتمي للفئة distanc بالقيم المبينة , بالضبط كما نستهل متغيرا بالأمر :
Int x=3;
والذي مكننا من تطبيق إمكانية استهلاك المتغيرات علي الكائنات أننا أنشأنا بادئة , أي دالة منتمية تلقائية العمل , في الفئة , ألا وهي :
Distance(int ft, int in)
{ feet = ft; inches = in; }
وهي دالة ذات معاملين كما تري . وتقوم هذه الدالة بتخصيص القيم المدخلة الأول للبيان الخاص بالأقدام والثاني للبيان الخاص بالبوصات فالأمر المذكور يقوم بتعريف الكائن واستدعاء البادئة في نفس الوقت .
زيادة تحميل البادئات
يحتوي تعريف الفئة علي بادئين , الأولي منهما بلا معاملات , والثانية بمعاملين , وقد فرغنا توا من معرفة فائدتها , إنها التي مكنتا من إستغلال إمكانية الاستهلال . فما فائدة البادئة الأولي ؟
ولكنا أيضا نريد تعريف كائنات دون استهلال , كما صنعنا في البرنامج engobj حين عرفنا كائنين بالأمر :
englobj.cpp
// englobj.cpp
// objects using English measurements
#include <iostream.h>
#include <conio.h>
class Distance // English Distance class
{
private:
int feet;
float inches;
public:
void setdist(int ft, float in) // set distance to args
{ feet = ft; inches = in; }
void getdist() // get length from user
{
cout << "\nEnter feet: "; cin >> feet;
cout << "Enter inches: "; cin >> inches;
}
void showdist() // display distance
{ cout << feet << "\'-" << inches << '\"'; }
};
void main()
{
Distance dist1, dist2; // define two lengths
dist1.setdist(11, 6.25); // set dist1
dist2.getdist(); // get dist2 from user
// display lengths
cout << "\ndist1 = "; dist1.showdist();
cout << "\ndist2 = "; dist2.showdist();
getche();
}
Distance dist1, dist2
وقد فعلنا ذلك بنجاح دون أن ننشئ بادئة تتولي تخصيص قيم الأقدام والبوصات عند إدخالها .
الإجابة أن لغة السي++ تنشئ البادئة الضمنية التي ذكرناها في كافة اللغات البرمجية حين لا يقوم المبرمج بتعريف أي بادئ تتقبل فيه القيم . ولكن حين يقوم المبرمج بإنشاء بادئات من لدنه للقيام بأغراض معينة , فإنه يجب أن يشير صراحة لهذه البادئة الضمنية , وهو الذي حققه البادئة الفارغة من المعاملات .
ويعرف محول الصياغة أي البادئتين هي المقصود , فحين يتضمن أحد الأوامر معاملي الاستهلال , يقوم البرنامج بتشغيل البادئة ذات المعاملين , وحين لا يتضمن الأمر أيه معاملات , يقوم بتشغيل البادئة الخالية منها .
وطالما أن لدينا بادئتين بنفس الاسم , يقومان بغرضين مختلفين , فإننا نطلق علي ذلك زيادة تحميل البادئات Constructors overloading .
الدالة المعرفة خارج الفئة
تلاحظ في البرنامج أن الدالة add_dist() قي أكتفي في تعريف الفئة بالإعلان عنها , ثم ترك تعريفها خارج الفئة , وقد استخدم لذلك مؤثر جديد هو "::" , وذلك في الأمر :
Void distance :: add_dist(distance d2, distance d3)
ويفيد المؤثر المذكور أن الدالة المعرفة هي دالة منتمية للفئة المذكورة , ويسمي " مؤثر النطاق scope resolution operator . ويبين الشكل صيغة هذا المؤثر . ] تستخدم هذه الطريقة في الدوال الكبيرة لتبسيط صياغة الفئة [ .
شكل صيغة المؤثر scope resolution operator
الكائنات كمعاملات للدوال
نستطيع الأن أن نشرح كيف يعمل البرنامج ؛ عن طريق البادئة الخالية من المعاملات أنشئ الكائنان distance 1, distance3 , وعن طريق البادئة ذات المعاملين أنشئ الكائن distanc2 مستهلا بالقيم المرحلة إلي معاملاته . وتدخل قيم الكائن distance 1 عن طريق الدالة getdist() , والتي تحصل علي القيم من المستخدم .
وللحصول علي قيم الكائن distance3 يرحل الكائنان الأول والثاني كمعاملات للدالة add_dist() , وصيغة الترحيل تماثل ترحيل قيم المتغيرات البسيطة , مجرد ذكر اسم الكائن كمعامل للدالة . وحيث إن الدالة هي دالة منتمية للفئة , فإنه بإمكانها التعامل مع كافة عناصر الكائنتين الموجودين في معاملاتها , وهما dist1, dist2 وكذلك الكائن التي استدعيت به , وهو dist3 فحينما تتعامل مع المتغير feet , يكون التعامل مع العنصر dist3.feet . وفي الفصل الثاني عشر سوف نري أن الدالة المنتمية يمكنها أن تشير ألي الكائن بأكمله , باستخدام الكلمة الحاكمة this.
لاحظ أن دالة الجمع قد عرفت بأنها void , بمعني أنها لا تعيد قيما فنتيجة الجمع تدخل مباشرة في الكائن dist3 .
استهلال الكائن بكائن – بادئة النسخ الضمنية
يمكنك استهلال الكائن بكائن من نفس فئته , والمفاجأة هنا أنك غير محتاج لبادئة للقيام بذلك فهي متضمنة تلقائيا في كل الفئات , تسمي "بادئة النسخ الضمنية default copy constructor " وتحتوي علي معامل واحد . والبرنامج التالي يعطيك فكرة عن إستخدامه :
ecopycon.cpp
// ecopycon.cpp
// initialize with object, using default copy constructor
// UCS Laboratories
#include <iostream.h>
#include <conio.h>
class Distance // English Distance class
{
private:
int feet;
float inches;
public:
Distance() // constructor (no args)
{ }
// no one-arg constructor
Distance(int ft, float in) // constructor (two args)
{ feet = ft; inches = in; }
void getdist() // get length from user
{
cout << "\nEnter feet: "; cin >> feet;
cout << "Enter inches: "; cin >> inches;
}
void showdist() // display distance
{ cout << feet << "\'-" << inches << '\"'; }
};
void main()
{
Distance dist1(11, 6.25); // two-arg constructor
Distance dist2(dist1); // one-arg constructor
Distance dist3 = dist1; // also one-arg constructor
// display all lengths
cout << "\ndist1 = "; dist1.showdist();
cout << "\ndist2 = "; dist2.showdist();
cout << "\ndist3 = "; dist3.showdist();
getche();
}
وفي البرنامج استهل الكائن dist1 باستخدام البادئة ذات المعاملين , ثم أنشأنا كائنين أخريين كل قد أستهل بالكائن الأول , مستخدمين إمكانية بادئة النسخ الضمنية , وبصورتين الأولي :
Distance distance2 (distance1);
والثانية :
Distance distanc3 = distance1;
وفي مقالات قادمة سوف نبين كيف تنشئ بادئة نسخ لك , بزيادة تحميل البادئات الضمنية .
استعادة كائن من دالة
رأينا في برنامج englcon.cpp كيف يمرر كائن لدالة , والأن سوف نري دالة تعيد كائنا . سوف نعدل البرنامج إلي englret.cpp .
englret.cpp
// englret.cpp
// function returns value of type Distance
#include <iostream.h>
#include <conio.h>
class Distance // English Distance class
{
private:
int feet;
float inches;
public:
Distance() // constructor (no args)
{ feet = 0; inches = 0.0; }
Distance(int ft, float in) // constructor (two args)
{ feet = ft; inches = in; }
void getdist() // get length from user
{
cout << "\nEnter feet: "; cin >> feet;
cout << "Enter inches: "; cin >> inches;
}
void showdist() // display distance
{ cout << feet << "\'-" << inches << '\"'; }
Distance add_dist( Distance ); // add
};
// add this distance to d2
Distance Distance::add_dist(Distance d2) // return the sum
{
Distance temp; // temporary variable
temp.inches = inches + d2.inches; // add the inches
if(temp.inches >= 12.0) // if total exceeds 12.0,
{ // then decrease inches
temp.inches -= 12.0; // by 12.0 and
temp.feet = 1; // increase feet
} // by 1
temp.feet += feet + d2.feet; // add the feet
return temp;
}
void main()
{
Distance dist1, dist3; // define two lengths
Distance dist2(11, 6.25); // define, initialize dist2
dist1.getdist(); // get dist1 from user
dist3 = dist1.add_dist(dist2); // dist3 = dist1 + dist2
// display all lengths
cout << "\ndist1 = "; dist1.showdist();
cout << "\ndist2 = "; dist2.showdist();
cout << "\ndist3 = "; dist3.showdist();
getche();
}
من وجهة نظر المستخدم , لا فرق بين البرنامجين , فهما في الواقع متشابهين بقدر كبير , ولكن الاختلاف يبين طبيعة تعامل الدوال مع الكائنات . ففي البرنامج الأول , أرسلت المسافتان لدالة الجمع كمعاملات لها , وخزنت نتيجة الجمع في الكائن التي تنتمي إليه الدالة ألا وهو dist3 وفي البرنامج الثاني , أخذت المسافة الثانية فقط كمعامل لدالة الجمع , وجمعت علي المسافة التي تنتمي إليها الدالة , ألا وهي dist1 , ثم أعيدت النتيجة للدالة الأصلية لتخصص للمسافة dist3 . ويبين الأمر التالي هذه العملية :
Dist3 = dist1.add_dist(dist2);
عدد المعاملات في الدوال المنتمية
نلفت الانتباه إلي أن دالة الجمع أخذت الصورة التالية :
Distance add_dist(distance); // معامل واحد
بينما أخذت دالة الجمع في البرنامج retstrc بالمقالات السابقة الصورة :
Distance adding(distance, distance)// luhlghk
معني ذلك أن الدالة المنتمية تحتاج إلي عدد من المعاملات أقل بمقدار 1 عن مثيلتها من الدوال العادية , والسبب منطقي , حيث إن الكائن الذي تنتمي إليه الدالة يتضمن المتغير الأخر . وهذا واضح من مقارنة صورتي الاستدعاء فهو في برنامجا :
Dist3 = dist1.add_dist(dist2)
بينما في البرنامج السابق يجب إدراج المتغيرين الداخلين في عملية الجمع في أمر الاستدعاء فكان علي الصورة :
D3 = add(d1, d1)
هنا نصل لقاعدة , هي أن الدالة المنتمية تحتاج إلي عدد عوامل أقل من الدالة العادية بـ 1 , هو المتغير المتضمن في الكائن المنتمية إليه .
فلو أن الدالة لا تحتاج إلا لمعامل واحد , لما احتجنا له , ولنضرب مثلا بدالة تعطي مربع العدد , ولو كانت عادية , لكانت علي الصورة :
V2 = square(v1);
أما لو كانت دالة منتمية , لكانت علي الصورة :
Obj2 = obj1.square();
مثال نطور البرنامج cards.cpp المعطي في الفصل الخامس كتطبيق شامل للبرمجة الكائنية .
cardobj.cpp
// cardobj.cpp
// cards as objects
// UCS Laboratories
#include <iostream.h>
#include <conio.h>
enum Suit { clubs, diamonds, hearts, spades };
const int jack = 11; // from 2 to 10 are
const int queen = 12; // integers without names
const int king = 13;
const int ace = 14;
enum Boolean { false, true };
class card
{
private:
int number; // 2 to 10, jack, queen, king, ace
Suit suit; // clubs, diamonds, hearts, spades
public:
card () // constructor (no args)
{ }
card (int n, Suit s) // constructor (two args)
{ suit = s; number = n; }
void display(); // display card
Boolean isEqual(card); // same as another card?
};
void card::display() // display the card
{
if( number >= 2 && number <= 10 )
cout << number << " of ";
else
switch(number)
{
case jack: cout << "jack of "; break;
case queen: cout << "queen of "; break;
case king: cout << "king of "; break;
case ace: cout << "ace of "; break;
}
switch(suit)
{
case clubs: cout << "clubs"; break;
case diamonds: cout << "diamonds"; break;
case hearts: cout << "hearts"; break;
case spades: cout << "spades"; break;
}
}
Boolean card::isEqual(card c2) // return true if cards equal
{
return ( number==c2.number && suit==c2.suit ) ? true : false;
}
void main()
{
card temp, chosen, prize; // define various cards
int position;
card card1( 7, clubs ); // define & initialize card1
cout << "\nCard 1 is the ";
card1.display(); // display card1
card card2( jack, hearts ); // define & initialize card2
cout << "\nCard 2 is the ";
card2.display(); // display card2
card card3( ace, spades ); // define & initialize card3
cout << "\nCard 3 is the ";
card3.display(); // display card3
prize = card3; // prize is the card to guess
cout << "\nI'm swapping card 1 and card 3";
temp = card3; card3 = card1; card1 = temp;
cout << "\nI'm swapping card 2 and card 3";
temp = card3; card3 = card2; card2 = temp;
cout << "\nI'm swapping card 1 and card 2";
temp = card2; card2 = card1; card1 = temp;
cout << "\nNow, where (1, 2, or 3) is the ";
prize.display(); // display prize card
cout << "? ";
cin >> position; // get user's guess of position
switch (position)
{ // set chosen to user's choice
case 1: chosen = card1; break;
case 2: chosen = card2; break;
case 3: chosen = card3; break;
}
if( chosen.isEqual(prize) ) // is chosen card the prize?
cout << "That's right! You win!";
else
cout << "Sorry. You lose.";
cout << " You chose the ";
chosen.display(); // display chosen card
cout << endl;
getche();
}
لدينا في هذا البرنامج بادئتين , الأول بلا معاملات أنشئ به الكائنات الغير مستهلة : temp, chosen, prize , وبادئة ذات معاملين للكائنات المستهلة : card1, card2 , card3 , ودالة الإظهار , ودالة مقارنة التساوي .
تعليقات
إرسال تعليق