Pointers and const

Pointers and const: हमने पिछले अध्‍यायों में देखा है कि जब हमें Program में किसी Variable के मान को पूरे Program में स्थिर रखना होता है, तब हम उस Variable को const Declare करते हैं। जैसे-

const int var1 = 123;

यदि हम किसी भी तरीके से इस var1 के मान को Modify करना चाहें तो Compiler हमें ऐसा नहीं करने देता है और Error Massage दे देता है। लेकिन जब हम Pointer के सम्बंध में const Modifier को देखते हैं, तब हमें दो स्थितियों को ध्‍यान में रखना होता:

  1. पहली ये कि हम स्वयं Pointer को const रखना चाहते हैं और
  2. दूसरी ये कि हम उस Variable के मान को const रखना चाहते हैं, जिसको Pointer Point कर रहा है या
  3. हम दोनों को ही const रखना चाहते हैं।

इन तीनों सम्भावनाओं को समझने के लिए हम निम्नानुसार कुछ Code Segments को देखते हैं:

int a;                     	// int variable

const int* p = &a;         	// pointer to constant int
++p;                       	// ok
++(*p);                    	// error: can't modify a const a

int* const q = &a;         	// constant pointer to int
++q;                       	// error: can't modify a const q
++(*q);                    	// ok

const int* const r = &a;   	// constant pointer to constant int
++r;                       	// error: can't modify a const r
++(*r);                    	// error: can't modify a const a

यदि हम const Keyword को data type के पहले प्रयोग करते हैं, जैसाकि p के Definition में किया गया है, तो ये एक ऐसे Variable का Pointer होता है, जिसका मान Constant है। यदि const को Data Type के बाद में लिखा जाता है, जैसाकि q के Definition में किया गया है, तो ये किसी Variable का Constant Pointer होता है।

प्रथम स्थिति में हम उस Variable में Stored मान को Change नहीं कर सकते हैं, जिसे Pointer Point कर रहा है और दूसरी स्थिति में, हम Pointer में Stored Address को change नहीं कर सकते हैं। यदि हम दोनों स्थानों पर const का प्रयोग करते हैं, जैसाकि r के Definition में किया गया है, तो हम ना तो उस Variable के मान को बदल सकते हैं, जिसका Address Pointer में Stored है और ना ही हम Pointer में Store Address को ही Modify कर सकते हैं।

जहां भी जरूरी और सम्भव हो, हमें const का प्रयोग करना चाहिए। यदि हम चाहते हैं कि Pointer का मान जो कि किसी Variable का Address होता है, कभी Change ना हो तो Pointer को const करना चाहिए। जबकि यदि हम ये चाहते हैं कि Pointer जिस Variable को Point कर रहा है, हमेंशा उसी Variable को Point करे, तो हमें इसे “Pointer to const” करना चाहिए।

ये सभी बातें इस Code Segment में ज्यादा महत्वपूर्ण नहीं लग रही होंगी, क्योंकि Variable a स्वयं const नहीं है। जब हम इसके मान को Directly Change कर सकते हैं, तो हम इसे Pointer द्वारा क्यों Modify करें? लेकिन जब हमारे पास पहले से ही एक const Variable हो और हम उस Variable के लिए एक Pointer Define करना चाहते हों,  तब ये सभी बातें ज्यादा Practical या Realistic हो जाती हैं।

const int b = 99;    // const variable
int* r = &b;         // error: can't convert const to non-const
const int* s = &b;   // ok
int* const t = &b;   // error: can't convert const to non-const

 इस Code Segment में देखें तो जब हम ऐसा Statement लिखते हैं जो const Variable को Change करता है, तो Compiler हमें const Variable का मान Change नहीं करने देता है चाहे हम Pointer के द्वारा ही Variable का मान Change करने की कोशिश कर रहे हों। हम किसी const Variable का Address किसी Pointer को तब तक Assign नहीं कर सकते हैं जब तक कि Pointer को const Define नहीं किया गया हो।

उपरोक्त Code Segment से समझें तो हम const int b का Address r को Assign नहीं कर सकते हैं, क्योंकि r const नहीं है जबकि s को कर सकते हैं, क्योंकि s const Pointer है। ऐसा करने से हम किसी const Variable के मान को Change करने के लिए निम्न Statement का प्रयोग नहीं कर सकते हैं:

++(*s);                         //Error: Can’t Modify a const

const का प्रयोग एक स्थान पर काफी महत्वपूर्ण तब होता है जब हम किसी Function में Pointer Pass करते हैं। जब हम किसी Function को Call करते हैं और उसमें Argument को Pointer द्वारा Pass करते हैं, तब हमें हमेंशा इस बात के लिए सावधान रहना पडता है, कि Function उस Argument के Actual मान को Modify ना करे।  इस स्थिति में हमें Pointer Argument को Function में const रूप में भेजना होता है। यानी Pointer जिस किसी भी मान को Point कर रहा है, उस मान को Function द्वारा Modify ना किया जा सके, इस Requirement को पूरा करने के लिए Pointer Argument को Function में const करके भेजना चाहिए। जैसे:

void func(const int* p)
{
   ++p;      		// OK
   ++(*p);   		// Error: Can't modify a constant int
}

जब हम उपरोक्त Code Segment के अनुसार Function में Pointer Argument Pass करते हैं, तब ये निश्चित हो जाता है कि ये Function उस Variable का मान Change नहीं कर सकता है, जिसे Argument Pointer Point कर रहा है। यदि हम निम्न तरीका Use करें तो ये पूरी तरह से निश्चित हो जाता है, कि ना तो Pointer में स्थित Address change होगा और ना ही उस Address पर स्थित Variable का मान Change होगा। यानी-

void func(const int* const p)
{
   ++p;     		 // error: can't modify a constant pointer
   ++(*p);   		// error: can't modify a constant int
}

इस तरीके को Use करने पर किसी Function को Call करने पर होने वाले Unexpected Side Effects कम हो जाते हैं।

किसी Constant Variable का Address Return करते समय ये जरूरी होता है कि Return Type भी const हो। ऐसा static या external Variables के साथ ही होता है क्योंकि Local Variables तो Function के Return होते ही Destroy हो जाते हैं। यदि हम किसी const Variable का मान Function से Return करवा रहे हैं और const का प्रयोग Return Type के साथ नहीं करते हैं, तो compiler हमें निम्नानुसार एक Error Message देता है:

Error Massage error: can’t convert const to non-const

उदाहरण के लिए यदि हम एक external Constant j को निम्नानुसार Define करते हैं:

const int j = 77;

तो हम इसे Function से निम्नानुसार तरीके से Return नहीं करवा सकते हैं:

int* func()
{
   return &j;   	// Error: Can't convert const to non-const
}

ऐसा इसलिये होता है क्योंकि func() जो Pointer Return करता है, हम उसके द्वारा j का मान निम्नानुसार Statement द्वारा Change कर सकते हैं:

int* p = func();
*p = 21;

जबकि j Constant है इसलिए j का मान Change नहीं होना चाहिए। इसलिए हमें Compiler द्वारा एक Error Message प्राप्त होता है। चूंकि Function एक const का Pointer Retrun कर रहा है, इसलिए हमें Return Type को भी निम्नानुसार const Declare करना होगा:

const int* p = func()   // OK

अब हम इस Function से Return होने वाले j के Pointer द्वारा j के मान को change नहीं कर सकते हैं। चूंकि Pointer p जिस Content को Point कर रहा है, उसे हम Change नहीं कर सकते हैं, इसलिए j का मान Function में Modify होने से सुरिक्षत रहता है। const के बारे में यहां हमने जो Discussion किया है, वह काफी Complicated लग सकता है, लेकिन const का प्रयोग करने का कारण काफी Simple है। ये Constant Variables को Modify होने से बचाता है।

इस तरीके का फायदा ये है कि Compiler Programmer द्वारा की जाने वाली सामान्‍य Mistakes को पकडता रहता है, इसलिए यदि कोई Error Generate होता है, तो हमें केवल ये Check करना होता है कि कौनसा Statement किसी const Variable को Modify कर रहा है। हालांकि ये जरूरी नहीं है कि const का प्रयोग करना ही पडे। लेकिन Error Free और सुरिक्षत Program बनाने के लिए const का आवश्‍यकतानुसार प्रयोग करना चाहिए।

हमने xString Class का जो पिछला उदाहरण देखा है उसमें कुछ समस्या है। उदाहरण के लिए उसमें = Operator की Overloading का पूरा फायदा प्राप्त नहीं हो रहा है, ताकि Class User उस Class के एक Object को निम्न Statement द्वारा दूसरे Object में Assign कर सके&

string2 = string1 ;

हम जो अगला उदाहरण देख रहे हैं उसमें = Operator को Overload किया गया है। इससे पहले कि हम इस Overloaded Operator की Coding करें, हमें ये तय करना होगा कि हम char* प्रकार की Actual String को किस प्रकार से Handle करेंगे, जो कि xString Class का मुख्‍य Data है। एक तरीका ये हो सकता है कि हर xString Object में उसका स्वयं का char* String हो।

यदि हम एक xString Object की String को दूसरे Object को Assign करते हैं तो हमें केवल Source String को Destination String पर Copy करना होगा। लेकिन ऐसा करने पर एक ही प्रकार की String दो जगहों पर Store हो जाएगी, जो कि एक Efficient तरीका नहीं कहा जा सकताए विशेषकर के तब जब String काफी बडी हो।

हर xString Object की स्वयं की String हो, इसके बजाय हम हर xString Object में Data Member के रूप में String का Pointer रख सकते हैं। अब यदि हम एक xString Object की String दूसरे xString Object को Assign करते हैं, तो दोनों Object के Pointers समान Sting को Point करेंगे।

इस तरीके को Use करने पर हम Memory को Waste होने से बचा सकते हैं। लेकिन यदि हम ये तरीका Use करते हैं, तो हमें उस समय सावधान रहना होगा जब xString Object Destroy होगा। यदि xString Object द्वारा Reserve की गई Memory को हम Destructor के प्रयोग द्वारा Free करते हैं, तो उस Sting को जितने भी अन्‍य Objects के Pointer Point कर रहे होंगे, वे सभी Objects उस String को Point करेंगे, जो कि Delete हो चुकी होगी।

इसलिए xString Objects में Pointers to String को Successfully Use करने के लिए हमें एक ऐसे तरीके की जरूरत है, जिससे ये पता चलता रहे कि किसी String को कितने Objects Point कर रहे हैं, ताकि हम String को तब तक Delete होने से बचा सकें जब तक कि उस String को Point करने वाला अन्तिम Object Delete नहीं हो जाता।

मानलो कि हमारे पास कई xString Objects हैं जो किसी एक String को Point कर रहे हैं और हम उन सभी Objects को Count करना चाहते हैं। तो हम इस Counting को कहां Store करें हमें ये भी सोचना होगा।

हर xString Object के में से कितने Object किसी एक ही String को Point कर रहे हैं, उस Count को ध्‍यान में रखना काफी Unmanageable काम है, इसलिए हम xString Class के एक Member Variable द्वारा इन Objects की Counting नहीं कर सकते हैं। इसके लिए हम एक Static Variable का प्रयोग कर सकते हैं।

हम समान String को Point करने वाले सभी Objects के Addresses व Counting को Store करने के लिए एक Pointer Array का प्रयोग कर सकते हैं। लेकिन इससे काम काफी बढ सकता है इसलिए हम एक और Class Create करके उसमें Counting को Store कर सकते हैं। इस दूसरी Class का हर Object जिसे हम strCount कहेंगे, में एक Counter व एक Pointer to String होगा और हर xString Object में जो Pointer है वह इस Class के किसी एक Appropriate strCount को Point करेगा। strCount Object char* String को Manage करता है। इसका Constructor String को Create करने के लिए new Operator का प्रयोग करता है और इसका Destructor उस Object को Delete करने का काम करता है।

xString Object को ये सुविधा प्रदान करने के लिए कि वह strCount Object के Count व String को Access कर सके, हमने strCount में छोटे-छोटे चार Member Function getstr(), getCount(), incCount() व decCount() Create किए हैं। xString का Modified Program निम्नानुसार है:

// memory-saving xString class
// the this pointer in overloaded assignment
#include <iostream.h>
#include <string.h>                 	// for strcpy(), etc
#include <conio.h>

class strCount                      	// keep track of number
{                                   		// of unique strings
	private:
		int count;                    	// number of xStrings
		char* str;                    	// pointer to string

	public:
		strCount(const char* const s) 	// one-arg constructor
		{
			int length = strlen(s);    	// length of string argument
			str = new char[length+1];  	// get memory for string
			strcpy(str, s);            	// copy argument to it
			count=1;                   	// start count at 1
		}

		~strCount()                   	// destructor
		{ delete[] str; }

		char* getstr()                	// get string
		{ return str; }

		int getCount()                	// get count
		{ return count; }

		void incCount()               	// increment count
		{ ++count; }

		void decCount()               	// decrement count
		{ --count; }
};

class xString                       	// xString class
{
	private:
		strCount* psc;                	// pointer to strCount

	public:
		xString()                     	// no-arg constructor
		{
			psc = new strCount("NULL");
		}

		xString(const char* const s)  	// 1-arg constructor
		{
			psc = new strCount(s);
		}

		xString(const xString& S)     	// copy constructor
		{
			cout << "\nCOPY CONSTRUCTOR";
			psc = S.psc;
			psc->incCount();
		}

		~xString()                   	// destructor
		{
			if(psc->getCount()==1)    	// if we are its last user,
				delete psc;            	// delete our strCount
			else                      	// otherwise,
				psc->decCount();       	// decrement its count
		}

		void display()               	// display the xString
		{
			cout << psc->getstr();               	// print string
			cout << " (Address=" << psc << ")";     	// print address
		}

		// assignment operator
		xString& operator = (const xString&  S)
		{
			cout << "\nASSIGNMENT";

			if(psc->getCount()==1)     	// if we are its last user,
				delete psc;             	// delete our strCount
			else                       	// otherwise,
				psc->decCount();        	// decrement its count

			psc = S.psc;               	// use argument's strCount
			psc->incCount();           	// increment count
			return *this;              	// return this object
		}
};

void main()
{                                 		// 1-arg constructor
	xString string1 = " Om Em Namha ";
	cout << "\nString1 ="; string1.display();// display String1
	cout << "\n";

	xString string2;                       	// define String2
	string2 = string1;                      // set equal to String1

	cout << "\nString2 ="; string2.display(); // display String2
	cout << "\n";

	xString string3(string1);               // initialize String3 to String1
	cout << "\nString3 ="; string3.display();    // display String3

	cout << "\n";
	getch();
}

इस Program में main() Function string1 को निम्न String Initialize करता है:

“On Em Namha”

फिर एक दूसरी string2 को string1 की String निम्नानुसार Assign की जाती है:

string2 = string1;

फिर string3 में String1 को निम्न Statement द्वारा Initialize किया जाता है:

xString string3(string1);

string2 = string1 Statement Overloaded Operator=() को Call करता है और string3(string1) Statement Copy Constructor को Call करता है। Program तीनों Strings व उनके Address को strCount Object के आधार पर Display करता है। इस Program का Output कुछ निम्नानुसार प्राप्त होता है:

	String1 = Om Em Namha  (Address=0x0085308c)

	ASSIGNMENT
	String2 = Om Em Namha  (Address=0x0085308c)

	COPY CONSTRUCTOR
	String3 = Om Em Namha  (Address=0x0085308c)

हम देख सकते हैं कि main() Program कितना Simple है। एक बार Classes को Create कर लेने के बाद उन Classes को Use करना काफी आसान हो जाता है।

जैसाकि हमने पहले बताया कि strCount Class में Data Member के रूप में Actual String का Pointer है और xString Class के कितने Objects Create हुए हैं जो इस String को Point कर रहे हैं, उनकी Counting है।

इस Class का एक Single Constructor Argument के रूप में एक Pointer to String लेता है और उस String को Store करने के लिए एक Memory Area Create करता है।  ये Argument की String को इस Memory Area में Copy करता है और Count का मान 1 Set कर देता है, क्योंकि xString का Create होने वाला केवल एक Object इस समय तक उस String को Point कर रहा है।

strCount Class का Destructor Object द्वारा Reserve की गई Memory को Free करता है। हमने इस Destructor में delete[] Bracket के साथ में लिखा है क्योंकि String एक 1-D Array होता है।

xString Class में तीन Constructors हैं। यदि एक नई String Create होती है, जैसाकि Zero और One Argument Constructor में हुआ है, तो इस String को Hold करने के लिए एक नया strCount Object Create होता है और psc Pointer उस Object को Point करने के लिए Set हो जाता है।

यदि एक Existing xString Object Copy होता है, जैसाकि Copy Constructor और Overloaded Assignment Operator में हुआ है, तो Pointer psc पुराने strCount Object को Point करने के लिए Set हो जाता है और इस Object का Counter Increment हो जाता है।

Overloaded Assignment Operator को उस पुराने strCount Object को भी Delete करना पडता है जिसे psc Point कर रहा होता है। यहां हमने delete के साथ Bracket का प्रयोग नहीं किया है, क्योंकि हम यहां केवल एक Single strCount Object को Delete कर रहे हैं।

निम्नानुसार Statement द्वारा कई xString Objects के लिए Assignment Operators की Chaining सम्भव हो सके,

string4 = string5 = string6 ;

इसके लिए हमने operator=() से एक Value Return किया है। हमने Value को this Pointer का प्रयोग करके By Reference Value Return किया हैं ताकि Extra Object Create ना हो। इस Program में हमने xString व strCount के बीच Communication प्रदान करने के लिए strCount Class के Member प्रयोग किया है। इस प्रकार का Communication प्राप्त करने के लिए हम xString Class को strCount Class का Friend बना सकते हैं। (Pointers and const – StackOverflow)

this and const
C++ Linked List Class

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