Throwing Multiple Exceptions

Throwing Multiple Exceptions: Function विभिन्न प्रकार के Exceptional Conditions को Show करने के लिए विभिन्न प्रकार के कई Exception Objects को throw कर सकता है। इसे समझने के लिए निम्न Code Segment देखिए जिसमें दो अलग प्रकार की Problems को Report किया गया है:

	int AnyFunction()
	{
	  if (conditionA)           	// If conditionA is true,
		throw "Big trouble!";   // throw a string object.
	  if (conditionB)           	// If conditionB is true,
		throw Overflow();       // throw an Overflow class object.
	  return 123;               	// If no problems, return normally
	}

यदि conditionA True हो तो Function एक String Exception throw करता है, जो कि Big Trouble को Report करता है। यदि conditionB True हो तो Function एक Overflow Class का Object throw करता है, जिसे इस Code Segment की स्थिति में Class के Default Constructor को Call करके Create किया गया है। यदि Function कोई Error Condition Detect नहीं करता है तो ये अपने Caller को मान 123 प्रदान करते हुए Normally Return होता है। Exception के बारे में हमें निम्न तीन बातें ध्‍यान रखनी होती हैं:

  • विभिन्न Exceptional Conditions को Represent करते हुए एक Function अपने Caller को एक या एक से अधिक Exceptions को throw कर सकता है।
  • जो Function throw Statement को Execute करता है, वह Function एक Exception को throw करते ही तुरन्त Terminate हो जाता है।
  • पिछले दोनों Observations हमें बताते हैं कि एक Exception हमें एक Alternate Return Mechanism का Function Provide करता है। यानी किसी Exception के Generate होने पर यदि उसे Solve करने का catch Statement Block लिखा गया है, तो हम फिर से उस Function में Return हो जाते हैं, जहां पर Exception Generate हुआ था ना कि Exception Generate होने पर हमारा Program Terminate होता है।

हमारे पिछले उदाहरण का अन्तिम Point ये है कि AnyFunction सामान्‍यतया एक int मान Return करता है, लेकिन यदि कोई Exception Generate होता है, तो ये या तो एक String Return करता है या फिर Overflow Class का एक Object Return करता है। Function द्वारा return होने वाले इन Special मानों को केवल catch Statement ही प्राप्त कर सकता है। यदि Function द्वारा कोई Exception Generate नहीं होता है तो Function Normally Return होता है।

हम विभिन्न प्रकार के कई Exception Objects को catch Statements की एक Series के रूप में Handle कर सकते हैं। उदाहरण के लिए Error Class व String Exceptions को Handle करने के लिए सामान्‍यत% हम निम्नानुसार क्रम से दो catch Statements लिख सकते हैं:

	catch (Error e) 
	{
	  // Handle Error class exception objects
	}

	catch (const char* message) 
	{
	  // Handle string exception objects
	}

जैसाकि हमने पहले कहा कि जिस Statements या Member Functions में Exception की संभावना होती है, उन्हें try Block में लिखना चाहिए। यानी Exception Object को Application Program में Receive करने के लिए Class के Member Functions को try Block में Call करना चाहिए। जैसे

	try 
	{
	  int x = AnyFunction(); // Try calling this function
	}

	catch (Error e) 	// If function throws an exception
	{         
	  e.Report();           // call the Error objects Report function
	  exit(–1);             // and exit the program.
	}

try Block में वे Statements होते हैं जिनके लिए हम Exceptions को catch करना चाहते हैं। विभिन्न catch Statements को try Block के Just नीचे लिखा जाता है। यदि हम try Block व catch Block के बीच में एक Statement भी लिख दें, तो Compiler Error देता है।

Multiple try Blocks व उनसे Associated catch Blocks की Nesting भी की जा सकती है। उदाहरण के लिए निम्न Code Segment देखें-

	try 
	{
		  FunctionA();          // Try this function in outer block
		  try                   // Nested try block. Skipped on exception
		  {
			FunctionB();    // Try this function in inner block
		  }
		  catch (Error e) 	// Catch exceptions thrown by FunctionB()
		  {       
			e.Report();     // Call exception object’s Report()
		  }
	}
	catch (Error e)          	// Catch exceptions thrown by FunctionA()
	{  
		e.Report();             // Call exception object’s Report()
	}

चलिए, try को समझने के लिए हम कुछ और Examples देखते हैं जो ये Clear करने में मदद करेंगे कि try Block क्या होता है और उन्हें कैसे Use किया जाना चाहिए। एक try Block में एक या कई Statements हो सकते हैं। जैसे

	try 
	{
	  cout << "Here we go! << endl;
	  int x = AnyFunction();
	  cout << "x == " << x << endl;
	}

ये try Block सबसे पहले एक Message Display करता है। फिर ये AnyFunction को Call करता है और Function के Result Value को एक int प्रकार के Variable x में Assign करता है। यदि AnyFunction कोई Exception throw करता है तो try Block तुरन्त Terminate हो जाता है और AnyFunction से Return होने वाला मान Variable x को Assign नहीं होता है।

try Block के बाद एक या एक से अधिक catch Block हो सकते हैं। यदि catch Block ना हों तो try Block को Use करने का कोई मतलब नहीं रह जाता है। निम्न उदाहरण को देखिए जो AnyFunction Function द्वारा throw किए जाने वाले Multiple Exceptions को Handle कर रहा है।

	try 
	{
	  cout << "Here we go! << endl;
	  int x = AnyFunction();
	  cout << "x == " << x << endl;
	}

	catch (char* message) 
	{
	  cout << "Error! – " << message << endl;
	  exit(–1);  					// Optional
	}

	catch (Overflow) 
	{
	  cout << "Overflow!" << endl;
	  exit(–2);  					// Optional
	}
	// Program continues here normally

Expanded Function सबसे पहले AnyFunction को Call करता है। यदि Function Normally Return होता है तो x में Function द्वारा Return मान Assign हो जाता है, जिसे Output में Print कर दिया जाता है।

यदि कोई Exception Generate ना हो तो दोनों catch Statements Skip हो जाते हैं, क्योंकि catch Block में Declare किए गए किसी भी प्रकार का Exception Object AnyFunction द्वारा Create नहीं होता है।

यदि AnyFunction कोई Exception throw करता है, तो try Block तुरन्त Terminate हो जाता है और AnyFunction द्वारा throw किए गए Object को Appropriate catch Block Catch कर लेता है।

यदि Program में कोई catch Statement Block ना हो तो Function के Exception Generate करने पर Program एक Special Run Time Handler के Execute होने के कारण Terminate हो जाता है।

चलिए, अब एक Working Program देखते हैं जिसमें Exceptions का प्रयोग किया गया है। ये Example Stack Program से है, जिस पर हमें पिछले अध्‍यायों में भी कई बार चर्चा की है। सामान्‍यतया पिछले उदाहरणों में जिस Stack Class का हमने प्रयोग किया है, व Stack Program दो Common Errors को Detect नहीं करता है:

  • Underflow
  • Overflow

हमने इस Program में इन Exception को Handled करने के लिए Program में Exceptions का प्रयोग किया है। ये Program निम्नानुसार है:

	// demonstrates exceptions
	#include <iostream.h>
	#include <conio.h>
	const int MAX = 3;                    	 // stack holds 3 ints

	class Stack
	{
		private:
			int st[MAX];             // array of integers
			int top;                 // index of top of stack

		public:
			class Range              // exception class for Stack
			{                        // note: empty class body
			};

			Stack()                  // constructor
			{ top = -1; }

			void push(int var)
			{
				if(top >= MAX-1)               	// if stack full,
						throw Range(); 	// throw exception
				st[++top] = var;               	// put number on stack
			}

			int pop()
			{
				if(top < 0)                    	// if stack empty,
						throw Range();     	// throw exception
				return st[top—];             	// take number off stack
			}
	};

	void main()
	{
		Stack s1;
		try
		{
			s1.push(11);
			s1.push(22);
			s1.push(33);
			//    s1.push(44);                       // oops: stack full
			cout << "1: " << s1.pop() << endl;
			cout << "2: " << s1.pop() << endl;
			cout << "3: " << s1.pop() << endl;
			cout << "4: " << s1.pop() << endl;       // oops: stack empty
		}
		catch(Stack::Range)                              // exception handler
		{
			cout << "Stack Full or Empty" << endl;
		}

		cout << "Arrive here after catch (or normal exit)" << endl;
		getch();
	}

चलिए, इस Program के चार Features पर नजर डालते हैं जो कि Exceptions के साथ Deal करते हैं। Class Specification में एक Exception Class है। Class में कुछ Statements भी हैं जो Exceptions को throw करते हैं। Application Program के main() भाग में एक try Block है जिसमें Exception हो सकता है। इस Exception को Handle करने के लिए एक catch Block try Block के नीचे ही है। Program सबसे पहले stack Class में एक Inner Exception Class को निम्नानुसार Specify करता है:

class Range
{   
     // note: empty class body
};

हम देख सकते हैं कि इस Class की Body Empty है, इसलिए इस Class के Object में कोई Data या Member Function नहीं होगा। हमें इस Example Program में केवल Class के नाम Range की ही जरूरत है। इस नाम का उपयोग throw Statement को एक catch Block से Connect करने में होता है। हालांकि Class की Body हमेंशा Empty नहीं होती है।

जब main() Program उस Stack में से किसी Value को Pop करने की कोशिश करता है जो कि पहले से ही Empty है या तब किसी Value को Push करने की कोशिश करता है, जब Stack Full होता है, तो Underflow व Overflow की दोनों ही स्थितियों में Stack Class में एक Exception Generate होता है।

main() Program को ये बताने के लिए कि उसने Stack Class के किसी Object को Manipulate करते हुए कुछ Mistake किया है, Stack Class का Member Function इन स्थितियों को if Statement द्वारा Check करता है और Program में एक Exception throw करता है। Stack Class में दो स्थानों पर निम्न Statement द्वारा Exception throw किया जाता है:

throw Range();

इस Statement का Range Part Range Class के लिए Implicit Constructor को Invoke करता है। ये Constructor Range Class का एक Object Create करता है। इस Statement का throw Part Program Control को Exception Handler पर Pass कर देता है।

main() Function के वे सभी Statements जो कि Error या Exception Generate कर सकते हैं, जो कि Stack Class के Objects को Manipulate करते हैं, उन्हें एक Block में Enclosed रखा जाता है जिसकी शुरूआत try Keyword से होती है। यानी:

try
{
    // code that operates on objects that might cause an exception
}

ये Application Program के Normal Codes का एक सामान्‍य सा हिस्सा है। इसे हमें तब भी लिखना चाहिए जब हम Exceptions को Handle नहीं कर रहे होते हैं। Program के सभी Codes को इस Block में लिखना जरूरी नहीं होता है। केवल उन्हीं Codes को इस Block में लिखना होता है, जो Stack Class से Interact करते हैं।

वह Code जो Exception को Handle करता है, उसे भी एक Block मे Enclosed किया जाता है और इस Block की शुरूआत में catch Keyword का प्रयोग किया जाता है और एक Parenthesis में Exception Class का नाम लिखना होता है जो बताता है कि Exception किस Class से आ रहा है। Stack Class के सम्बंध में हमने इसे निम्नानुसार लिखा है:

	catch(Stack::Range)
	{
	   	// code that handles the exception
	}

इस Construction को Exception Handler कहते हैं। इसे try Block के तुरन्त बाद में लिखना जरूरी होता है। जब कोई Exception Generate होता है, तब निम्न क्रम में विभिन्न Events होते हैं:

  • try Block के बाहर के सभी Codes Normal Way में Execute होते हैं।
  • Control try Block में Enter होता है।
  • Try Block का कोई Statement Member Function में Error Generate कर देता है।
  • Member Function एक Exception throw करता है।
  • Control Exception Handler catch Block पर Transfer होता है, जो कि try Block के Just बाद में Define कि; गया होता है।

यही क्रम हर बार संचालित होता है। हम देख सकते हैं कि Resulting Code कितने Clean हैं। try Block का कोई भी Statement Exception Generate कर सकता है, लेकिन हमें हर Statement के Return Value को Check करने की जरूरत नहीं होती है।

क्योंकि try-throw-catch Statements का Arrangement उन सभी Exceptions को Automatically Handle कर लेता है। हमारे इस उदाहरण में दो Statements ऐसे लिखे गए हैं जो Exceptions Generate करते हैं। इनमें से पहला Statement निम्नानुसार है:

s1.push(44);  // pushes too many items

यदि हम इस Statement के आगे लगे हुए Comment Symbol को हटा दें, तो ये Statement एक Exception को Generate करता है। दूसरा Statement निम्नानुसार है:

cout << “4: ” << s1.pop() << endl;  // pops item from empty stack

चलिए, अब हम Multiple Exceptions का भी उदाहरण देख लेते हैं। इसके लिए हमने पिछले उदाहरण को ही थोडा Modify किया है। ये Modified Program निम्नानुसार है:

// demonstrates two exception handlers
	#include <iostream.h>
	#include <conio.h>
	const int MAX = 3;                              	// stack holds 3 ints
	class Stack
	{
		private:
			int st[MAX];                            // stack: array of integers
			int top;                                // index of top of stack

		public:
			class Full { };                         // exception class
			class Empty { };                        // exception class

			Stack()                                 // constructor
				{ top = -1; }

			void push(int var)                      // put number on stack
			{
				if(top >= MAX-1)                // if stack full,
					throw Full();           // throw Full exception
				st[++top] = var;
			}
  
			int pop()                               // take number off stack
			{
				if(top < 0)                     // if stack empty,
					throw Empty();          // throw Empty exception
				return st[top--];
			}
	};

	void main()
	{
		Stack s1;
		try
		{
			s1.push(11);
			s1.push(22);
			s1.push(33);
			//    s1.push(44);                       // oops: stack full
			cout << "1: " << s1.pop() << endl;
			cout << "2: " << s1.pop() << endl;
			cout << "3: " << s1.pop() << endl;
			cout << "4: " << s1.pop() << endl;       // oops: stack empty
		}
		catch(Stack::Full)
		{
			cout << "Stack Full" << endl;
		}
		catch(Stack::Empty)
		{
			cout << "Stack Empty" << endl;
		}
		getch();
	}

// Output
   Stack Full 
   Stack Empty

इस Program में जब Stack Full होता है और push() Member Function को Program Call कर लेता है, तो निम्न Statement Execute होता है:

throw Full();

और जब किसी Empty Stack से किसी Item को Pop किया जाता है तब निम्न Statement Execute होता है:

throw Empty();

Output के आधार पर हम देख सकते हैं कि हर Exception के लिए एक अलग catch Block Execute होता है। यानी:

	try
	{
	   	// code that operates on Stack objects
	}
	catch(Stack::Full)
	{
	  	// code to handle Full exception
	}
	catch(Stack::Empty)
	{
	   	// code to handle Empty exception
	}

इस Code Segment में हम देख सकते हैं कि एक try Block के बाद लिखे गए सभी catch Statements switch Statement की तरह हैं, जिनमें से केवल वही catch Statement Execute होता है, जिससे सम्बंधित Exception Generate होता है।

चलिए, एक और उदाहरण देखते हैं। इस उदाहरण में हमने पिछले अध्‍याय के Distance Class को Use किया है। एक Distance Object में एक Integer Feet Value होती है और एक Floating Point Inches Value होती है।

इस Class के साथ समस्या ये है कि Class का User Object को जितने चाहे उतने Inches Initialize कर सकता है, जबकि Inches का मान हमेंशा 12 से कम होता है। यदि User Inches में 12 से अधिक मान Input कर देता है तो समस्या तब पैदा होती है जब Class Inches के मान के साथ Arithmetic करने की कोशिश करता है। क्योंकि Arithmetical Routine जैसे कि Operator+() Function ये मानता है कि Inches का मान 12 से कम होता है। इससे Output में हमें 4’-20’’ जैसा एक गलत Result भी प्राप्त हो सकता है, जबकि Inches का मान 20 हो ही नहीं सकता।

	// exceptions with Distance class
	#include <iostream.h>
	#include <string.h>                            	 // for strcpy()
	#include <conio.h>

	class Distance                                   // English Distance class
	{
		private:
			int feet;
			float inches;

		public:
			class InchesEx { };               // exception class
			Distance()                        // constructor (no args)
			{ 
				feet = 0; inches = 0.0; 
			}
			Distance(int ft, float in)        // constructor (two args)
			{
				if(in >= 12.0)            // if inches too big,
					throw InchesEx(); // throw exception
				feet = ft;
				inches = in;
			}
			void getdist()                    // get length from user
			{
				cout << "\nEnter feet: ";  cin >> feet;
				cout << "Enter inches: ";  cin >> inches;
				if(inches >= 12.0)        // if inches too big,
					throw InchesEx(); // throw exception
			}
			void showdist()                   // display distance
			{ 
				cout << feet << "\'-" << inches << '\"'; 
			}
	};

	void main()
	{
		try
		{
			Distance dist1(17, 3.5);           // 2-arg constructor
			Distance dist2;                    // no-arg constructor 
			dist2.getdist();                   // get distance from user
							   // display distances
			cout << "\ndist1 = ";  dist1.showdist();
			cout << "\ndist2 = ";  dist2.showdist();
		}
		catch(Distance::InchesEx)                   // catch exceptions
		{
			cout << "\nInitialization error: "
			"inches value is too large.";
		}
	}

हमने इस Distance Class मे एक Exception Class को Include किया है। ऐसा करने पर जब भी User Inches का मान 12 या 12 से अधिक Initialize करता है, एक Exception Generate होता है।

ऐसा दो स्थानों पर होता है। पहला तब जब User Object Create करके 2-Arguments Constructor द्वारा उसे Initialize करता है और दूसरा तब जब Program User से Inches Input करने के लिए Prompt करता है। main() Function में Distance Object के साथ जितने भी Interactions होते हैं, उन्हें try Block में Enclosed किया गया है और हर catch Block एक Error Message Display करता है।

मानलो कि यदि Application Generate होने वाले Exception के बारे में और अधिक जानकारी चाहता हो, तब क्या होगा\ उदाहरण के लिए यदि पिछले उदाहरण के आधार पर ही Programmer ये भी जानना चाहता हो कि वास्तव में उसने कौनसा गलत मान Input किया था\

साथ ही यदि इसी प्रकार का Exception किसी दूसरे Member Function द्वारा throw किया जाए, तो ये जानना महत्वपूर्ण हो सकता है कि किस Member Function ने Exception Generate किया है। क्या इस तरह की जानकारी को उस Member Function से Application Program में Pass किया जा सकता है, जिसमें Exception Generate होता है \

इस सवाल का जवाब पहले ही दिया जा चुका है जिसमें हमने कहा था कि किसी Function में Exception होने पर केवल Control ही Handler में Pass नहीं होता है, बल्कि Exception Class के Construction द्वारा एक Object भी Create होता है। पिछले उदाहरण के आधार पर यदि हम कहें तो जब हम निम्न Statement द्वारा Application Program में Exception throw करते हैं:

        throw InchesEx();

तब वास्तव में हम InchesEx प्रकार का एक Object भी Create कर रहे होते हैं। यदि हम Exception Class में Data Members को Add करें तो हम इन Object के Create होते समय इन्हें Initialize कर सकते हैं। Exception Handler इस Object के Data को Exception को Receive करते समय प्राप्त कर सकता है। ये ठीक उसी तरह है जैसे हम किसी Ball पर कोई Message लिखें और उसे किसी पडौसी के घर में throw कर दें। (Throwing Multiple Exceptions – StackOverflow)

	// exceptions with arguments
	#include <iostream.h>
	#include <string.h>                          	// for strcpy()
	#include <conio.h>

	class Distance                                  // English Distance class
	{
		private:
			int feet;
			float inches;

		public:
			class InchesEx                 // exception class
			{
				public:
					char origin[80]; // for name of routine
					float iValue;    // for faulty inches value

					InchesEx(char* or, float in)  // 2-arg constructor
					{
						strcpy(origin, or);   // store string
						iValue = in;          // store inches
					}
			};// end of exception class

			Distance()                              // no-arg constructor
			{ feet = 0; inches = 0.0; }

			Distance(int ft, float in)              // 2-arg constructor
			{
				if(in >= 12.0)
					throw InchesEx("2-arg constructor", in);
				feet = ft;
				inches = in;
			}

			void getdist()                           // get length from user
			{
				cout << "\nEnter feet: ";  cin >> feet;
				cout << "Enter inches: ";  cin >> inches;
				if(inches >= 12.0)
					throw InchesEx("getdist() function", inches);
			}

			void showdist()                           // display distance
			{ cout << feet << "\'-" << inches << '\"'; }
	};

	void main()
	{
		try
		{
			Distance dist1(17, 3.5);      	// 2-arg constructor
			Distance dist2;                	// no-arg constructor
			dist2.getdist();               	// get value
							// display distances
			cout << "\ndist1 = ";  dist1.showdist();
			cout << "\ndist2 = ";  dist2.showdist();
		}
		catch(Distance::InchesEx ix)             // exception handler
		{
			cout << "\nInitialization error in " << ix.origin
			<< ".\n   Inches value of " << ix.iValue
			<< " is too large.";
		}
	}

जब कोई Exception throw होता है तब Data को Pass करने के Operation के तीन भाग होते हैं:

  • Exception Class के Constructors व Data Members को Specify करना।
  • जब Exception throw हो तब इस Constructor को Initialize करना। और
  • जब Exception catch हो तब Object के Data को Access करना।

CPP Programming Language in Hindiये Article इस वेबसाईट पर Selling हेतु उपलब्‍ध EBook C++ Programming Language in Hindi से लिया गया है। इसलिए यदि ये Article आपके लिए उपयोगी रहा, तो निश्चित रूप से ये पुस्तक भी आपके लिए काफी उपयोगी साबित होगी। 

C++ Programming Language in Hindi | Page: 666 | Format: PDF

BUY NOW GET DEMO REVIEWS