تغيير نوع البيانات في ++C

c

تغيير نوع البيانات

تحدثنا سابقا عن تحويل أنواع البيانات من نوع إلي أخر من الأنواع الأساسية , مثل int إلي float أو العكس , وبينا أن ذلك قد يكون أليا لا يتدخل فيه المبرمج , أو قسريا باستخدام مؤثر التحديد القسري cast operator والذي صيغته أن يوضع المتغير المراد تغيير نوعه (أو أسم النوع ) بين قوسين .

وسوف نقوم في البقية الباقية من المقالات بشرح طريقة زيادة تحميل مؤثر التحويل القسري لكي يستخدم في التحويل بين نوع محدد بمعرفة المبرمج (كائن) ونوع أساسي , أو بين كائنات من أنواع مختلفة .

التحويل بين كائن ونوع أساسي

في المثال التالي سوف نقدم لك برنامجا يحول بين المسافات بالوحدات الإنجليزية ( التي يعبر عنها بكائن من الفئة distance ) والمترية ( والتي تكون أعدادا كسرية , أي من النوع float )

englconv.cpp


 


// englconv.cpp


// conversions: Distance to meters, meters to Distance


// UCS Laboratories


#include <iostream.h>


#include <conio.h>


 


const float MTF = 3.280833;       // meters to feet


 


class Distance                    // English Distance class


   {


   private:


      int feet;


      float inches;


   public:


      Distance()                  // constructor (no args)


     { feet = 0; inches = 0.0; }


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


     {                        // convert meters to Distance


     float fltfeet = MTF * meters;  // convert to float feet


     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 getdist()              // get length from user


     {


     cout << "\nEnter feet: ";  cin >> feet;


     cout << "Enter inches: ";  cin >> inches;


     }


      void showdist()             // display distance


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


 


      operator float()            // conversion function


     {                        // converts Distance to meters


     float fracfeet = inches/12;  // convert the inches


     fracfeet += float(feet);     // add the feet


     return fracfeet/MTF;         // convert to meters


     }


   };


 


void main()


   {


   Distance dist1 = 2.35;          // uses 1-arg constructor to


                   // convert meters to Distance


   cout << "\ndist1 = "; dist1.showdist();


 


   dist1 = 1.00;                   // this form also uses


                   // 1-arg constructor


   cout << "\ndist1 = ";  dist1.showdist();


 


 


   Distance dist2(5, 10.25);       // uses 2-arg constructor


 


   float mtrs = float(dist2);      // uses conversion function to


                   // convert Distance to meters


   cout << "\ndist2 = " << mtrs << " meters";


 


   mtrs = dist1;                   // this form also uses


                   // conversion function


   cout << "\ndist1 = " << mtrs << " meters";


getche();


   }





قد طبقت عملية التحويل من الوحدات المترية إلي الإنجليزية علي الكائن d1 مرتين ثم طبقت عملية التحويل العكسية علي الكائن d2 ثم علي المسافة d1 لتعود لقيمتها المترية .



وفي عمليات التحويل كانت العبارات المستخدمة بسيطة واضحة المعني , وسوف نعرض الأن لأسلوب كلا التحويلين :



من الأساسي للكائن



يتضمن البرنامج بادئة ذات معامل واحد من النوع float تقوم دالتها بتحويل العدد المبين في المعامل إلي الصورة المبينة , وهي تستدعي عندما أنشأنا كائنا بمعامل واحد , وذلك بالأمر :



Distance dist1 = 2.35;



فكان الكائن الذي أنشأته البادئة مصاغا في الصيغة المطلوبة



ولكنا نفذنا التحويل علي نفس المسافة بأسلوب أخر , وهي الأمر :



Dist1 = 1.0;



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



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



من الكائن إلي الأساسي



يقوم بهذه المهمة مؤثر التحيد القسري float() بعد زيادة تحميله عن طريق الأوامر التالية :



Float fracfeet = inches/12;



] يقوم هذا الأمر بإنشاء متغير كسري هو fracfeet توضع فيه قيمة البوصات بعد تحويلها لأقدام بالقسمة علي 12 [



Fracfeet += float (feet);



] يجمع قيمة المتغير المذكور علي الأقدام ولما كانت المتغير feet معرف كعدد صحيح int بينما المتغير fracfeet معرف كعدد كسري , فقد وجب تحويل الأول إلي float , باستخدام المؤثر float() (استخدام عادي هذه المرة ) وتوضع نتيجة الجمع في المتغير fracfeet [ .



Return fracfeet/MTF;



لتحويل القيمة إلي أمتار



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



Mtrs = dist1;



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



التحويل بين عبارة نصية وكائن



البرنامج التالي يحول من عبارة نصية إلي كائن وبالعكس .






strconv.cpp


 


// strconv.cpp


// convert between ordinary strings and class String


// UCS Laboratories


#include <iostream.h>


#include <string.h>      // for strcpy(), etc.


#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 0, no args


     { str[0] = '\0'; }


 


      String( char s[] )          // constructor 1, one arg


     { strcpy(str, s); }      // convert string to String


 


      void display()              // display the String


     { cout << str; }


 


      operator char*()            // conversion function


     { return str; }          // convert String to string


   };


 


void main()


   {


   String s1;                     // use constructor 0


   char xstr[] =                  // create and initialize string


        "\nJoyeux Noel! ";


   s1 = xstr;                     // use constructor 1


                  // to convert string to String


   s1.display();                  // display String


 


   String s2 = "Bonne Annee!";    // uses constructor 1


                  // to initialize String


 


   cout << (char*)s2;             // use conversion function


                  // to convert String to string


                  // before sending to << op


   getche();


   }




فالبادئة ذات المعامل الوحيد هي التي تتولي تحويل مصفوفة محارف إلي كائن من الفئة string فدالتها تحتوي علي الدالة المكتبية التي تقوم بمهمة نسخ المصفوفات strcpy() ويمكن استثارة هذه الدالة بالأمر :



String s2= ''bonne anee!'';



أو عن طريق التخصيص بالأمر :



S1 = xstr;



وقد وضعت دالة تحويل بين الكائنات من فئة string إلي مصفوفات محارف عادية بزيادة تحميل المؤثر بالأمر :



Operator char*()



{ return str; }



واستخدام النجمة هذا تعني مؤشر وسوف نتناول موضوع المؤشرات في الفصل الثاني عشر , ويكفي أن نقول أن التعبير char* هو صورة أخري من التعبير عن المصفوفات ذات المحارف وقد طبق التحويل في العبارة :



Cout << (char*)s2;



وقد استخدمت في هذه العبارة المصفوفة s2 , فحيث إن المؤثر << لا يعرف أي شئ عن الفئة string فإن محول الصياغة يبحث عن وسيلة تتيح له تنفيذ تحويل كائن هذه الفئة string فإن محول الصياغة يبحث عن وسيلة تتيح له تنفيذ تحويل كائن هذه الفئة إلي مصفوفة محارف يمكن المؤثر << أن يتعامل معها , فيجدها في المؤثر القسري زائد التحميل operator char*() , فيستخدمه في إنشاء مصفوفة عادية ترسل إلي المؤثر << ليظهرها ( يمكن طبعا إظهار المصفوفة باستخدام الدالة المنتمية display() ولكن الصياغة هنا أوضح وأجمل ) .



التحويل بين كائنات مختلفة النوع



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



وسوف نقدم لك الطريقتين في مثالين لتحويل الإحداثيات القطبية إلي كارتيزية .



طريقة دالة التحويل






// polarec1.cpp


// converts from Polar to Rec using routines in Polar (source)


// UCS Laboratories


#include <iostream.h>


#include <math.h>            // for sin(), cos(), etc.


#include <conio.h>


 


class Rec


   {


   private:


      double xco;                    // x coordinate


      double yco;                    // y coordinate


   public:


      Rec()                          // constructor 0, no args


     { xco = 0.0; yco = 0.0; }


      Rec(double x, double y)        // constructor 2, two args


     { xco = x; yco = y; }


      void display()                 // display


     { cout << "("  << xco


        << ", " << yco << ")"; }


   };


 


class Polar


   {


   private:


      double radius;


      double angle;          


   public:


      Polar()                        // constructor 0, no args


     { radius=0.0; angle=0.0; }


      Polar(double r, double a)      // constructor 2, two args


     { radius=r; angle=a; }


      void display()                 // display


     { cout << "("  << radius


        << ", " << angle << ")"; }


      operator Rec()                 // conversion function


     {                           // Polar to Rect


     double x = radius * cos(angle);  // find x and y to


     double y = radius * sin(angle);  // initialize nameless


     return Rec(x, y);                // Rec for return


     }


   };


 


void main()                                 


   {                                    


   Rec rec;                          // Rec using constructor 0


   Polar pol(10.0, 0.785398);        // Polar using constructor 2


 


   rec = pol;                        // convert Polar to Rec


                     // using conversion function


                     // or use rec = Rec(pol);


 


   cout << "\npol="; pol.display();  // display original Polar


   cout << "\nrec="; rec.display();  // display equivalent Rect


   getche();


   }




في هذا البرنامج وضعت دالة التحويل في الدالة operator rec()



طريقة البادئة وحيدة المعامل






polarec2.cpp


 


// polarec2.cpp


// converts Polar to Rec using routine in Rec (destination)


// UCS Laboratories


#include <iostream.h>


#include <math.h>                    // for sin(), cos(), etc.


#include <conio.h>


 


class Polar                          // point, polar coordinates


   {


   private:


      double radius;                 // radius coord


      double angle;                  // angle coord (radians)


   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 << ")"; }


      double getr()                  // these routines allow


     { return radius; }          // radius and angle to be


      double geta()                  // accessed from outside


     { return angle; }           // the class


   };


 


class Rec                            // point, rectangular coords


   {


   private:


      double xco;                    // x coordinate


      double yco;                    // y coordinte


   public:


      Rec()                          // constructor 0, no args


     { xco = 0.0; yco = 0.0; }


      Rec(double x, double y)        // constructor 2, two args


     { xco = x; yco = y; }


      Rec(Polar p)                   // constructor 1, one arg


     {                           //    Polar to Rec


     double r = p.getr();


     double a = p.geta();        // get r and a from Polar


     xco = r * cos(a);           // using getr() and geta()


     yco = r * sin(a);           // change to our xco and yco


     }


      void display()                 // display


     { cout << "("  << xco


        << ", " << yco << ")"; }


   };


 


void main()                                 


   {                                    


   Rec rec;                          // Rec using constructor 1


   Polar pol(10.0, 0.785398);        // Polar using constructor 2


 


   rec = pol;                        // convert Polar to Rec


 


   cout << "\npol="; pol.display();  // display original Polar


   cout << "\nrec="; rec.display();  // display converted Rec


   getche();


   }




روتين التحويل هنا يتمثل في الدالة rec(polar p) , وهي تتقبل كائنا من الفئة polar , لتحوله للفئة rec . وحتي يمكن لهذه الدالة التعامل مع عناصر كائنات الفئة غير المنتمية لها فقد تضمن تعريف الفئة polar دالتين يتيحان إمرار القيم التي تطلبها دالة التحويل لها .



الخيار بين الأسلوبين



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



مخاطر التحويل وزيادة التحميل



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



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



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



2- استخدم صيغا متماثلة : استخدم المؤثرات الزائدة التحميل بنفس الطريقة التي تستخدم مع المتغيرات الأساسية , فمثلا , إذا كان a, b متغيرات أساسية , فإن :



A += b;



تعني أن يأخذ المتغير a قيمة جمعه علي b , فأي زيادة تحمل للمؤثر + يجب أن تكون إلي شئ مماثل , بأن ينفذ شيئا مثل :



A = a +b;



وعليك مراعاة أن بعض الصيغ ليست قابلة للتعديل , فليس ممكنا أن تستخدم مؤثرا ثنائيا ليقوم بتأثير أحادي أو العكس .



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



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



5- ليست كافة المؤثرات قابلة لزيادة التحميل : المؤثرات التالية ليست قابلة لزيادة التحميل : مؤثر النطاق scope resolttion ::, المؤثر الشرطي :?, مؤثر الإشارة :* الذي لم نصادفه بعد .

تعليقات

المشاركات الشائعة من هذه المدونة

المؤثرات الحسابية في C++

دوال النمط الرسومي في ++C

المؤثرات المنطقية في C++