مدیریت استثناها در ++C یا Exception Handling

در این جلسه به بررسی مدیریت استثناها در ++C یا Exception Handling می‌پردازیم. یک استثنا حالتی غیرطبیعی است که ممکن است در حین اجرای یک برنامه رخ دهد. یک استثنا در ++C پاسخی است که برای یک وضعیت ویژه پیش آمده حین اجرای برنامه ، مانند تقسیم بر صفر، تدارک دیده شده است.

استثناها راهی برای انتقال کنترل برنامه از یک بخش به بخش دیگر برنامه فراهم می‌کنند. استثنا در ++C با سه کلید واژه ساخته می‌شود: try ،catch و throw.

  • Thorw: یک برنامه هنگام رخ دادن مشکل، یک استثنا پرتاب (throw) می‌کند. این کار با استفاده از کلید واژه throw انجام می‌شود.
  • Catch: در جایی که قرار است مشکل پیش آمده مدیریت شود، با استفاده از یک کنترل کننده استثنا (exception handler)، یک استثنا گرفته می‌شود.
  • Try: بلوک try بلوک کدی، که ممکن است استثنای مشخصی در آن رخ دهد را شناسایی می‌کند. به دنبال این بلوک یک یا چند بلوک catch می‌آید.

فرض کنیم در یک بلوک یک استثنا رخ دهد، یک متد با استفاده از ترکیب کلید واژه‌های try و catch آن استثنا را می‌گیرد. بلوک try/catch حوالی کدی قرار می‌گیرد که احتمال دارد استثنا در آن رخ دهد. کد درون بلوک try/catch، کد محافظت شده نامیده می‌شود. ساختار استفاده از try/catch به صورت زیر است.

try {
   // protected code
} catch( ExceptionName e1 ) {
   // catch block
} catch( ExceptionName e2 ) {
   // catch block
} catch( ExceptionName eN ) {
   // catch block
}

می‌توان لیستی از دستورات catch را برای گرفتن استثناهای مختلفی که ممکن است در بلوک try رخ دهد فراهم کرد، زیرا بسته به شرایط مختلف، ممکن است در یک بلوک try  استثناهای مختلفی رخ دهد.

پرتاب کردن استثنا یا Throwing Exceptions

با استفاده از دستور throw می‌توان استثناها را به هر جایی از بلوک پرتاب کرد. عملوند دستور throw نوع استثنا را مشخص می‌کند. این عملوند می‌تواند هر عبارتی باشد که نتیجه آن تعیین کننده نوع استثنا خواهد بود.

مطلب پیشنهادی:  انتزاع داده (Data Abstraction) در ++C

در زیر مثالی از استثنا Throw هنگام مواجه با مشکل تقسیم بر صفر آورده شده است.

double division(int a, int b) {
   if( b == 0 ) {
      throw "Division by zero condition!";
   }
   return (a/b);
}

گرفتن استثنا یا Catching Exceptions

بلوک catch‌ای که پس از بلوک try می‌آید قادر به گرفتن هر استثنایی می‌باشد. می‌توانید نوع استثنایی که می‌خواهید گرفته شود را با اعلان آن درون پرانتزهای بعد از کلید واژه مشخص کنید.

try {
   // protected code
} catch( ExceptionName e ) {
  // code to handle ExceptionName exception
}

کد فوق استثنایی از نوع ExceptionName را می‌گیرد. اگر می‌خواهید تعیین کنید که بلوک catch هر نوع استثنا احتمالی را دریافت کند، باید درون پرانتز … را قرار دهید.

try {
   // protected code
} catch(...) {
  // code to handle any exception
}

مثال زیر، یک استثنا تقسیم بر صفر پرتاب کرده و در بلوک catch گرفته می‌شود.

#include <iostream>
using namespace std;

double division(int a, int b) {
   if( b == 0 ) {
      throw "Division by zero condition!";
   }
   return (a/b);
}

int main () {
   int x = 50;
   int y = 0;
   double z = 0;
 
   try {
      z = division(x, y);
      cout << z << endl;
   } catch (const char* msg) {
     cerr << msg << endl;
   }

   return 0;
}

از آنجایی که یک استثنا از نوع *const char رخ می‌دهد، بنابراین حین گرفتن این استثنا، باید از *const char در بلوک catch استفاده کنیم. اگر کد بالا کامپایل و اجرا شود، خروجی زیر بدست می‌آید.

Division by zero condition!

استثناهای استاندارد در ++C

++C لیستی از استثناهای استاندارد در هدرفایل <exception> تعریف کرده است. این لیست در نمودار سلسله مراتبی والد-فرزند زیر نشان داده شده است.

مطلب پیشنهادی:  کلاس های ذخیره سازی در ++C

مدیریت استثناها در ++C یا Exception Handling

جدول زیر توضیح مختصری از این استثناها را مهیا ساخته است.

ردیف

استثنا و توضیح آن

1

std::exception

کلاس والد تمام استثناهای استاندارد در ++C است.

2

std::bad_alloc

ممکن است همراه با عملگر new پرتاب شود.

3

std::bad_cast

ممکن است همراه با عملگر dynamic_cast پرتاب شود.

4

std::bad_exception

این استثنا برای مدیریت استثناهای غیرمنتظره در ++C مفید است.

5

std::bad_typeid

ممکن است همراه با عملگر typeid پرتاب شود.

6

std::logic_error

استثنایی است که از لحاظ تئوری با خواندن کد قابل شناسایی است.

7

std::domain_error

هنگامی‌که که محدوده نامعتبر ریاضی بوجود آید، این استثنا پرتاب می‌شود.

8

std::invalid_argument

این استثنا به دلیل آرگومان‌های غیرمجاز پرتاب می‌شود.

9

std::length_error

این استثنا زمانی پرتاب می‌شود که یک std::string خیلی بزرگ ساخته شود.

10

std::out_of_range

این استثنا ممکن است توسط متد «at» پرتاب شود.

مثلاً: std:vector و ()[]std:bitet<>::operator

11

std::runtime_error

استثنایی است که از لحاظ تئوری با خواندن کد قابل تشخیص نیست.

12

std::overflow_error

این استثنا زمانی رخ می‌دهد که سرریز ریاضی (mathematical overflow) رخ دهد.

13

std::range_error

زمانی رخ می‌دهد که می‌خواهید یک مقدار خارج از محدوده ذخیره کنید.

14

std::underflow_error

این استثنا زمانی رخ می‌دهد که پاریز ریاضیاتی (mathematical underflow) رخ  دهد.

تعریف استثناهای جدید

می‌توان یک استثنای جدید را با ارث گرفتن از کلاس exception و بازنویسی توابع آن ایجاد کرد. مثال زیر نحوه استفاده از کلاس std::exception را برای پیاده‌سازی استثنا دلخواه به روش استاندارد نشان می‌دهد.

#include <iostream>
#include <exception>
using namespace std;

struct MyException : public exception {
   const char * what () const throw () {
      return "C++ Exception";
   }
};
 
int main() {
   try {
      throw MyException();
   } catch(MyException& e) {
      std::cout << "MyException caught" << std::endl;
      std::cout << e.what() << std::endl;
   } catch(std::exception& e) {
      //Other errors
   }
}

خروجی زیر حاصل می‌شود.

MyException caught
C++ Exception

در اینجا، ()what یک متد عمومی‌است که کلاس exception آن را فراهم کرده و توسط کلاس‌های فرزند بازنویسی می‌شود. این متد نتیجه استثنا را برمی‌گرداند.

منبع: ترجمه از سایت tutorialspoint.com

اگر این نوشته‌ برایتان مفید بود لطفا کامنت بنویسید.

مطالعه دیگر جلسات این آموزش<< جلسه قبلی                    جلسه بعدی >>
مطلب پیشنهادی:  اورلود کردن عملگرهای یگانی در ++C

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

یک دیدگاه

  1. بسیار عالی بود سپاس فراوان