الدوال الصديقة في ++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;
تعليقات
إرسال تعليق