الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

Exception Handling معالجة الاستثناءات _ الدرس الأول

بدأه time1 في 24 أبريل 2008 · 4 رد · 9,272 مشاهدة · في المواضيع والدروس
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

معالجة الاستثنائات Exception Handling

السلام عليكم ورحمة الله وبركاته

مرحبا اخواني واخواتي بكم في هذه الجزء من اجزاء السي ++ , سنتكلم اليوم عن معالجة الاستثناءات Exception Handling وهي احد المواضيع الحساسة التي يجب ان يتقنها كل مبرمج على وجه العموم ,

سأقوم بتقسيم درسي إلى عدة اجزاء ابدا بها من الصفر متدرجا لاعلى لاصل إلى ماسأصل إليه , وعلى بركة الله نبدأ :

-----------------------------------------------------------------------------------------------------------------------

1- نظرة عامة

لقد جرى تصميم اسلوب معالجة الاستثناءات للتعامل مع الاخطاء المتزامنة Synchronous errors مثل محاولة القسمة على الصفر , فعند استخدام اسلوب معالجة الاستثناءات يقوم البرنامج بالتحقق من القيمة المدخلة قبل اجراء القسمة من خلال اجراء استثناء اذا كانت هذه القيمة مساوية للصفر .

تفيد تقنية معالجة الاستثناءات في التعامل مع المشاكل التي تحدث اثناء تفاعل البرنامج مع العناصر البرمجية الاخرى مثل : دوال البناء Constractor ودوال الهدم Destractor ...إلخ , فبدلا من قيام هذه العناصر البرمجية بمعالجة الاخطاء داخليا فإنها تستخدم الاستثناءات لإعلام البرنامج بحدوث المشاكل , يساعد ذلك المبرمجين على بناء اسلوب منفصل للتعامل مع الاخطاء من اجل كل تطبيق .

2- التعامل مع الاستثناءات :

توفر لغة C++ الكتل Try لمعالجة الاستثناءات , وتتألف الكتلة من الكلمة المفتاحية Try متبوعة بـ { } والذان يتضمنان تعريف الكتلة والاستثناءات التي يمكن ان تحصل.

بعد ذلك نستخدم كتلة catch اذا تضمن الاستثناء على وسيط فيجب ان نستخدم كلمة catch للاستقبال وللتفاعل مع هذا الاستثناء .

نستخدم الكلمة throw من اجل اللقاء الاستثناء متبوعة بنوع الاستثناء كمعامل لها .

مثال :

هذا البرنامج يأخذ رقين ويخرج حاصل قسمتهم , سنتأكد من ان الرقم الثاني ليس صفر

	int x,y;
	cin>>x>>y;
	try{
	if(y==0)
		throw;
	cout<<x/y;}
	catch(...){ }

هنا ستكون النتائج على شكل مربع حوار يحتوي على الازرار : تجاهل , اعادة , احباط .

كتابة كلمة throw فقط لوحدها يستدعي المدمر ويقفل البرنامج , اما اذا عدلنا كتابة البرنامج السابق وجعلناه يلقي سلسلة حرفية مثلا :

throw "Time1";

سيكون الناتج لا شئ لعدم كتابة اي تعليمات في كتلة catch , لنقم بطباعة جملة داخل كتلة catch مثلا :

	catch(...){cout<<"Exception Handling"<<endl; }

سنلاحظ الان اذا قمنا بإدخال الصفر , سيتم القاء استثناء وتقوم كتلة catch باستقبال الاستثناء وتنفيذ التعليمات وهي طباعة الجملة Exception Handling .

تعد السلسة الحرفية "Time1" بمثابة الخطأ الذي تلقيه throw وبما انه لا يوجد كتلة catch تتعامل مع النصوص , فسيتم استدعاء كتلة catch العامة .

اذا عدلنا كتلة catch لتتعامل مع السلاسل الحرفية:

	catch(char * x){cout<<"Exception Handling:"<<x<<endl;

فسيكون الناتج Exception Handling :Time1 .

يمكن لنا ان نحدد اكثر من كتلة لـ catch تتعامل مع استثناءات مختلفة .

سنقوم الان بتطوير البرنامج السابق وهذه المرة سيقوم باختبار الرقمين , اذا كان الرقم الاول اكبر من 10 يكون عبارة عن استثناء , يطبع لنا More واذا كان الرقم الثاني صفر يطبع لنا Zero :

	int x,y;
	cin>>x>>y;
	try{
	if(y==0)
		throw "Zero";
	if(x>10)
		throw x;
	cout<<x/y;}

	catch(char * x){cout<<"Exception Handling:"<<x<<endl; }
	catch(int  x){cout<<"Exception Handling:"<<x<<"More than 10"<<endl; }

بقي ان اتطرق إلى الدالة exceptions الموجودة في ملف iostream وهي تقوم بمعرفة الخطأ بمجرد ادخال بيانات في متغير لا يقبل ذلك , كأدخال اسم في متغير عدد صحيح int

	int x;
		cin.exceptions(cin.failbit);
	try
	{
		cin>>x;
		cout<<x;
	}
	catch(...){cerr<<"This Is Not Number"<<endl;}

-----------------------------------------------------------------------------------------------------------------------

استخدام OOP

فيما سبق : لو اعاد throw سلسلة نصية وكانت catch تتعامل مع النصوص وتؤدي غرض معين مثل طباعة جملة على الشاشة , فلن نستيع مثلا ان نعيد سلسلة نصية اخرى لتتعامل معها كتلة catch جديدة.

على سبيل المثال :

		throw "Zero";
		throw "More Than 10"

كليهما سيتعاملان مع

catch(char * x)

لكي نفرق بينهم كنا نستخدم انواع مختلفة مع throw مثلا في مثالنا السابق كنا استخدمنا مع throw للرقم الثاني سلسلة نصية وللرقم الاول متغير عددي

		throw "Zero";
		throw x;

عندها ستتعامل كتلة catch مع النوعية الملقاة من قبل throw , من خلال هذه الطريقة نستطيع ان نعيد قيم معدودة من الاستثناءات , مثل char , int , float , double ..إلخ

ولكي نجد حلا يجب ان نعرف كائنات Object تنتمي إلى انواع جديدة غير الانواع التقليدية المعرفة مسبقا .

استخدام الكائنات:

سنقوم اولا بكتابة الصنف class الذي يمثل لنا مجموعة الاستثناءات في هذا البرنامج

class DZero
{
public:
	DZero(int =0,int=0);
	void Print_Divive();
	class Zero{};
	class More{};

private:
	int x,y;

[code]DZero::DZero(int a,int b):x(a),y(b){}

void DZero::Print_Divive()
{
	cerr<<x/y<<endl;
}

داخل البرنامج الرئيسي , تقوم كتلة try تقوم بإلقاء عدت استثنائات من الاستثنائات الموجودة في الصنف الرئيسي DZero على حسب الشروط المطروحة , إما اذا كان الرقم الاول اكبر من 10 , او اذا كان الرقم الثاني يساوي الصفر .

try
	{
	cin>>x>>y;
	if(x>10)
		throw DZero::More();
	if(y==0)
		throw DZero::Zero();
	DZero d(x,y);
	d.Print_Divive();
	}

الجوهر الوحيد يكمن في كتل catch والانواع التي تستقبلها لتنفيذ تعليماتها ,

catch(DZero::More)
{cout<<"More"<<endl;}
catch(DZero::Zero)
{cout<<"Zero"<<endl;}

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

كما نستطيع ان نعرف دوال داخل الاصناف More & Zero ونستدعيها لتقوم بتنفيذ تعليمات مختلفة , مثلا :

class DZero
{
public:
	DZero(int =0,int=0);
	void Print_Divive();
	class Zero{};
	class More
	{
	public:
		void Print();


	};

private:
	int x,y;
};

وان نعيد نجعل الدالة Print الخاصة بالصنف More تقوم بطباعة الجملة More

void DZero::More::Print()
{
	cout<<"More"<<endl;
}

DZero::DZero(int a,int b):x(a),y(b){}

void DZero::Print_Divive()
{
	cerr<<x/y<<endl;
}

في هذه الحالة سنقوم بتعديل catch الخاصة باستقبال الاستثناء more إلى

catch(DZero::More a)
{
	a.Print();
}

----------------------------------------------------------------------------------------------------------------------

اخير سنأخذ فكرة عن الملف الرأسي exception وسنزيد في التفصيل من خلال الدروس القادمة

سأقوم بكتابة نفس المثال السابق , القسمة على الصفر , ولكن هذه المرة بالاستفادة من الملف exception

نقوم باستدعاء الملف

#include <exception>
using std::exception;

نقوم بإنشاء صنف جديد DZero بالاشتقاق من الصنف exception

class DZero :public exception
{
public:
   DZero::DZero()
	  : exception( "Error : Zero" ) {}
};

في داخل try نلقي الاستثناء كما تعودنا من نوع DZero

try
	{
	cin>>x>>y;

	if(y==0)
		throw DZero();

	cout<<x/y;
	}

وكتلة catch تقوم باستقبال الاستثناء وتقوم بطباعة الجملة Error:Zero من خلال استدعاء الدالة What

catch(DZero & d)
{cout<<d.what()<<endl;}

إلى هنا اكون قد اعطيت فكرة عامة عن الاستثناءات , في المرة القادمة سأتدرج إلى مستوى اعلى وسنتحدث عن :

معالجة الاستثناءات الغير متوقعة .

إفراغ مكدس الاستدعاءات .

معالجة اخفاقات new

الصف auto_ptr والحجز الديناميكي للذاكرة .

التعامل مع الملفات والمصادر .

امثلة.

تمنياتي بالتوفيق

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#2

واصل ابداعك

كان ودي اناقش لاني مبتدى بس الان اختبارات لكن بعد الاختبارات لي رجعة

#3

كنت هنا ..

استفدت من شرحك واصل فنحن بحاجة لمثل هذه الشروحات

وفقك المولى لما يحب ويرضى

#4

مساء الخير والسلام عليكم

ممكن حد بالله عليكم يفدنى فى السؤال بتاعى وهو بيقول

ازاى اقسم برنامج سى بلاس بلاس واستدعى الاقسام دى من خلال الانكلود

#5

كنت هنا ..

استفدت من شرحك واصل فنحن بحاجة لمثل هذه الشروحات

وفقك المولى لما يحب ويرضى

مواضيع مشابهة