زيادة تحميل المؤثرات الثنائية في ++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 d2Distance 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 Distancereturn 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 Polardouble 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 Polarscout << "\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 stringss2.display();
s3.display();
s3 = s1 + s2; // add s2 to s1, assign to s3 s3.display(); // display s3getche();
}
يظهر البرنامج أولا ثلاث عبارات نصية , (الثالثة فارغة) , ثم يوصل الأولي والثانية , و يوضعها في العبارة الثالثة , ثم يظهرها .
والمفروض أن يكون تحميل المؤثر مألوفا الأن , فالإعلان :
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 d2boolean 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 userif(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 onevoid Distance::operator += (Distance d2)
{ feet += d2.feet; // add the feet inches += d2.inches; // add the inchesif(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 signfor(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 signfor(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] في أوامر البرنامج .
تعليقات
إرسال تعليق