Search

1.9 — Header files

Headers, और उनका तात्पर्य

जैसे-जैसे program बड़ा होता जाता है (या इसमें और ज्यादा files जुड़ने लगते हैं), हर एक function जो किसी दुसरे file में स्थित हो, को forward declare करना बड़ी जल्दी ही थकाऊ काम बन जाता है । क्या ये एक बेहतर उपाय नहीं होगा की आप सभी forward declarations को एक ही जगह पर रख सके?

C++ में केवल C++ code files (.cpp extension वाली files) ही नहीं, बल्कि दुसरे files भी देखे जा सकते हैं । इन दुसरे तरह के files को header file या include file कहा जाता है । साधारणतः, header files में .h extension लगा होता है, लेकिन कभी-कभी ये .hpp, या बिना किसी extension के भी देखे जा सकते हैं । Header files का प्रयोग प्रायः declarations, जो किसी दुसरे files में प्रयोग किये जाने हैं, को एक जगह रखने के लिए किया जाता है ।

Standard library के header files का प्रयोग करना

इस program को देखे:

ये program cout की मदद से console पर “Hello, world!”print करेगा । फिर भी, ध्यान दीजिये की ये program cout को कही भी define नही करता, तो compiler को पता कैसे चला की cout आखिर है क्या? इसका जवाब है, cout को “iostream” नाम के किसी header file में declare किया गया है । जब हम #include line का प्रयोग करते हैं, असल में हम request कर रहे होते हैं कि “iostream” नाम के header file में स्थित सभी चीजें including file (जहाँ इस line का प्रयोग किया गया है) में copy हो जाये । इस तरह से header file की सारी चीजें (function, classes आदि) inclusion के बाद code file में भी इस्तेमाल की जा सकती हैं ।

ध्यान रखे की साधारणतः header files में केवल declarations ही रखे जाते हैं । उनमे ये नहीं define किया जाता की कोई चीज़ implement (कार्यान्वित) कैसे की गयी है । तो अब ये सवाल सामने आता है की यदि cout को “iostream” header file में केवल declare किया गया है, तो इसे define कहाँ किया गया होगा ? इसे C++ runtime support library में implement किया गया है, जो की program link होते वक़्त अपने आप program से जुड़ जाता है या link हो जाता है ।

अब ज़रा सोचिये की क्या होता यदि iostream नाम का कोई header file C++ में नहीं होता । आप जब भी std::cout का प्रयोग करना चाहते, आपको अपने हर program में सबसे ऊपर std::cout से related declarations को copy/paste करना होता ! और इसे करते वक़्त आपका ये जानना ज़रूरी हो जाता की आपके लिए क्या उपयोगी साबित होगा और क्या नहीं । iostream को #include करना इन सब चीजों से कहीं ज्यादा आसान है !

आपका अपना header files लिखना

चलिए अब उस example पर वापस जाते हैं जिसे हमने पिछले lesson में देखा था । इस example में हमारे पास दो files थें, add.cpp और main.cpp, जो कुछ इस तरह से define किये गए थे :

add.cpp:

main.cpp:

(यदि आप इस example को शुरुआत से लिखना चाहते हो, तो add.cpp को अपने project में जोड़ना ना भूले)

हमने forward declaration का प्रयोग किया था ताकि main.cpp को compile करते वक़्त compiler को ये पता चल सके की आखिर add क्या है । जैसा की पहले ही बता दिया गया है, किसी दूसरे code file में रहने वाले functions को हर file, जो उस function का इस्तेमाल करना चाहता हो, में forward declare करना जल्द ही थकाऊ बन सकता है ।

Header files हमे इस परेशानी से मुक्ति दिला सकते हैं । Header file को केवल एक बार लिखना होता है, और इसके बाद आप इसे जितने चाहो उतने files में include कर सकते हो । ये code के maintenance में भी काफी उपयोगी साबित हो सकता है क्योंकि header files का इस्तेमाल करने से, यदि कोई function prototype कभी बदलता भी है, तो code में बहुत सारे जगहों पर बदलाव करने की आवश्यक्ता को कम करता है ।

आपका अपना header file लिखना आश्चर्यजनक रूप से बहुत ही आसान है । Header files दो भागों से बने होते हैं ।

इनमे से एक को header guard कहा जाता है, जिसके बारे में हम आगे के lesson में चर्चा करेंगे ( preprocessor section में)। Header guards एक ही फ़ाइल में एक ही header को एक से ज्यादा बार #include होने से रोकता है ।

इसका दूसरा भाग असल में .h file का सबसे अहम हिस्सा है, जिसमे उन सारे functions के declarations लिखे होने चाहिए जिन्हें हम दूसरे code files में इस्तेमाल करना चाहते हैं । हमारे सारे header files में .h extension लगा होना चाहिए, इसलिए हम अपने नए header file को add.h नाम देंगे:

add.h:

इस header file का इस्तेमाल main.cpp में करने के लिए, हमे इसे main.cpp में #include करना होगा ।

main.cpp जहाँ add.h को #include किया गया है:

add.cpp में कोई बदलाव नहीं किया जायेगा:

जब compiler #include "add.h" line को compile करेगा, ये add.h के contents को मौजूदा file (जिसमे header file को include किया गया है) में copy कर लेगा । हमने add.h में add() function का prototype लिखा है, ये अब main में add के forward declaration के रूप में काम करेगा !

फलस्वरूप, हमारा program बिना किसी परेशानी के compile और link हो जायेगा ।

Note: जब आप किसी file को #include करते हैं, included file (जिसे include करना है) के contents including file(जिसमे include करना है) में inclusion की जगह (including file में जहाँ include किया जा रहा है) में copy कर लिया जाता है ।

यदि आपको add.h के ना मिलने का कोई compiler error मिलता है, तो एक बार देखकर पक्का कर ले की आपने header file का नाम add.h ही दिया है । आपने इसे कैसे बनाया है और क्या नाम दिया है, इसपर निर्भर करते हुए header file को “add” (बिना extension) या “add.h.txt” या “add.hpp” जैसा कोई भी नाम दिया जा सकता है ।

यदि आपको add() के define ना होने का कोई linker error मिलता है, तो पक्का कर ले की आपने अपने project में add.h को अच्छी तरह से #include किया है, ताकि ये आसानी से compile हो सके ।

Angled brackets और quotes

आप संभवतः ये जानने के लिए उत्सुक होंगे की iostream लिखते वक़्त angled bracket (< और >), और add.h लिखते वक़्त quotes (” और “) का प्रयोग क्यों किया गया है । इसका जवाब है, हम angled bracket का प्रयोग compiler को ये बताने के लिए करते हैं, की हम किसी ऐसे file को include करना चाहते हैं जो compiler के साथ पहले से ही जुड़ा हुआ है । ऐसा करने से compiler उस header file को हमारे code file में include करने के liye system directories में ढूंढेगा । Double quotes compiler को ये बताते हैं की ये header file हमने खुद से बनाया है, इसलिए compiler इसे सबसे पहले उस जगह ढूंढेगा जहाँ हमने अपने project के source files को store किया है । यदि compiler को header फ़ाइल वहाँ नहीं मिला, तो ये इसे उन directories में खोजेगा जिन्हें आपने project बनाने के दौरान compiler/IDE की settings का हिस्सा होने के रूप में specify किया था । यदि यहाँ भी header file नहीं मिला, तो इसे फिर system directories में खोजा जायेगा ।

Rule: Compiler के साथ आने वाले header files को include करने के लिए angled brackets का उपयोग करें । अन्य किसी भी header files के लिए quotes का उपयोग करें ।

iostream में .h extension क्यूँ नही होता ?

ये सवाल भी अकसर पूछा जाता है की ” iostream या कोई दूसरा header file जो standard library का हिस्सा है, में .h extension क्यूँ नहीं लगाया जाता ?” जवाब है, क्यूँकि iostream.h और iostream दो अलग-अलग header files हैं ! इसे अच्छे से समझने के लिए थोडा इतिहास जानना होगा 🙂

जब C++ को बनाया गया था, उस वक़्त standard runtime library के सारे header files एक .h extension के साथ ख़त्म होते थे । इसका किसी तरह का कोई विरोध नहीं हुआ, सबकुछ बिलकुल ठीक था । cout और cin का original version iostream.h में ही रखा गया । जब language को ANSI committee ने standardize करने का फैसला लिया, तो उन्होंने निर्णय किया की runtime library के सारे functions को std namespace में डाल दिया जाये (जो एक अच्छी तरकीब थी) । फिर भी, इसमें एक दिक्कत थी : यदि सारे functions को std namespace में डाल दिया जाता, तो पुराने सारे programs काम नहीं कर पाते !

इस परेशानी से पार पाने और पुराने programs के लिए भी C++ को compiltable बनाये रखने के लिए, header files का एक बिलकुल नया set define किया गया जिनमें नामों को बिलकुल iostream.h के नामों की तरह ही रखा गया, लेकिन बिना किसी .h extension के । इन सभी नए header files की functionalities को std namespace के अंदर रखा गया । इस तरह, पुराने programs जिनमे #include लिखा गया था, उन्हें दोबारा नए नियमों के अनुसार लिखने की ज़रूरत नहीं पड़ी ।

जब आप standard library के किसी header file को include करते हैं, तो ध्यान रहे की आप header files का वो version इस्तेमाल कर रहे हो जिसमे .h extension नहीं लगाया गया है (यदि non .h version उपलब्ध हो तो) । नहीं तो आप header files का वो version इस्तेमाल कर बैठियेगा जिसका अब उपयोग करना ना तो सही माना जाता है, और ना ही modern compilers के द्वारा supported है ।

एक छोटे note के रूप में, standard library के कुछ header files ऐसे भी हैं जिनका non .h version उपलब्ध ही नहीं है । इन files के लिए .h version को include करना ठीक है । इनमे से कई libraries C programming के पुराने standard के रूप में implemented हैं, और C language namespaces को support नहीं करता । फलस्वरूप, इन libraries की functionalities std namespace के ज़रिये access नहीं की जा सकती । इसके अलावे, जब आप अपने header files लिखते हैं, आपको उन्हें एक .h extension ज़रूर देना चाहिए, क्यूँकि आप अपने code को std namespace में नहीं रखने वाले हो ।

Rule: यदि एक non .h version उपलब्ध हो, तो standard library के header files का इस्तेमाल करने के लिए इसी को include करें, साथ ही इनकी functionalities को std namespace के ज़रिये access करें । यदि file का non .h version उपलब्ध नही है , या फिर आपने अपने headers बनाये हैं, तो .h version का इस्तेमाल करें ।

Header files को दूसरी directories से include करना

और एक सवाल जो सबसे ज्यादा पूछा जाता है वो ये है, की दूसरी directories में से header files को कैसे include किया जाये ।

इसे करने का एक (बुरा) तरीका है, आप जिस header file को include करना चाहते हैं, उसके पूरे path को #include line में लिख लेना, कुछ ऐसे:

इसमें एक खामी ये है की इसे करते वक़्त आपको header file के पूरे directory structure को code में शामिल करना होगा । यदि इसके बाद आपने कभी भी directory structure को update किया, तो आपका code काम नही करेगा ।

इससे बेहतर और आसान तरीका ये होगा की compiler या IDE को ये बता दिया जाये की आपके पास किसी directory में कुछ header files मौजूद हैं, ताकि compiler या IDE header files को ढूंढने के लिए इन directories पर जा सके, यदि मौजूदा directory (जहाँ आपका project स्थित है), में कोई header file ना मिले तो । इसे करने के लिए हमे IDE में एक “include path” या “search directory” की setting करनी पड़ेगी ।

Visual Studio में, सबसे पहले Solution explorer में अपने project पर right click करें, और यहाँ से “Properties”, और इसके बाद “VC++ Directories” tab का चयन कर लें । यहाँ पे, आपको “Include Directories” नाम की एक line दिखाई देगी । यहाँ आप अपने include directories set कर लें ।

Code::Blocks में, Project menu में जाकर “Build Options” select कर ले, और यहाँ से “Search directories” tab को चुन लें । यहाँ आपके include directories add कर लें ।

g++ का इस्तेमाल करते हुए, आप अलग include directory जोड़ने के लिए -I option का इस्तेमाल कर सकते हैं ।

इस तरीके को अपनाने की एक खूबी है की यदि आप भविष्य में header file के directory structure को change भी करते हैं, तो आपको compiler या IDE की केवल एक setting को बदलने की ज़रूरत है, ना की हर code file की #include lines को ।

क्या हम function definitions को किसी header file में रख सकते हैं ?

यदि आप ऐसा करते हैं तो C++ कोई complain नहीं करेगा, लेकिन आम तौर पर आपको ऐसा नही करना चाहिए ।

जैसा की पहले बताया गया है, जब आप किसी file को #include करते हो, included file के सारे contents including file में उस जगह पे copy हो जाते हैं, जहाँ उन्हें #include किया जाता है । इसका मतलब ये है की आप header में जो भी definitions रखोगे, वो उस header फ़ाइल को include करने वाले हर एक file में copy हो जाएँगी ।

छोटे projects में ये कोई बड़ी समस्या पैदा नही कर सकती। लेकिन बड़े projects जहाँ functions की संख्या सैकड़ों में होती है, ये codes को compile होने में अत्यधिक समय की खपत होने का कारण बन सकती है, या फिर आपके executable के size को ज़रूरत से ज्यादा बड़ा बना सकती है। वो इसलिए क्योंकि ऐसा करने से एक ही code जो 100 से ज्यादा lines की भी हो सकती है, को हर बार including file के साथ compile होना होगा । यदि आप किसी साधारण code file में किसी function के definition को change करते हो, तो आपको केवल उस .cpp file में उपस्थित code को recompile करना पड़ेगा, लेकिन यदि आप header file में रखे किसी definition में बदलाव करते हो, तो उस header file को include करने वाले हर एक code file को recompile करना होगा । आपका header file में किया गया छोटा सा बदलाव आपके पूरे project को फिर से compile करने की नौबत ला सकता है ।

कुछ बहुत ज्यादा छोटे functions को header files में define करना सही है, बशर्ते वे छोटे ही हों (मुश्किल से एक या दो lines के) 🙂 ।

Header files की बेहतरीन practice

नीचे header files लिखने के कुछ महत्वपूर्ण tips दिए गए हैं :

  • Header files लिखते वक़्त उनमे header guards लिखना ना भूले ।
  • यदि variables constant नहीं हैं, तो उन्हें header files में define ना करें । जहाँ तक संभव हो, header files में केवल और केवल declarations को जगह दें । Header file में किसी तरह का definition रखना programming के नज़रिये से एक बुरी आदत है ।
  • किसी भी function को header files में define ना करें ।
  • हर एक header file कोई विशेष काम करने के लिए बना होना चाहिए, और जितना हो सके स्वतंत्र होना चाहिए । उदाहरण के लिए, आप A से जुड़ी सारी functionalities को किसी header file A.h, और B से जुड़ी functionalities को header file B.h में रख सकते हैं । ऐसा करने से, यदि आपको बाद में कभी केवल A की functionalities पर ध्यान देना है, तो आप केवल A.h को include कर सकते हैं, और बिना मतलब B.h में declare किये गए code files को compile करने से बच सकते हैं।
  • Header files को उनसे related source files के अनुसार ही नाम दिया जाना चाहिए । (जैसे की, grades.h का तालमेल grades.cpp से होना चाहिए) ।
  • जितना हो सके, अपने header files में कम से कम header files को #include करने की कोशिश करें । बस उतने ही files को #include करें, जितने की ज़रूरत आपके header file को होगी ।
  • .cpp files को कभी भी #include ना करें
1.10 -- Preprocessor और header guards पर एक नज़र
Index
1.8 -- Multiple files के साथ programs

Leave a Comment

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