تماس درباره   صفحه اصلی
  زبان ++C > استثناها و خطاها  
 
 

استثناها و خطاها


استثناها خطاهای درحال اجرا هستند. به طور معمول خطاها توسط دستور if تشخيص و در صورت برقرار بودن شرط عمل مناسب، که اغلب نمايش پيغام و اتمام برنامه بود، انجام می گرفت. راه ديگر هنگام مواجه شدن با خطا گير انداختن آن هنگام پيش آمدن شرايط خطاست. کدی که ممکن است باعث خطا شود اجرا می شود و هر خطائی که ايجاد می شود گرفته می شود و عمل مناسب انجام می گيرد. بلاک های try-catch بر اساس اين مفهوم عمل می کنند. مديريت خطاها به طور سطحی در اينجا معرفی می شود.

خطاها
مديريت استثناها
بلاک های try-catch


مديريت خطاها از موضوعات مهم در برنامه نویسی است. حالات متعددی وجود دارند که ممکن است ایجاد خطا کند. مثلا باز کردن فایلی که وجود ندارد یا دريافت ورودی نادرست از کاربر. کنترل این خطاها هنگام اجرای برنامه اهمیت زیادی دارد. اگر برنامه این گونه خطاها را در دست نگیرد برنامه سقط می کند و نارضايتی کاربر را باعث می شود. روش قديمی بررسی خطا توسط if و دادن یک پیغام دوستانه به کاربر و اتمام برنامه بود. C++ راه ديگری را توسط بلاک های try-catch برای مديريت خطاهای در حال اجرا در اختيار مي گذارد.

خطاها

خطاها را به سه دسته می توان تقسیم کرد:

• خطاهای گرامری (syntax error). خطاهائی که با رعايت نکردن قواعد زبان برنامه نويسی توسط کامپايلر تشخيص داده می شوند. مثلا بجای if(x!=y) بنویسید if(x<>y). کامپایلر کد برنامه را تا وقتی خطای گرامری وجود دارد کامپایل نمی کند.
• خطاهای زمان اجرا (runtime error). هر وقفه ای که در جریان عادی اجرای برنامه پیش آید که معمولا باعث سقط برنامه می شود. مثلا باز کردن فایلی که وجود ندارد یا تقسیم بر صفر. این خطاها استثنا (exception) نامیده می شوند.
• خطاهای منطقی (login error). وقتی برنامه کامپایل و اجرا می شود اما به دليل خطائی در منطق برنامه نتایج غلطی تولید می کند. سخت ترین نوع خطا است که معمولا به آن bug هم گفته می شود.

برای رفع خطا ابتدائی ترین کاری که می شود کرد تست کلیه برنامه و کشف و برطرف سازی خطا است. کنترل مناسب خطاها از سقط برنامه جلوگيری می کند. کامپایلر خطاهای گرامری را مشخص می کند. مديريت استثناها خطاهای زمان اجرا را بر عهده دارند بنابراين خطاهای منطقی بزرگترین مشکل برنامه نویس خواهد بود.


مديريت استثناها

قبل از اينکه سيستم مديريت استثناها معرفی شود برنامه های کامپيوتری کاملا بدون خطا نبودند. البته برای گرفتن خطاها روش هائی وجود داشت که معمول ترين آن بررسی مقداری بود که توسط توابع برگردانده می شد و نحوه اجرای آن را بيان می کرد. کدی که تابع را فراخوانی می کرد مقدار بر گشتی را چک می کرد و مطابق با آن عمل می کرد (اگر با استفاده از Windows API برنامه نوشته باشيد با اين روش آشنا هستيد).

مفهوم کلی مديريت استثناها ساده است. هر زمان که استثنائی شناسائی می شود يک فلگ فرضی خطا بالا می رود. سيستمی همواره مراقب اين فلگ خطاست. و درصورت بالا رفتن فلگ کد مديريت خطا فراخوانی می شود. بالا رفتن فلگ خطا را اصطلاحا گير انداختن (throwing) خطا می نامند. وقتی خطائی به دام انداخته می شود سيستم با گرفتن خطا (catching) عکس العمل نشان می دهد. محاصره کردن بلاکی از کد حساس به خطا و مديريت استثنا را سعی کردن (trying) به اجرای بلاک می نامند.

دلايل خوبی برای استفاده از مديريت استثناها وجود دارد. مهمترين آن جداکردن کد مديريت خطا از کد برنامه است. وقتی استثنائی گرفته می شود مسيری موازی اجرا طی می شود و با اجرای نرمال کد برنامه تداخلی ندارد. اين باعث می شود نوشتن کد ساده تر شود چون مجبور نيستيد برای هر خطائی چک داشته باشيد. علاوه بر اين يک استثنا نمی تواند نديده گرفته شود و تضمين می کند که با خطا برخورد کند. و چون جلوی سقط ناخواسته برنامه را می گيرد اتکاپذيری برنامه را بالا می برد.

از مزايای ديگر مديريت استثناها اين است که خطا می تواند خارج از محدوده تابع گيرانداخته شود. يعنی اگر درونی ترين تابع خطائی داشته باشد اين خطا می تواند تا تابع بالائی که دارای بلاک trying است منتشر شود که به برنامه نويس اجازه می دهد کد مديريت خطا را در هر جا مثل تابع اصلی قرار بدهد.


بلاک های try-catch

مديريت استثنا بر اساس مفاهيمی که گفته شد توسط سه دستور try، catch و throw‌ عمل می کند. بلاک کدی می خواهيم استثناهای آن را بگيريم با دستور try مشخص می شود. درون بلاک می توان هر خطائی با دستور throw گير افتاده و از بين می رود. بلاک catch که محلی برای کد مديريت خطا است بلافاصله بعد از بلاک try قرار می گيرد.

فرم کلی به صورت زير است:

try {
   ...
   ...
   throw Exception;
   ...
   ...
   }
catch( Exception e ) {
   ...
   ...
   }


مثال. هر استثنائی که موقع دسترسی به فايل test.txt رخ دهد گرفته و پيغام مناسب نمايش داده می شود.

#include <fstream.h>
#include <iostream.h>
int main () {
   try {
      char buffer[256];
      ifstream myfile ("test.txt");
      while (! myfile.eof() ) {
         myfile.getline (buffer,100);
         cout << buffer << endl;
      }
   }
   catch(…) {
      cout << “There was an error !\n”;
   }
   return 0;
}


سه نقطه درون پرانتز catch به معنی اینستکه کلیه خطاها توسط این بلاک مديريت می شود. می توان انواع مختلفی از خطاها را هندل کرد.

وقتی استثنائی رخ می دهد کنترل به بلاک catch منتقل می شود بنابراين دستورات بعد از throw اجرا نخواهد شد. در بلاک catch کد بايد به نحوی باشد که اجرای برنامه را به صورت معمول ادامه يابد يا در صورت برطرف نشدن خطا با exit يا abort خاتمه پيدا کند. اگر بلاک catch اجرای برنامه را به پايان نرساند کنترل اجرا به دستور بعد از بلاک try-catch منتقل می شود.

نکته. بین دو بلاک هیچ چیز نباید باشد.
نکته. در صورت گير انداختن يک استثنا دستورات بعد از throw اجرا نخواهد شد.
نکته. يک استثنا را می توان به تابعی که تابع جاری را صدا زده است انداخت تا مديريت شود .


مثال. خطا در تابع گرفته می شود اما به برنامه اصلی برای مديريت ارسال می شود.

#include <iostream.h>
float divide_number(float , float);
int main() {
   float dividend,divisor,answer;
   try {
      cout << "Please enter a number \n";
      cin >> dividend;
      cout << "Please enter a number \n";
      cin >> divisor;
      answer = divide_number(dividend,divisor);
      cout << dividend << " divided by ";
      cout << divisor << " is " << answer;
      }
   catch(...) {
      cout << "oops, there is an error!";
      }
   return 1;
}
float divide_number(float num1, float num2) {
   try {
      float answer;
      answer = num1/num2;
      return answer;
      }
   catch(...) {
      throw;
   }
}


هر دستور try می تواند با چندين دستور catch در ارتباط باشد. هر کدام برای نوع مختلفی از استثنا. نوع داده ای که در دستور catch مشخص شده است با استثنای تطبيق داده می شود سپس بخش مربوطه به اجرا در می آيد و از بقيه صرفنظر می شود. هر نوع داده ای از جمله کلاس های تعريف شده در برنامه می تواند بدام انداخته شود.


مثال. اعداد صحیح توسط catch گرفته می شود. اگر تقسیم بر صفر باشد یک عدد صحیح به بلاک catch داده می شود. بلاک catch استثنا از نوع صحيح را می گيرد.

#include <iostream.h>
int main(){
   int answer, divisor, dividend;
   try {
      cout << "Please enter an integer \n";
      cin >> divisor;
      cout << "Please enter another integer \n";
      cin >> dividend;
      if(dividend ==0) throw 0;
      answer = divisor/dividend;
      cout << answer;
      return 0;
      }
   catch (int i) {
      cout << "You cannot divide by zero";
      }
}

مثال. کاربرد مديريت استثناها در استفاده از حافظه پويا.

try {
   NodePtr = new ListNodeClass(Item, NextPtr);
   if (NodePtr == NULL) {
      throw "Cannot allocate memory";
   }
   NodePtr = GetNode(Item, Front->Next);
   }
catch(char * Str) {
   cout << "Error: Could not insert at front " << Str << endl;
   throw;
   }

مثال. مديريت استثنا با چند بلاک catch.


try{
   cin >> test;
   if (test == 0 ) throw test;
   if (test == 1 ) throw 'a';
   if (test == 0 ) throw 123.5;
   }
catch(float FloatNum){
   cout << "Caught an exception with float value: " << FloatNum << endl;
   }
catch(int IntNum) {
   cout << "Caught an exception with int value: " << IntNum << endl;
   }
catch(...){ // catch anything else
   cout << "Caught an exception with type not int or float" << endl;
   }


وقتی چندین خطا را پاسخ می دهید سلسله مراتب خطاها اهمیت دارد. خطاها با ترتیبی که در سلسله مراتب نشان داده شده است باید در تله انداخته شوند یعنی اگر شما run-time-error را گیر انداختید نمی توانید عبارت catch دیگری برای file access error داشته باشید چون خطاهائی که در سلسله مراتب بالاتر هستند کلیه خطاهای پایینی را در بر دارند.

Exception – the parent class for all exception class
   Login-error – the parent class of a variety of logic error
      invalid-argumet
      out-of-range
   Runtime-error – the parent of class for a variety of rumtime error
      overflow-error
      FileAccessError
      range-error


 


 


صفحه اصلی| PDF| درباره| تماس