الكائنات كمعاملات للدوال في ++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



Object-Oriented Programming in C   _Page_0262_Image_0001



الكائنات كمعاملات للدوال



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

التسميات: