الدوال الصديقة في ++C

c

الدوال الصديقة

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

الأصدقاء هم الصلة

لنفرض أنك تريد لدالة أن تتعامل في نفس الوقت مع بيانات كائنين منتمين لفئتين مختلفتين , في موقف كهذا ليس غير دالة صديقة friend function تقوم بدور صلة بين الكائنين . وإليك يطبق هذه الفكرة .

friend.cpp


 


// friend.cpp


// friend functions


#include <iostream.h>


 


class beta;              // needed for frifunc declaration


 


class alpha


   {


   private:


      int data;


   public:


      alpha()  { data = 3; }            // no-arg constructor


      friend int frifunc(alpha, beta);  // friend function


   };


 


class beta


   {


   private:


      int data;


   public:


      beta()  { data = 7; }             // no-arg constructor


      friend int frifunc(alpha, beta);  // friend function


   };


 


int frifunc(alpha a, beta b)            // function definition


   {


   return( a.data + b.data );


   }


 


void main()


   {


   alpha aa;


   beta bb;


   cout << frifunc(aa, bb);             // call the function


   }




لدينا في البرنامج فئتين , تقوم بادئة كل فئة بتحديد قيمة عنصر البيانات فيها . ودالة frifunc(), تقوم بعملية جمع البيانين المنتمين للفئتين , وتعيد حاصل الجمع , فهي كما تري باستطاعتها التعامل مع عنصر البيانات فيهما .



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



اقتحام الحدود



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



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



إليك البرنامج التالي الذي يطبق فيه عملية زيادة التحميل علي المؤثر + لجمع مسافتين بالأقدام والبوصات :






nofri.cpp


 


// nofri.cpp


// limitation to overloaded + operator


// UCS Laboratories


#include <iostream.h>


 


class Distance                    // English Distance class


   {


   private:


      int feet;


      float inches;


   public:


      Distance()                  // constructor (no args)


     { feet = 0; inches = 0.0; }


      Distance( float fltfeet )   // constructor (one arg)


     {                        // convert float to Distance


     feet = int(fltfeet);           // feet is integer part


     inches = 12*(fltfeet-feet);    // inches is what's left


     }


      Distance(int ft, float in)  // constructor (two args)


     { feet = ft; inches = in; }


      void showdist()             // display distance


     { cout << feet << "\'-" << inches << '\"'; }


      Distance operator + (Distance);


   };


                  // add this distance to d2


Distance Distance::operator + (Distance d2)   // return the sum


   {


   int f = feet + d2.feet;        // add the feet


   float i = inches + d2.inches;  // add the inches


   if(i >= 12.0)                  // if total exceeds 12.0,


      { i -= 12.0; f++;  }        // less 12 inches, plus 1 foot


   return Distance(f,i);          // return new Distance with sum


   }


 


void main()


   {


   Distance d1 = 2.5;               // constructor converts


   Distance d2 = 1.25;              // meters to Distance


   Distance d3;


   cout << "\nd1 = "; d1.showdist();


   cout << "\nd2 = "; d2.showdist();


 


   d3 = d1 + 10.0;                  // distance + float: ok


   cout << "\nd3 = "; d3.showdist();


// d3 = 10.0 + d1;                  // float + Distance: ERROR


// cout << "\nd3 = "; d3.showdist();


   }




في البرنامج تم زيادة تحميل المؤثر + لكي يجمع كائنين من فئة Distance لإجراء ذلك يبحث عن كائنين من نفس الفئة علي طرفي المؤثر فماذا لو طبقناه في الأمر التالي :



D3 = d1 + 10.0;



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



D3 = 10.0 + d1;



فلن يقبله المترجم , لماذا ؟ لأن المقدار إلي اليمين من نوع Distance وهو لا يستطيع القيام بالعملية العكسية أي تحويل هذا النوع إلي عدد كسري .



هناك حل نعرفه و استخدام التحويل القسري علي الصورة التالية :



D3 = Distance(10.0) + d1;



ولكن الدوال الصديقة تعطينا حلا أكثر قبولا , كما تري في البرنامج التالي :






frengl.cpp


 


// frengl.cpp


// friend overloaded + operator


// UCS Laboratories


#include <iostream.h>


 


class Distance                    // English Distance class


   {


   private:


      int feet;


      float inches;


   public:


      Distance()                  // constructor (no args)


     { feet = 0; inches = 0.0; }


      Distance( float fltfeet )   // constructor (one arg)


     {                        // convert float to Distance


     feet = int(fltfeet);           // feet is integer part


     inches = 12*(fltfeet-feet);    // inches is what's left


     }


      Distance(int ft, float in)  // constructor (two args)


     { feet = ft; inches = in; }


      void showdist()             // display distance


     { cout << feet << "\'-" << inches << '\"'; }


      friend Distance operator + (Distance, Distance);  // friend


   };


                  // add D1 to d2


Distance operator + (Distance d1, Distance d2) 


   {


   int f = d1.feet + d2.feet;        // add the feet


   float i = d1.inches + d2.inches;  // add the inches


   if(i >= 12.0)                  // if inches exceeds 12.0,


      { i -= 12.0; f++;  }        // less 12 inches, plus 1 foot


   return Distance(f,i);          // return new Distance with sum


   }


 


void main()


   {


   Distance d1 = 2.5;                // constructor converts


   Distance d2 = 1.25;               // float-feet to Distance


   Distance d3;


   cout << "\nd1 = "; d1.showdist(); 


   cout << "\nd2 = "; d2.showdist();


 


   d3 = d1 + 10.0;                   // distance + float: ok


   cout << "\nd3 = "; d3.showdist();


   d3 = 10.0 + d1;                   // float + Distance: ok


   cout << "\nd3 = "; d3.showdist();


   }




صنع المؤثر الزائد التحميل هذه المرة من دالة صديقة , هي :



Friend Distance operator + (distance d1, Distance d2)



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



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






misq.cpp


 


// misq.cpp


// member square() function for Distance


// UCS Laboratories


#include <iostream.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 showdist()             // display distance


     { cout << feet << "\'-" << inches << '\"'; }


 


      float square();             // member function


   };


 


float Distance::square()          // return square of


   {                              // this Distance


   float fltfeet = feet + inches/12;    // convert to float


   float feetsqrd = fltfeet * fltfeet;  // find the square


   return feetsqrd;               // return square feet


   }


 


void main()


   {


   Distance dist(3, 6.0);         // two-arg constructor (3'-6")


   float sqft;


 


   sqft = dist.square();          // return square of dist


                  // display distance and square


   cout << "\nDistance = "; dist.showdist();


   cout << "\nSquare = " << sqft << " square feet";


   }




وتري أن أمر التربيع هو :



Sqft = dist.square();



والأن إليك البرنامج الثاني :






frisq.cpp


 


// frisq.cpp


// friend square() function for Distance


// UCS Laboratories


#include <iostream.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 showdist()             // display distance


     { cout << feet << "\'-" << inches << '\"'; }


 


      friend float square(Distance);  // friend function


   };


 


float square(Distance d)          // return square of


   {                              // this Distance


   float fltfeet = d.feet + d.inches/12;  // convert to float


   float feetsqrd = fltfeet * fltfeet;    // find the square


   return feetsqrd;               // return square feet


   }


 


void main()


   {


   Distance dist(3, 6.0);         // two-arg constructor (3'-6")


   float sqft;


 


   sqft = square(dist);           // return square of dist


                  // display distance and square


   cout << "\nDistance = "; dist.showdist();


   cout << "\nSquare = " << sqft << " square feet";


   }




وتري أن أمر التربيع قد أخذ الصورة :



Sqft = square(dist);



هذا ويمكنك أن تجعل كافة دوال فئة beta صديقة لفئة alpha بإعلان الأولي صديقة في تعريف الثانية , علي النحو التالي :



Class alpha



{



.



.



Friend class beta;

التسميات: