الفئة iostream وفئات _withassign في ++C

الفئة iostream وفئات _withassign

الفئة iostream المشتقة من كل من ostream و istream معا تعمل فقط كفئة أساسية لعدد من الفئات المشتقة منها , أهمها iostram_withassign وليس لها دوال خاصة بها (عدا بادئتها ومنهياتها) .

ولدينا ثلاثة فئات في هذه المجموعة _withassign

Iostream_withassign مشتقة من الفئة istream

ostream_withassign مشتقة من الفئة ostream

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

لماذا نحتاج إلي فئات قابلة للنسخ وأخري غير قابلة لذلك ؟ بشكل عام , ليس مستحبا نسخ كائنات التدفقات . والسبب هو أن كل كائن منها مصحوب بكائن streambuf معين يحتوي علي حجم من الذاكرة تكفي لبيانات الكائن . فإذا ما نسخت كائنا , تسببت في بلبلة إذا نسخ معه الكائن streeambuf علي أنه في بعض الحالات القليلة يستلزم الأمر نسخ كائن لتدفق , كما في حالة إعادة التوجيه للكائنين << و >> سوف نتحدث عن ذلك لاحقا ) .

ولهذا السبب وضعت الفئات istream, ostream, iostream لا تقبل النسخ ( يجعل بادئات النسخ ومؤثرات النسخ الخاصة بها والمزادة التحميل خاصة ) , ومعها الفئات _withassign المشتقة منها تقبل ذلك .

كائنات التدفق سابقة التعريف

استخدمنا إلي الأن بكثرة أشهر كائنين من مجموعة الفئات _withassign , وهما cin, المشتق من الفئة istream_withassign و cout المشتق من الفئة ostream_withassign ويعملان عادة مع لوحة المفاتيح والشاشة . أما الكائنان الأخران فهما : cerr و clog المشتقان أيضا من الفئة ostream_withassign .

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

تدفق الأخطاء

إلي الأن , استخدمنا فقط أسلوبا مباشرا إلي حد ما للإدخال والإخراج , مستخدمين تعبيرات من قبيل :

Cout << ''Good morning'':

Cin >> var;

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

بتات حالات الأخطاء

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

Object-Oriented Programming in C   _Page_0604_Image_0001

شكل بتات أخطاء التدفق

جدول بتات الأخطاء

اسم البتة

معناها

Goodbit

لا أخطاء ( كافة البتات في الوضع 0 )

eofile

الوصول لانتهاء الملف

Failbit

خطأ من المستخدم , إنهاء قبل الأوان

badbit

عملية غير معتمدة (لا تدفق لها)

Hardfail

خطأ لا يمكن إصلاحه

جدول دوال بتات الأخطاء

الدالة

الغرض منها

Int = eof();

إعادة true في حالة نهاية الملف

Int = fail();

إعادة true في حالة قيمة 1 لأي من بتات الأخطاء الثلاثة

Int = bad();

إعادة true في حالة قيمة 1 للبتة badbit أو failbit

Int = good();

إعادة true في حالة كافة البتات = 0

Clear(int=0);

في حالة عدم وجود معامل البتات توضع في القيمة 0 , في غير ذلك , توضع البتة المقصودة في الوضع 1 , مثال : clear(ios::failbit)

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

#include<iostream.h>

Main()

{

Int I;

While(1) // cycle until input ok.

{

Cout << ''\nEnter an integer'';

Cin >> I;

If (cin.good() ) // if no errors

{

Cin.ignore(10, '\n'); //remove new line

Break; // exit loop

}

Cin.clear();

Cout << ''Incorrect input'';

Cin.ignore(10, '\n'); // remove new line

}

Cout << ''Integer is: '' << I;

}

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

إدخال عدد كبير من المحارف

إذا كان عدد المحارف المدخلة لتدفق الإدخال كثيرا , فقد يسبب ذلك مشكلة خاصة إذا كان إدخالها بطريق الخطأ كما في مثالنا , فمن الأمور المعتادة أن تظل المحارف الزائدة باقية في التدفق بعد أن يكون الإدخال قد انتهي فرضا وقد تدخل المحارف المتخلفة للعملية التالية التي لا شأن لها بها . فحتي يمكن التخلص من مثل هذه المحارف , تستخدم الدالة ignore() لكي تقرأ وتلقي بعيدا إلي عدد 10 محارف بما فيها المحدد المذكور في دالتها فمثلا يؤدي الأمر التالي إلي قراءة عدد أقصي هو 10 محارف بما فيها السطر الجديد , بواسطة cin ويلغيها من الإدخال :

Cin.ignore(10, '\n');

تجاهل المسافات البيضاء

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

Cin.unset(ios::skipws);

كما سنعرض لك في المثال التالي .

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

برنامج المسافات الإنجليزية مع تتبع الأخطاء

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

 

englerr.cpp


 


// englerr.cpp


// input checking with English Distance class


// UCS Laboratories


 


#include <iostream.h>


#include <string.h>               // for strchr()


#include <stdlib.h>               // for atoi()


#include <conio.h>


 


int isint(char*);                 // prototype


const int IGN = 10;               // characters to ignore


 


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 << '\"'; }


      void getdist();             // get length from user


   };


 


void Distance::getdist()          // get length from user


   {


   char instr[80];                // for input string


 


   while(1)                       // cycle until feet are right


      {


      cout << "\n\nEnter feet: ";


      cin.unsetf(ios::skipws);    // do not skip white space


      cin >> instr;               // get feet as a string


      if( isint(instr) )          // is it an integer?


         {                        // yes


         cin.ignore(IGN, '\n');   // eat chars, including newline


         feet = atoi(instr);      // convert to integer


         break;                   // break out of 'while'


         }                        // no, not an integer


      cin.ignore(IGN, '\n');      // eat chars, including newline


      cout << "Feet must be an integer\n";  // start again


      }  // end while feet


 


   while(1)                       // cycle until inches are right


      {


      cout << "Enter inches: ";


      cin.unsetf(ios::skipws);    // do not skip white space


      cin >> inches;              // get inches (type float)


      if(inches>=12.0 || inches<0.0)


         {


         cout << "Inches must be between 0.0 and 11.99\n";


         cin.clear(ios::failbit); // "artificially" set fail bit


         }


      if( cin.good() )            // check for cin failure


         {                        // (most commonly a non-digit)


         cin.ignore(IGN, '\n');   // eat the newline


         break;                   // input is OK, exit 'while'


         }


      cin.clear();                // error; clear the error state


      cin.ignore(IGN, '\n');      // eat chars, including newline


      cout << "Incorrect inches input\n";  // start again


      }  // end while inches


   }


 


int isint(char* str)              // return true if the string


   {                              //    represents type int


   int slen = strlen(str);        // get length


   if( slen==0 || slen > 5)       // if no input, or too long


      return 0;                   // not an int


   for(int j=0; j<slen; j++)      // check each character


                                  // if not digit or minus


      if( (str[j] < '0' || str[j] > '9') && str[j] != '-' )


         return 0;                // string is not an int


   long n = atol(str);            // convert to long int


   if( n<-32768L || n>32767L )    // is it out of int range?


      return 0;                   // if so, not an int


   return 1;                      // it is an int


   }


 


void main()


// UCS Laboratories


   {


   Distance d;                    // make a Distance object


   char ans;


   do


      {


      d.getdist();                // get its value from user


      cout << "\nDistance = ";


      d.showdist();               // display it


      cout << "\nDo another (y/n)? ";


      cin >> ans;


      cin.ignore(IGN, '\n');      // eat chars, including newline


      } while(ans != 'n');        // cycle until 'n'


   gethce();


   }





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



Cin.clear(ios::failbit);



فحين يختبر البرنامج الشرط cin.good() فإنه يجد أن أحد البتات ليست 0 وبالتالي يصدر إشارة الخطأ .

تعليقات

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

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

الرسم Graphics

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