Search

1.7 — Forward declarations

add.cpp नाम के इस आसान से program पर एक नज़र डालिए:

आप सोचेंगे की ये program result के रूप में ये देगा:

The sum of 3 and 4 is: 7

दरअसल, ये compile भी नहीं होगा! Visual Studio 2005 Express इसे compile करते वक़्त निम्नलिखित error देगा:

add.cpp(6) : error C3861: 'add': identifier not found
add.cpp(10) : error C2365: 'add' : redefinition; previous definition was 'formerly unknown identifier'

इस program के compile न होने का कारण है की compiler किसी file को क्रमबद्ध रूप में पढता है । जब compiler main के line 6 में function call statement, add() तक पहुँचता है, इसे तब तक पता नहीं होता की आखिर add क्या है, क्यूंकि हमने line 10 के पहले add को define ही नही किया है! ये हमें मिले पहले error का जवाब है (“identifier not found”) ।

जब Visual Studio 2005 line 10 में add() के वास्तविक declaration पर पहुँचता है, ये समझता है की add को यहाँ redefine (दोबारा define) किया गया है । ये थोडा अजीब है, क्यूंकि add को कभी पहले define नही किया गया था । Visual Studio के नए versions दूसरा वाला error message नहीं देंगे ।

दुसरे error के बिलकुल बेकार होने के बावजूद भी, यहाँ एक चीज़ नोट करने लायक है की किसी एक error के चलते compiler आपको कई और error दे सकता है, जो अकसर बिना मतलब के होते हैं । अर्थात, यदि आप main error को सुधार लो, तो आपको बाकी errors की चिंता करने की ज़रूरत नहीं क्यूंकि ये main error के fix होते ही अपने आप fix हो जायेंगे ।

Rule: जब आप compiler errors को fix कर रहे हों, सबसे पहले आये error को सबसे पहले fix करे

इस problem को सुलझाने के लिए, हमे इस बात को समझना होगा की compiler अब तक add को नहीं पहचानता । तो compiler को add के बारे में कैसे बताया जाये ? इसके दो तरीके हैं:

Option 1: add() को main() से पहले define करें:

इस तरह, जब main() add() को call करेगा, compiler को पहले से पता होगा की add क्या है । क्यूंकि ऊपर दिया गया program बहुत ही छोटा है, functions को इस तरह आगे-पीछे करने का टिका काम कर जायेगा और हमारी समस्या सुलझ जाएगी । लेकिन किसी बड़े program में functions को , कौन सा function किस function को call कर रहा है (और किस क्रम में call कर रहा है), ये खोज कर श्रेणीबद्ध करना काफी मुश्किल हो जाता है ।

इसके अलावा, यहाँ एक और दिक्कत है । ये solution हमेशा कारगर साबित नहीं होगा । मान लीजिये आप एक program लिख रहे है जिनमे दो functions A और B मौजूद हैं । यदि function A function B को call करेगा, और function B भी function A को call करना चाहेगा, तो आप किसे पहले define करोगे, ताकि दोनों functions सही से काम करें । यदि आप A को पहले define करते हो, तो compiler B को नहीं समझ पायेगा । यदि आप B को पहले define करते हो, तो compiler A को नहीं पहचान पायेगा ।

Function prototypes और forward declaration

Option 2: ऊपर दिए गये compiler error को fix करने का दूसरा तरीका है forward declaration

Forward declaration हमे compiler को किसी identifier को define करने से पहले ही इसके program में होने की सुचना देने में मदद करता है ।

Functions में, हम forward declaration का प्रयोग कर function के body ({ और } के बीच का भाग) को लिखने से पहले ही compiler को इस function के program में होने की सुचना दे सकते हैं । इस तरह से, जब compiler function call statement को देखेगा, ये जाने बिना ही की function असल में करता क्या है, function को पहचान लेगा ।

किसी function का forward declaration लिखने के लिए, हम एक declaration statement का प्रयोग करते हैं जिसे function prototype कहा जाता है । Function prototype में इस function का return type, नाम और parameters लिखा जाता है, पर function body नहीं (curly braces के बीच का भाग) । और क्यूंकि function prototype एक statement है, ये एक semicolon के साथ ख़त्म होता है ।

यहाँ add() function के लिए एक function prototype लिखा गया है:

अब जो program कुछ देर पहले compile नहीं हुआ था, आइये देखे की add() का एक prototype देने के बाद इसमें क्या बदलाव आता है:

अब जब compiler main() में add() को call करने वाले statement तक पहुचेगा, इसे पता होगा की add() किस तरह से define किया गया है (एक function जो दो integer parameters लेता है और एक integer return करता है), और इसलिए ये कोई error नही देगा ।

यहाँ एक और बात ध्यान देने लायक है की function prototypes में parameters के नाम लिखना ज़रूरी नहीं है । ऊपर दिए गये code को कुछ इस तरह से भी लिखा जा सकता है:

फिर भी, हमारे अनुसार parameters को नाम देना ज्यादा बेहतर है क्यूंकि इससे आपको केवल prototype को देखकर ही पता चल जायेगा की parameters को कैसा नाम दिया गया है । अन्यथा इसके लिए आपको function के वास्तविक definition को देखना होगा ।

Tip: आप function definition से copy-paste कर आसानी से function prototype लिख सकते हैं । पर अंत में एक semicolon देना मत भूलना

Function body को भूल जाने पर क्या होगा

बहुत से नए programmers पूछते है की: क्या होगा यदि हम किसी function को forward declare तो कर दे पर इसे program में कही भी define न करें ।

इसका जवाब है: ये निर्भर करता है । यदि एक forward declaration लिखा गया है, पर function कभी call नहीं किया गया तो program बिना कोई परेशानी के compile और run करेगा । लेकिन यदि forward declaration लिखा गया, forward declared function को call किया गया पर इसे program में कही भी define नहीं किया गया है, तो program compile तो हो जायेगा, लेकिन linker complain करेगा की वो function call को नहीं समझ पा रहा है ।

इस program पर एक नज़र डाले:

इस program में, हमने add() को forward declare किया है और इसे call भी किया है, लेकिन add() को कही भी define नहीं किया । जब हम इस program को compile करेंगे, Visual Studio 2005 Express ये message देगा:

Compiling...
add.cpp
Linking...
add.obj : error LNK2001: unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z)
add.exe : fatal error LNK1120: 1 unresolved externals

जैसा की आप देख सकते हैं, program आसानी से compile हो गया, लेकिन ये link नहीं हो पाया क्यूंकि int add(int, int) को कभी भी define नहीं किया गया है ।

Forward declarations के कुछ और तरीके

Forward declarations का सबसे ज्यादा प्रयोग functions के साथ होता है । फिर भी, forward declarations C++ के दुसरे identifiers के साथ भी इस्तेमाल किये जा सकते हैं, जैसे variables और user-defined types के साथ । Identifiers के दुसरे प्रकार (जैसे की user-defined types) forward declaration के लिए एक अलग syntax का इस्तेमाल करते हैं ।

आगे के एक lesson में हम देखेंगे की forward declarations का प्रयोग दुसरे identifiers के साथ कैसे किया जा सकता है ।

Declarations vs. definitions

C++ में, आप “declaration” और “definition” जैसे शब्दों का कई जगह इस्तेमाल होते हुए देखोगे । इनका मतलब क्या है ? अब आप इनके बीच के अंतर को पहचानने लायक knowledge इकठ्ठा कर चुके हो ।

Definition असल में identifier को implement (काम करने लायक बनाना) या instantiate (identifier के लिए memory allocate करना) करता है । नीचे definitions के कुछ उदाहरण दिए गये हैं:

आप एक identifier के लिए केवल एक definition लिख सकते हो । Definition linker को संतुष्ट करने के लिए ज़रूरी है ।

Declaration एक statement है जो एक identifier (variable या function के नाम) और इसके type को define करता है । Declarations के कुछ उदहारण नीचे दिए गये हैं:

Declaration compiler को संतुष्ट करने के लिए काफी है । इसलिए एक forward declaration भी compiler को खुश रखने में काफी है, जैसा की हमने कुछ ही देर पहले देखा । लेकिन यदि आप function के definition को लिखना भूल गये हो, तो linker आपको error देगा ।

आप note करोगे की “int x” दोनों ही categories में आता है । C++ में, सभी definitions एक तरह से declarations भी हैं । क्यूंकि “int x” एक definition है, by default ये एक declaration भी है और यही बात और कई declarations पर भी लागु होती है ।

फिर भी, यहाँ कुछ ऐसे declarations भी मौजूद हैं जो definition नही हैं, जैसे की function prototypes । इन्हें pure declarations भी कहा जाता है । Pure declarations के कुछ और उदाहरण variables के forward declarations, class declarations, और type declarations (आगे बढ़ते हुए आप इन सब के बारे में जानोगे, फ़िलहाल आपको इनकी चिंता करने की जरूरत नहीं) हैं । आप एक identifier के लिए जितना चाहो, pure declaration लिख सकते हो (पर ये बिलकुल बेकार साबित होंगे) ।

Quiz

1) एक function prototype और forward declaration के बीच क्या अंतर है?

2) इस function के लिए prototype लिखे:

इनमे से हर एक program के लिए, पता करें की क्या ये compile , link , या compile और link दोनों होने में असफल होता है । यदि आप sure नहीं हो, तो इन्हें compile कर के देखो!

3)

4)

5)

6)

Quiz Answers
1) Show Solution

2) Show Solution

3) Show Solution

4) Show Solution

5) Show Solution

6) Show Solution

1.8 -- Multiple files के साथ programs
Index
1.6 -- Whitespace और basic formatting

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter