زيادة تحميل المؤثرات الثنائية في ++C
زيادة تحميل المؤثرات الثنائية
المؤثرات الحسابية
في برنامج englcon.cpp بالمقالات السابقة استخدمنا الدالة add_dist() لكي يمكننا جمع مسافتين , بالأمر :
Dist3.add_dist(dist, dist);
وسوف نعيد صياغة البرنامج بزيادة تحميل المؤثر "+" حتي يمكننا وضع الأمر علي الصيغة :
Dist3 = dist1 + dist2;
englplus.cpp
// englplus.cpp
// overloaded '+' operator adds two Distances
// UCS Laboratories
#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 operator + ( Distance ); // add two distances
};
// 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,
{ // then decrease inches
i -= 12.0; // by 12.0 and
f++; // increase feet by 1
} // return a temporary Distance
return Distance(f,i); // initialized to sum
}
void main()
{
Distance dist1, dist3, dist4; // define distances
dist1.getdist(); // get dist1 from user
Distance dist2(11, 6.25); // define, initialize dist2
dist3 = dist1 + dist2; // single '+' operator
dist4 = dist1 + dist2 + dist3; // multiple '+' operators
// display all lengths
cout << "\ndist1 = "; dist1.showdist();
cout << "\ndist2 = "; dist2.showdist();
cout << "\ndist3 = "; dist3.showdist();
cout << "\ndist4 = "; dist4.showdist();
getche();
}
وفي هذا البرنامج كان الإعلان عن دالة زيادة تحميل المؤثر , علي الصورة :
Distance operator + (distance):
ثم كان تعريف الدالة خارج الفئة وهي دالة تأخذ معاملا هو كائن من الفئة distance وتعيد كائنا بنفس الفئة .
وهنا أيضا نري أن الملاحظة التي أوردناها حول عدد معاملات الدوال المنتمية , من حيث كونها لا تحتاج للمتغير المتضمن في الكائن المنتمية إليه , سارية في حالة دوال المؤثرات (أنظر الفصل السابع "عدد المعاملات في الدوال المتضمنة ) ويبين الشكل هذا المنطق .
شكل المؤثر الثنائي – معامل واحد
جمع الإحداثيات القطبية
يبين الشكل طريقة تعيين النقاط بالإحداثيات الكارتيزية ( البعد السيني والبعد الصادي ) وشكل أسلوب الإحداثيات القطبية (المسافة وزاوية الانحراف ) . ويبين الشكل طريقة جمع هذه الإحداثيات , أما الشكل فيبين طريقة جمع الإحداثيات الكارتيزية . ويبين الشكل العلاقة بين نظامي الإحداثيين .
شكل الإحداثيات الكارتيزية
شكل الإحداثيات القطبية
شكل جمع الإحداثيات القطبية
شكل جمع الإحداثيات الكارتيزية
شكل العلاقة بين النظامين
ولكي نجمع الإحداثيات القطبية , فإننا سوف نفعل ذلك علي ثلاث خطوات : نحولها إلي كارتيزية , ثم نجمعها جمعا كارتيزيا , ثم نحولها مرة أخري إلي إحداثيات قطبية . ومعادلتي التحويل من الإحداثيات القطبية إلي الكارتيزية هي :
X = radius*cos(angle);
Y = radius*sqrt(x*x + y*y);
أما التحويل العكسي :
angle = atan(y/x);
radius = sqrt(x*x) + y*y);
وتوجد في لغة السي ++ دوال مكتبية لإيجاد مع كل من sin, cos, atan للزوايا .
وإليك البرنامج .
polaplus.cpp
// polaplus.cpp
// operator '+' with polar coordinates
// UCS Laboratories
#include <iostream.h>
#include <math.h> // for sin(), cos(), etc.
#include <conio.h>
class Polar
{
private:
double radius; // distance
double angle; // angle in radians
double getx() // these two functions
{ return radius*cos(angle); } // convert this Polar
double gety() // object into x and y
{ return radius*sin(angle); } // rectangular coords
public:
Polar() // constructor, no args
{ radius=0.0; angle=0.0; }
Polar(double r, double a) // constructor, two args
{ radius=r; angle=a; }
void display() // display
{ cout << "(" << radius
<< ", " << angle << ")"; }
Polar operator + (Polar p2) // add two Polars
{
double x = getx() + p2.getx(); // add x and y coords
double y = gety() + p2.gety(); // for this and p2
double r = sqrt(x*x + y*y); // convert new x and y
double a = atan(y/x); // to Polar
return Polar(r, a); // return temp Polar
}
};
void main()
{
Polar p1(10.0, 0.0); // line to the right
Polar p2(10.0, 1.570796325); // line straight up
Polar p3; // uninitialized Polar
p3 = p1 + p2; // add two Polars
cout << "\np1="; p1.display(); // display all Polars
cout << "\np2="; p2.display();
cout << "\np3="; p3.display();
getche();
}
تحتوي الفئة polar علي بيانين radius, angle , وقد عرفا double هو النوع الذي تتعامل معه غالبا المكتبية الرياضية , وتقاس الزوايا بالتقدير القطري radians . وتضم الفئة بادئتين , واحد للاستهلال الصفري والأخر للاستهلال بالقيم , ودالة للإظهار كما توجد دالتين لتحويل الإحدثيات القطبية إلي كارتيزية , هما getx() و gety() وقد جعلا خاصين , حيث لا يلزم استدعاؤها من خارج الفئة .
وتقوم دالة المؤثر operator+ بجمع إحداثيات قطبيين معا ويمكن بنفس المنطق زيادة تحميل بقية المؤثرات الحسابية لتعمل مع الإحداثيات القطبية .
وصل العبارات النصية
لا يمكن في لغة السي استخدام المؤثر "+" لوصل العبارات النصية concatenation string كما في لغات غيرها , ولكن باستخدام كائن العبارة النصية في برنامج strobj.cpp , وزيادة تحميل المؤثر المذكور , يمكننا تحقيق ذلك وإليك البرنامج :
strplus.cpp
// strplus.cpp
// overloaded '+' operator concatenates strings
// UCS Laboratories
#include <iostream.h>
#include <string.h> // for strcpy(), strcat()
#include <stdlib.h> // for exit()
#include <conio.h>
const int SZ = 80; // size of all String objects
class String // user-defined string type
{
private:
char str[SZ]; // holds a string
public:
String() // constructor, no args
{ strcpy(str, ""); }
String( char s[] ) // constructor, one arg
{ strcpy(str, s); }
void display() // display the String
{ cout << str; }
String operator + (String ss) // add a String to another
{
String temp; // make a temporary String
if( strlen(str) + strlen(ss.str) < SZ )
{
strcpy(temp.str, str); // copy this string to temp
strcat(temp.str, ss.str); // add the argument string
}
else
{ cout << "\nString overflow"; exit(1); }
return temp; // return temp String
}
};
void main()
{
String s1 = "\nMerry Christmas! "; // uses constructor 2
String s2 = "Happy new year!"; // uses constructor 2
String s3; // uses constructor 1
s1.display(); // display strings
s2.display();
s3.display();
s3 = s1 + s2; // add s2 to s1, assign to s3
s3.display(); // display s3
getche();
}
يظهر البرنامج أولا ثلاث عبارات نصية , (الثالثة فارغة) , ثم يوصل الأولي والثانية , و يوضعها في العبارة الثالثة , ثم يظهرها .
والمفروض أن يكون تحميل المؤثر مألوفا الأن , فالإعلان :
String operator + (string ss)
يبين أن المؤثر يأخذ معاملا من النوع string , ويعيد قيمة من نفس النوع . وتتضمن عملية الوصل إنشاء كائن مؤقت temp , ثم نسخ العبارة المتضمنة في كائننا فيه , ووصلها بالعبارة التي في المعامل باستخدام الدالة المكتبية strcat() , ثم إعادة القيمة بعد الوصل .
وليس بإمكاننا استخدام أسلوب الكائن المؤقت غير المسمي (راجع برنامج countpp3.cpp ):
Return string (string);
حيث إننا نريد الوصول للكائن المؤقت ليس فقط لاستهلاله , بل لوصله بالمعامل أيضا .
زيادة التحميل المتعدد
قدما ثلاثة تطبيقات لزيادة تحميل المؤثر "+" واحدة لجمع المسافات ذات الوحدات المختلفة , والثانية لجمع الأبعاد القطبية , والثالثة لوصل العبارات النصبة ويمكنك جمع كل هذه الإمكانيات في برنامج واحد وسوف يعرف محول الصياغة متي يطبق كل حالة .
مؤثرات المقارنة سوف نقوم الأن بزيادة تحميل نوع أخر من المؤثرات , مؤثرات المقارنة .
مقارنة المسافات
في المثال الأول سوف نقارن بين المسافات المحسوبة بالمقاييس الإنجليزية باستخدام المؤثر < , لنعرف إن كانت إحداهما أكبر من الأخري أم لا .
engless.cpp
// engless.cpp
// overloaded '<' operator compares two Distances
// UCS Laboratories
#include <iostream.h>
#include <conio.h>
enum boolean { false, true };
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 << '\"'; }
boolean operator < (Distance); // compare distances
};
// compare this distance with d2
boolean Distance::operator < (Distance d2) // return the sum
{
float bf1 = feet + inches/12;
float bf2 = d2.feet + d2.inches/12;
return (bf1 < bf2) ? true : false;
}
void main()
{
Distance dist1; // define Distance dist1
dist1.getdist(); // get dist1 from user
Distance dist2(6, 2.5); // define and initialize dist2
// display distances
cout << "\ndist1 = "; dist1.showdist();
cout << "\ndist2 = "; dist2.showdist();
if( dist1 < dist2 ) // overloaded '<' operator
cout << "\ndist1 is less than dist2";
else
cout << "\ndist1 is greater than dist2";
getche();
}
ويشابه أسلوب زيادة تحميل المؤثر < ما كان بالنسبة بالمؤثر +, عدا أن دالة المؤثر تعيد قيمة من النوع Boolean المعرفة في صدر البرنامج بواسطة المستخدم enum , تضم حالتين true, false لتمثل متغيرا منطقيا هذه القيمة المعادة هي نتيجة المقارنة , ويتم تحويل المسافتين إلي كسر عشري قبل إجراء المقارنة .
ونذكرك بأن العبارة :
Return (bf1 < bf2 ) ? true : false;
هي أوامر مختصر للأوامر :
If (bf1 < bf2)
Return true;
Else
Return false;
مقارنة عبارتين
إليك برنامجا أخر لمقارنة عبارتين , لنعرف تطابقهما من عدمه , وسوف نستخدم مؤثر التساوي "= =" .
strequal.cpp
// strequal.cpp
// overloaded '==' operator compares strings
// UCS Laboratories
#include <iostream.h>
#include <string.h> // for strcmp()
#include <conio.h>
const int SZ = 80; // size of all String objects
enum boolean { false, true };
class String // user-defined string type
{
private:
char str[SZ]; // holds a string
public:
String() // constructor, no args
{ strcpy(str, ""); }
String( char s[] ) // constructor, one arg
{ strcpy(str, s); }
void display() // display a String
{ cout << str; }
void getstr() // read a string
{ cin.get(str, SZ); }
boolean operator == (String ss) // check for equality
{
return ( strcmp(str, ss.str)==0 ) ? true : false;
}
};
void main()
{
String s1 = "yes";
String s2 = "no";
String s3;
cout << "\nEnter 'yes' or 'no': ";
s3.getstr(); // get String from user
if(s3==s1) // compare with "yes"
cout << "You typed yes\n";
else if(s3==s2) // compare with "no"
cout << "You typed no\n";
else
cout << "You didn't follow instructions\n";
getche();
}
يستخدم المؤثر "= =" في دالته الدالة المكتبية strcmp() ] اختصار لــــ string compare [ لإجراء المقارنة , وهي تعيد 0 إذا كانت العبارتان متساويتان , وإشارة سالبة إن كانت الأولي أقل من الثانية , وموجبة في حالة العكس , ولفظ " أقل " و " أكبر " يؤخذ بمعني الأسبقية في القاموس اللغوي .
ويمكن استخدام مؤثرات مثل < , > , أو مقارنة أطوال العبارات بحسب ما يريد المبرمج .
مؤثرات التخصيص الحسابية
ننهي حديثنا عن المؤثرات الثنائية بالمؤثر "=+", وهو يقوم بعمليتي الجمع والتخصيص معا . وسوف نستخدمه لجمع مسافتين , ثم تخصيص الناتج للمسافة الأولي . وهو مماثل للبرنامج englplus.cpp مع اختلاف دقيق .
englpleq.cpp
// englpleq.cpp
// overloaded '+=' assignment operator
// UCS Laboratories
#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 << '\"'; }
void operator += ( Distance );
};
// add distance to this one
void Distance::operator += (Distance d2)
{
feet += d2.feet; // add the feet
inches += d2.inches; // add the inches
if(inches >= 12.0) // if total exceeds 12.0,
{ // then decrease inches
inches -= 12.0; // by 12.0 and
feet++; // increase feet
} // by 1
}
void main()
{
Distance dist1; // define dist1
dist1.getdist(); // get dist1 from user
cout << "\ndist1 = "; dist1.showdist();
Distance dist2(11, 6.25); // define, initialize dist2
cout << "\ndist2 = "; dist2.showdist();
dist1 += dist2; // dist1 = dist1 + dist2
cout << "\nAfter addition,";
cout << "\ndist1 = "; dist1.showdist();
getche();
}
في هذا البرنامج تتم عملية الجمع بالأمر :
Dist1 +=dist2;
ولك أن تلاحظ الفرق بين دالتي المؤثر operator+= والمؤثر operator+ ففي الدالة الأخيرة تطلب الأمر إنشاء كائن ثالث dist3 لوضع ناتج الجمع فيه , معاد من الدالة إذا أردت استخدام هذا المؤثر في عمليات أكثر تعقيدا , علي النحو التالي مثلا :
Dist3 = dist1 += dist2;
فعليك أن تجعل الدالة من النوع الذي يعيد قيمة , وبأن تنهيها بالأمر :
Return distance(feet, inches);
حيث يخلق كائن مؤقت بلا اسم لغرض الإعادة .
مؤثر الأدلة
مؤثر الأدلة هو المؤثر الذي يحتوي علي أدلة المصفوفات [] , ويمكن أن يزاد تحميله لكي ننشئ مصفوفات ذات خواص معينة , وسوف نقدم مثالا لمصفوفة تحتوي علي رسالة إنذار بتجاوز حجمها , ولغرض المقارنة , سوف نتوصل لهذه النتيجة بثلاث طرق :
1- دالة للإدخال ودالة للإخراج :
arrover1.cpp
// arrover1.cpp
// creates safe array (index values are checked before access)
// uses separate put and get functions
// UCS Laboratories
#include <iostream.h>
#include <process.h> // for exit()
#include <conio.h>
const int LIMIT = 100; // array size
class safearay
{
private:
int arr[LIMIT];
public:
void putel(int n, int elvalue)
{
if( n< 0 || n>=LIMIT )
{ cout << "\nIndex out of bounds"; exit(1); }
arr[n] = elvalue;
}
int getel(int n)
{
if( n< 0 || n>=LIMIT )
{ cout << "\nIndex out of bounds"; exit(1); }
return arr[n];
}
};
void main()
{
safearay sa1;
for(int j=0; j<LIMIT; j++) // insert elements
sa1.putel(j, j*10);
for(j=0; j<LIMIT; j++) // display elements
{
int temp = sa1.getel(j);
cout << "\nElement " << j << " is " << temp;
}
getche();
}
في هذا البرنامج استخدمنا دالة للإدخال pute() ذات معاملين , هما دليل المصفوفة n وعناصرها elvalue وكلاهما من النوع int , والدالة لا تعيد قيما . كما استخدمنا دالة لإظهار العناصر علي الشاشة , هي الدالة gete() وهي تأخذ معاملا واحدا هو دليل المصفوفة , ثم تعيد قيمة العنصر المقابل لذلك الدليل إلي الدالة الأصلية ليخصص للمتغير temp الذي ستظهر قيمته علي الشاشة .
وتتولي دوارة إدخال القيم للمصفوفة ودوارة لإظهارها علي الشاشة .
2- دالة واحدة بالإعادة بالإشارة
arrover2.cpp
// arrover2.cpp
// creates safe array (index values are checked before access)
// uses one access() function for both put and get
// UCS Laboratories
#include <iostream.h>
#include <process.h> // for exit()
#include <conio.h>
const int LIMIT = 100; // array size
class safearay
{
private:
int arr[LIMIT];
public:
int& access(int n) // note: return by reference
{
if( n< 0 || n>=LIMIT )
{ cout << "\nIndex out of bounds"; exit(1); }
return arr[n];
}
};
void main()
{
safearay sa1;
for(int j=0; j<LIMIT; j++) // insert elements
sa1.access(j) = j*10; // *left* side of equal sign
for(j=0; j<LIMIT; j++) // display elements
{
int temp = sa1.access(j); // *right* side of equal sign
cout << "\nElement " << j << " is " << temp;
}
getche();
}
في هذه الدالة استخدمنا دالة واحدة هي access() مكان الدالتين , وهو تماثل الدالة gete() عدا أن الإعادة فيها غير مباشرة (لاحظ المؤثر & قبل اسم الدالة ) وبمراجعه ما قلناه عن الإعادة بالإشارة في الفصل السادس , فإن هذا الأسلوب يتيح لنا أن نخصص قيمة للدالة , وهو ما فعلناه بالأمر :
Sa1.access(j) = j*10;
فالدالة إلي يسار علامة التخصيص , لكي يخصص لها قيمة العنصر الموجود علي اليمين , وهو مل لا يمكن عمله في حالة الإعادة المعتادة أي بالإعادة بالقيم .
ويتقبل الدالة هذه القيم تملأ عناصر المصفوفة , ثم تظهر بالطريقة التي تمت في البرنامج الأول .
3- مؤثر الأدلة بزيادة التحميل
arrover3.cpp
// arrover3.cpp
// creates safe array (index values are checked before access)
// uses overloaded [] operator for both put and get
#include <iostream.h>
#include <process.h> // for exit()
#include <conio.h>
const int LIMIT = 100; // array size
class safearay
{
private:
int arr[LIMIT];
public:
int& operator [](int n) // note: return by reference
{
if( n< 0 || n>=LIMIT )
{ cout << "\nIndex out of bounds"; exit(1); }
return arr[n];
}
};
void main()
{
safearay sa1;
for(int j=0; j<LIMIT; j++) // insert elements
sa1[j] = j*10; // *left* side of equal sign
for(j=0; j<LIMIT; j++) // display elements
{
int temp = sa1[j]; // *right* side of equal sign
cout << "\nElement " << j << " is " << temp;
}
getche();
}
حل المؤثر هنا محل الدالة access() في البرنامج السابق , مستفيدا أيضا بإمكانية الإعادة غير المباشرة (بالإشارة) . والمكسب هنا أن صيغة المصفوفة أخذت شكلها المعتاد sa1[j] في أوامر البرنامج .
تعليقات
إرسال تعليق