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)
ये Article इस वेबसाईट पर Selling हेतु उपलब्ध EBook C++ Programming Language in Hindi से लिया गया है। इसलिए यदि ये Article आपके लिए उपयोगी रहा, तो निश्चित रूप से ये पुस्तक भी आपके लिए काफी उपयोगी साबित होगी।
C++ Programming Language in Hindi | Page: 666 | Format: PDF