Virtual Destructor

Virtual Destructor: यदि हम किसी Class में किसी भी Virtual Function को Use करते हैं तो हमें उस Class के Destructors भी Create करने होते हैं। ऐसा क्यों किया जाता है इसे समझने के लिए हम एक Program देखते हैं, जिसमें दो Classes हैं और दोनों में कोई Virtual Destructor नहीं है।

	// non-virtual function used as base class destructor
	#include <iostream.h>
	#include <conio.h>

	class BaseClass
	{
		public:
			~Base()
			{ cout << “\nBaseClass destructor"; }
	};

	class DerivedClass : public Base
	{
		public:
			~DerivedClass()
			{ cout << “\DerivedClass destructor"; }
	};
	void main()
	{
		Base* pb = new DerivedClass;
		delete pb;    	// output is “Base Destructor”
		cout << “\nProgram terminates";
	}

जैसाकि हम जानते हैं कि एक Derived Class के Object में उसके Base Class व Derived Class दोनों के Data होते हैं, हालांकि ऐसा Non-Virtual Destructors के साथ नहीं होता है। ये तय करने के लिए कि Object पूरी तरह से Destroy हो, Base Class व Derived Class दोनों Classes के Destructors का Execute होना जरूरी होता है। लेकिन इस Program का Output निम्नानुसार प्राप्त हो रहा है:

BaseClass Destructor
Program terminates

ये समस्या बिल्कुल उसी प्रकार की है जैसी सामान्‍य Functions के साथ हमने पहले भी देखी है। यदि कोई Function Virtual नहीं है, तो जब किसी Base Class के Pointer के कारण ये Function Invoke होता है, चाहे Pointer में किसी Derived Class के Object का ही Pointer क्यों ना हो, केवल Base Class के Function का ही  Execution होता है। इसीलिए इस Program में DerivedClass का Destructor कभी Call नहीं होता है।

इस समस्या के समाधान के लिए हमें Base Class के Destructor को भी Virtual बनाना पडता है। इसे समझने के लिए इसी Program को हम निम्नानुसार Modify कर सकते हैं।

	// non-virtual function used as base class destructor
	#include <iostream.h>
	#include <conio.h>

	class BaseClass
	{
		public:
			virtual ~Base() = 0
			{ cout << “\nBaseClass destructor"; }
	};

	class DerivedClass : public Base
	{
		public:
			~DerivedClass()
			{ cout << “\DerivedClass destructor"; }
	};

	void main()
	{
		Base* pb = new DerivedClass;
		delete pb;    				// output is “Base Destructor”
		cout << “\nProgram terminates";
	}

// Output
   DerivedClass Destructor
   Base Destructor
   Program terminates

हम Output से देख सकते हैं कि अब दोनों Classes के Destructors Call हो रहे हैं। इसी उदाहरण के आधार पर हम एक और Realistic Example देखते हैं।

Derived Class के Objects उन System Resources को Use कर सकते हैं जिन्हें उस समय Released करना पडता है जब Derived Class के Object को Destroy करना होता है। अन्‍यथा Derived Class के Object के लिए Allocate की गई Memory Inaccessible हो जाती है और Memory Waste होती है।

हमारा अगला उदाहरण Person Example का ही Modified रूप है। इस Program में एक person Class है और उस Class से gradStudent नाम की एक Class को Derived किया गया है। इस Program में हम gradStudent के लिए Memory Allocate करने के लिए new Operator का प्रयोग कर रहे हैं और Student के नाम व Thesis Topic को Store करने के लिए Array के स्थान पर उस Allocated Memory का प्रयोग कर रहे हैं।

Person Class में Data Item name का एक Pointer है और gradStudent Class में Data Item thesis Topic का एक Pointer है। हम name व topic के लिए Memory Allocate करने के लिए Constructors का प्रयोग कर रहे हैं और Memory को Delete करने के लिए Destructors को Use कर रहे हैं। Program निम्नानुसार है:

Program
	// Virtual Destructors and the person class
	#include <iostream.h>
	#include <string.h>              	// for strlen(), strcpy()
	#include <conio.h>

	class person                     	// person class
	{
		protected:
			char* nameptr;

		public:
			person(char* np)        // 1-arg constructor
			{
				int length = strlen(np);           // find length of name
				nameptr = new char[length+1];      // allocate memory
				strcpy(nameptr, np);               // put name in memory
			}

			virtual ~person() = 0                 	   // destructor
			{
				cout << “\nperson Destructor";
				if(nameptr != NULL)                // if it has it been used,
				delete[] nameptr;                  // delete name
			}

			virtual void putData()
			{ cout << “\nName = ” << nameptr; }
	};  // end person class

	class gradStudent : public person  	// gradStudent class
	{
		private:
			char* topicptr;                       	// ptr to thesis topic

		public:
			gradStudent(char* n, char* t) :       	// 2-arg constructor
					person(n), topicptr(NULL)
			{
				int length = strlen(t);         // find length of topic
				topicptr = new char[length+1];  // allocate memory
				strcpy(topicptr, t);            // put topic in memory
			}

			~gradStudent()                        	// destructor
			{
				cout << “\ngradStudent destructor";
				if(topicptr != NULL)            // if it has it been used,
				delete[] topicptr;              // delete thesis topic
			}

			virtual void putData()
			{
				person::putData();
				cout << “\n  Thesis topic = ” << topicptr;
			}
	};  // end gradStudent class

	void main(void)
	{
		int j;
		const int total = 3;
		person* persPtr[3];                	// list of pointers to persons
		char name[40];                     	// temporary storage
		char topic[80];

		for(j=0; j<total; j++)             	// get data, make gradStudents
		{
			cout << “\nEnter name: "; 
			cin >> name;
			cout << “   Enter thesis topic: "; 
			cin >> topic;
			persPtr[j] = new gradStudent(name, topic);
		}

		for(j=0; j<total; j++)            	// display gradStudents
			persPtr[j]->putData();

		for(j=0; j<total; j++)            	// delete gradStudents
			delete persPtr[j];
		getch();
	}  // end main()

Person Class व gradStudent Class का Constructor new Operator द्वारा grad Student नाम व Thesis के लिए Memory प्राप्त करता है। इनका Destructor Memory को Delete करता है। हर Object के लिए दोनों Destructors Call होते हैं, जैसाकि Output में दिखाई दे रहा है, इसलिए निश्चित रूप से Allocate होने वाली सारी Memory Free हो रही है। Program से User का Interaction कुछ निम्नानुसार होता है। चूंकि हमने cin>> Stream Operator को Use किया है, इसलिए केवल One-Word String ही Accept हो रही है।

Enter name: Govind
   Enter thesis topic: ManMeJabMohNahiRakhte

Enter name: ManMohan
   Enter thesis topic: NaamDharayaHaiKyon

Enter name: BalGopal
   Enter thesis topic: NandLalManMohan

Name = Govind
  Thesis topic = ManMeJabMohNahiRakhte
Name = ManMohan
  Thesis topic = NaamDharayaHaiKyon
Name = BalGopal
  Thesis topic = NandLalManMohan
gradStudent destructor
person Destructor
gradStudent destructor
person Destructor
gradStudent destructor
person Destructor

अब सवाल ये पैदा होता है कि Destructors को किस स्थिति में Virtual रखना चाहिए। इसका जवाब है कि निम्न परिस्थितियों में किसी Virtual Class में Virtual Destructors का प्रयोग करना चाहिए:

  • जब Virtual Base Class से Derived Class को Create किया जा रहा हो।
  • जब Derived Class के Objects को Base Class के Pointers द्वारा Delete किया जा रहा हो।
  • जब Destructors इन दोनों प्रकार में से किसी एक Class के सम्बंध में कुछ महत्वपूर्ण काम जैसे कि Memory De-Allocation का काम कर रहे हों।

यदि किसी Class में Virtual Functions हों,  तो उपरोक्त सभी स्थितियां कभी ना कभी पैदा होती ही हैं। जब किसी Class में एक Function को Virtual बनाया जाता है, तब ये जरूरी नहीं होता है कि Class के सभी अन्‍य Functions को Virtual बनाया जाए। क्योंकि यदि Class में एक भी Function Virtual होता है तो उस Class के हर Object में एक Virtual Table Add हो जाती है।

कई बार हमें ये पता करने की जरूरत पड जाती है कि कोई Object किस Class का है। ऐसी जरूरत हमें तब पडती है जब हमारे पास Objects के Pointers का एक Array होता है और हम इन Pointers का प्रयोग करके विभिन्न Classes के Objects को Point कर सकते हैं। यदि हमारे पास कोई Global Function हो जो इस Array के किसी Object के Pointer को Access करना चाहता हो, तो Function ये कैसे पता लगा पासगा कि उसे किस Object का Pointer प्राप्त हो रहा है।

जैसाकि हमने पिछले Programs में देखा है कि हम इस तरह के Pointers का प्रयोग किसी Object के Reference में Virtual Functions को Call करने के लिए करते है और उसी Class का Function Call होता है जिसकी Class के Object का Pointer उस Object में होता है। Virtual Function का Mechanism ये जानता है कि Pointer किस प्रकार के Object को Point कर रहा है। लेकिन ये Information तुरन्त Programmer को पता नहीं चल सकती है।

सामान्‍यतया किसी Object से उसी Class का पता लगाने के लिए ज्यादातर Compilers में एक typeid() नाम का Function होता है जो हमें किसी Object के Class को पता करने की सुविधा प्रदान करता है। इस प्रक्रिया को Runtime Type Identification कहा जाता है। निम्न Program द्वारा हम इस प्रक्रिया को समझ सकते हैं:

// Program
// Demonstrates typeid() function
	#include <iostream.h>
	#include <typeinfo.h>      	// for typeid()
	#include <conio.h>

	class ClassA
	{ };

	class ClassB
	{ };

	void main()
	{
		ClassA ObjA;
		ClassB ObjB;

		if( typeid(ObjA) == typeid(ClassA) )
			cout << “\nObjA is an object of ClassA";
		else
			cout << “\nObjA is not a member of ClassA";

		if( typeid(ObjB) == typeid(ClassA) )
			cout << “\nObjB is an object of ClassA";
		else
			cout << “\nObjB is not an object of ClassA";
	}

// Output
   ObjA is an object of ClassA
   ObjB is not an object of ClassA

किसी Object द्वारा उस Object की Class को जानने के लिए हम जिस typeid() Function का प्रयोग करते हैं, वह Function typinfo.h नाम की Header File में होता है, इसलिए इस Header File को हमें हमारे Program में Include करना जरूरी होता है।

हम typeid() Function के Operand के रूप में Class या Object का नाम Use कर सकते हैं, जिससे हमें पता चल सकता है कि कोई Object किसी Particular Class का है या नहीं। typeid() function से एक Pointer Return होता है जिसका प्रयोग हम Comparison करने के लिए कर सकते हैं। typeid() Function Basic प्रकार के Data Type के साथ भी समान प्रकार से काम करता है।

हमें सामान्‍यतया RTTI की जरूरत तब होती है जब Base Class Pointers में Derived Class के Objects के Address होते हैं। ये वह स्थिति होती है जिसके बारे में हमने पहले चर्चा की है जिसमें Virtual Functions किसी Class को Polymorphic बनाते हैं। ये तब होता है जब हमारे पास Pointers to Objects का एक Array होता है या जब Base Class के Argument लेने वाले Functions Arguments के रूप में Objects के Reference या Pointers लेते हैं।

मानलो कि हम person Class के उदाहरण को Modify करके ये चाहते हैं कि Program ये Indicate करे कि वह किस प्रकार के person (Student, teacher या person) को Display कर रहा है। निम्न उदाहरण person Class के उदाहरण का Modified Program है:

// Program
// Runtime Type Identification with person class
	#include <iostream.h>
	#include <typeinfo.h>              	// for typeid()
	#include <conio.h>

	class person                       	// person class
	{
		protected:
			char name[40];

		public:
			virtual void getData()
			{ cout << “   Enter name: ";  cin >> name; }

			virtual void putData()
			{ cout << “Name=” << name; }
	};                              		// student class

	class student : public person
	{
		private:
			float gpa;                   	// grade point average

		public:
			void getData()               	// get student data from user
			{
				person::getData();
				cout << “   Enter student's GPA: "; 
				cin >> gpa;
			}

			void putData()
			{
				person::putData();
				cout << “   GPA=” << gpa;
			}
	};                               		// teacher class

	class teacher : public person
	{
		private:
			int numPubs;                  	// number of papers published

		public:
			void getData()                	// get teacher data from user
			{
				person::getData();
				cout << "   Enter number of teacher's publications: "; 
				cin >> numPubs;
			}

			void putData()
			{
				person::putData();
				cout << “   Publications=” << numPubs;
			}
	};

	void main(void)
	{
		person* persPtr[100];                 	// list of pointers to persons
		int n = 0;                            	// number of persons on list
		char choice;                          	// 'p', 's', etc.

		do
		{
			cout << “Enter person, student or teacher (p/s/t): "; 
			cin >> choice;

			if(choice=='s')                 // put new student
				persPtr[n] = new student;       //    in array
	
			else if(choice=='t')                 	// put new teacher
				persPtr[n] = new teacher;       //    in array

			else                                 	// put new person
				persPtr[n] = new person;        //    in array

				persPtr[n++]->getData();  	// get data for person
				cout << “   Enter another (y/n)? ";   	// do another person?
				cin >> choice;
		} while( choice=='y' );              	// cycle until not 'y'

		for(int j=0; j<n; j++)
		{                                    	// display class name
			if( typeid(*persPtr[j]) == typeid(student) )
				cout << “\nStudent, "; 

			else if( typeid(*persPtr[j]) == typeid(teacher) )
				cout << “\nTeacher, "; 

			else if( typeid(*persPtr[j]) == typeid(person) )
				cout << “\nPerson,  "; 

			else
				cout << “\nError: unknown type";
				persPtr[j]->putData();  // display name
		} // end for

		for(int j=0; j<n; j++)               	// delete all objects
			delete persPtr[j];
		getch();
	}  // end main()

// Output
Enter person, student or teacher (p/s/t): p
   Enter name: BalKishan
   Enter another (y/n)? y
Enter person, student or teacher (p/s/t): s
   Enter name: Madhav
   Enter student's GPA: 4
   Enter another (y/n)? y
Enter person, student or teacher (p/s/t): s
   Enter name: Gopal
   Enter student's GPA: 3
   Enter another (y/n)? y
Enter person, student or teacher (p/s/t): t
   Enter name: Nandlal
   Enter number of teacher's publications: 20
   Enter another (y/n)? y
Enter person, student or teacher (p/s/t): t
   Enter name: BalGopal
   Enter number of teacher's publications: 120
   Enter another (y/n)? n

Person,  Name=BalKishan
Student, Name=Madhav   GPA=4
Student, Name=Gopal   GPA=3
Teacher, Name=Nandlal   Publications=20
Teacher, Name=BalGopal   Publications=120

जैसाकि इस Program से हम देख सकते हैं कि RTTI (Run Time Type Identification) हमें String Store करने की परेशानी से बचा लेता है। साथ ही हम ये भी जान सकते हैं कि Object किस Class का है। (Virtual Destructor – StackOverflow)

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