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 को देखे:
|
1
2
3
4
5
6
7
|
#include <iostream>
int main()
{
using namespace std;
cout <<
"Hello, world!" << endl;
return 0;
}
|
ये 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:
|
1
2
3
4
|
int add(int x, int y)
{
return x + y;
}
|
main.cpp:
|
1
2
3
4
5
6
7
8
9
10
|
#include <iostream>
int add(int x, int y); // function prototype की सहायता से एक forward declaration
int main()
{
using namespace std;
cout <<
"The sum of 3 and 4 is " << add(3, 4) << endl;
return 0;
}
|
(यदि आप इस 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:
|
1
2
3
4
5
6
7
8
9
|
// यहाँ से header guard की शुरुआत होती है । ADD_H कोई भी अद्वितीय (unique) नाम हो सकता है । ज्यादातर, हम इसे header file के नाम के अनुसार ही लिखते हैं ।
#ifndef ADD_H
#define ADD_H
// यहाँ से .h file के प्रमुख भाग की शुरुआत होती है जहाँ हम सभी declarations
को रखने वाले हैं
int add(int x, int y);
// add.h का function
prototype -- semicolon लगाना मत
भूलियेगा!
// header guard यहाँ समाप्त हुआ
#endif
|
इस header file का इस्तेमाल main.cpp में करने के लिए, हमे इसे main.cpp में #include करना होगा ।
main.cpp जहाँ add.h को #include किया गया है:
|
1
2
3
4
5
6
7
8
9
|
#include <iostream>
#include "add.h" // हमारा header file
int main()
{
using namespace std;
cout <<
"The sum of 3 and 4 is " << add(3, 4) << endl;
return 0;
}
|
add.cpp में कोई बदलाव नहीं किया जायेगा:
|
1
2
3
4
|
int add(int x, int y)
{
return x + y;
}
|
जब 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 में लिख लेना, कुछ ऐसे:
|
1
2
|
#include "headers/myHeader.h"
#include
"../moreHeaders/myOtherHeader.h"
|
इसमें एक खामी ये है की इसे करते वक़्त आपको 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 का इस्तेमाल कर सकते हैं ।
|
1
|
g++ -o main -I /source/includes main.cpp
|
इस तरीके को अपनाने की एक खूबी है की यदि आप भविष्य में 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 ना करें

No comments:
Post a Comment