# .हिंदीफ़ेले CLASS_H और #define CLASS_H का उपयोग .h फ़ाइल में क्यों किया जाता है लेकिन इनकैप नहीं?


136

मैंने हमेशा लोगों को लिखते देखा है

class.h

#ifndef CLASS_H
#define CLASS_H

//blah blah blah

#endif

सवाल यह है कि वे .cpp फ़ाइल के लिए ऐसा क्यों नहीं करते हैं, जिसमें कक्षा के कार्यों की परिभाषाएँ होती हैं?

मान लीजिए कि मेरे पास है main.cpp, और main.cppइसमें शामिल हैं class.hclass.hफ़ाइल नहीं है includeकुछ भी है, तो कैसे करता है main.cppपता है कि में है class.cpp?


5
"इम्पोर्ट" शायद वह शब्द नहीं है जिसका आप यहाँ उपयोग करना चाहते हैं। शामिल।
केट ग्रेगोरी

5
C ++ में, फ़ाइलों और कक्षाओं के बीच कोई 1-टू -1 सहसंबंध नहीं है। आप जितनी चाहें उतने वर्गों को एक फ़ाइल में रख सकते हैं (या एक वर्ग को कई फ़ाइलों में विभाजित कर सकते हैं, हालाँकि यह शायद ही कभी मददगार है)। इसलिए मैक्रो होना चाहिए FILE_H, नहीं CLASS_H
sbi

जवाबों:


302

सबसे पहले, अपनी पहली जांच को संबोधित करने के लिए:

जब आप इसे .h फ़ाइल में देखते हैं :

#ifndef FILE_H
#define FILE_H

/* ... Declarations etc here ... */

#endif

हेडर फ़ाइल को कई बार शामिल करने से रोकने की यह एक पूर्वप्रक्रमक तकनीक है, जो विभिन्न कारणों से समस्याग्रस्त हो सकती है। आपकी परियोजना के संकलन के दौरान, प्रत्येक .cpp फ़ाइल (आमतौर पर) संकलित की जाती है। सरल शब्दों में, इसका मतलब यह है कि कंपाइलर आपकी .cpp फ़ाइल को ले जाएगा , #includedइसके द्वारा कोई भी फाइल खोल देगा, उन सभी को एक बड़े पैमाने पर टेक्स्ट फाइल में बदल देगा, और फिर सिंटैक्स विश्लेषण करेगा और अंत में इसे कुछ इंटरमीडिएट कोड में बदल देगा, अनुकूलन / अन्य प्रदर्शन करेगा कार्य, और अंत में लक्ष्य वास्तुकला के लिए विधानसभा उत्पादन उत्पन्न करते हैं। इसके कारण, यदि कोई फ़ाइल #includedएक .cpp के अंतर्गत कई बार हैफ़ाइल, संकलक अपनी फ़ाइल सामग्री को दो बार जोड़ देगा, इसलिए यदि उस फ़ाइल के भीतर परिभाषाएँ हैं, तो आपको एक कंपाइलर त्रुटि मिलेगी जो आपको बता रही है कि आपने एक चर को फिर से परिभाषित किया है। जब फ़ाइल को संकलन प्रक्रिया में प्रीप्रोसेसर कदम से संसाधित किया जाता है, तो पहली बार इसकी सामग्री पहले दो पंक्तियों तक पहुंच जाती है यदि FILE_Hप्रीप्रोसेसर के लिए परिभाषित किया गया है तो जांच करेगा । यदि नहीं, तो यह परिभाषित करेगा FILE_Hऔर इसके और #endifनिर्देश के बीच कोड को संसाधित करना जारी रखेगा । अगली बार जब फ़ाइल की सामग्री प्रीप्रोसेसर द्वारा देखी जाती है FILE_H, तो उसके खिलाफ जांच झूठी होगी, इसलिए यह तुरंत स्कैन करेगा#endif और उसके बाद जारी रखेगा। यह पुनर्परिभाषित त्रुटियों को रोकता है।

और अपनी दूसरी चिंता को दूर करने के लिए:

एक सामान्य अभ्यास के रूप में C ++ प्रोग्रामिंग में हम विकास को दो फ़ाइल प्रकारों में अलग करते हैं। एक .h के विस्तार के साथ है और हम इसे "हेडर फ़ाइल" कहते हैं। वे आम तौर पर कार्यों, वर्गों, संरचनाओं, वैश्विक चर, टाइपडेफ़्स, प्रीप्रोसेसिंग मैक्रोज़ और परिभाषाओं आदि की एक घोषणा प्रदान करते हैं। मूल रूप से, वे सिर्फ आपको आपके कोड के बारे में जानकारी प्रदान करते हैं। फिर हमारे पास .cpp एक्सटेंशन है जिसे हम "कोड फ़ाइल" कहते हैं। यह उन कार्यों, वर्ग के सदस्यों, किसी भी संरचना के सदस्यों के लिए परिभाषा प्रदान करेगा, जिन्हें परिभाषा, वैश्विक चर आदि की आवश्यकता होती है। इसलिए .h फ़ाइल कोड घोषित करती है, और .cpp फ़ाइल घोषणाओं को लागू करती है। इस कारण से, हम आम तौर पर संकलन के दौरान प्रत्येक .cpp का संकलन करते हैंकिसी ऑब्जेक्ट में फ़ाइल करें और फिर उन ऑब्जेक्ट्स को लिंक करें (क्योंकि आप लगभग कभी भी एक .cpp फ़ाइल को दूसरी .cpp फ़ाइल में शामिल नहीं करते हैं )।

इन एक्सटर्नल को कैसे हल किया जाता है यह लिंकर के लिए एक काम है। अपने संकलक प्रक्रियाओं जब main.cpp , यह घोषणाओं कोड के लिए में हो जाता है class.cpp शामिल करके class.h । यह केवल यह जानना चाहिए कि ये फ़ंक्शन या चर क्या दिखते हैं (जो कि एक घोषणा आपको देती है)। तो यह आपकी main.cpp फाइल को किसी ऑब्जेक्ट फाइल में संकलित करता है (इसे main.obj कहते हैं )। इसी तरह, class.cpp को एक class.obj में संकलित किया जाता हैफ़ाइल। अंतिम निष्पादन योग्य का उत्पादन करने के लिए, एक लिंकर को उन दो ऑब्जेक्ट फ़ाइलों को एक साथ जोड़ने के लिए आमंत्रित किया जाता है। किसी भी बाहरी बाहरी चर या फ़ंक्शंस के लिए, कंपाइलर एक स्टब रखेगा जहां पहुंच होती है। लिंकर फिर इस स्टब को ले जाएगा और किसी अन्य सूचीबद्ध ऑब्जेक्ट फ़ाइल में कोड या चर की तलाश करेगा, और यदि यह पाया जाता है, तो यह दो ऑब्जेक्ट फ़ाइलों से कोड को आउटपुट फ़ाइल में जोड़ता है और स्टब को फ़ंक्शन के अंतिम स्थान के साथ बदल देता है या चर। इस तरह, main.cpp में आपका कोड फ़ंक्शंस को कॉल कर सकता है और class.cpp में वैरिएबल का उपयोग कर सकता है। IF और ONLY ifY THE DECLARED IN class.h

मुझे आशा है कि यह मददगार था।


मैं पिछले कुछ दिनों से .h और .cpp को समझने की कोशिश कर रहा था। इस उत्तर ने C ++ सीखने के लिए मेरा समय और ब्याज बचाया। अच्छा लिखा। धन्यवाद जस्टिन!
राजकुमार आर।

तुम सच में महान समझाया! शायद इसका उत्तर काफी अच्छा होगा अगर यह छवियों के साथ था
अलामिन

13

CLASS_Hएक है गार्ड शामिल हैं ; यह एक ही हैडर फ़ाइल को एक ही CPP फ़ाइल के भीतर कई बार (विभिन्न मार्गों के माध्यम से) शामिल करने से बचने के लिए उपयोग किया जाता है (या, अधिक सटीक रूप से, एक ही अनुवाद इकाई ), जो कई-परिभाषा त्रुटियों को जन्म देगा।

CPP फ़ाइलों पर गार्ड शामिल करने की आवश्यकता नहीं है क्योंकि, परिभाषा के अनुसार, CPP फ़ाइल की सामग्री केवल एक बार पढ़ी जाती है।

आपको लगता है कि importअन्य भाषाओं (जैसे जावा) में बयान के रूप में एक ही कार्य करने वाले गार्ड को शामिल किया गया है ; हालांकि ऐसा नहीं है। यह अन्य भाषाओं के #includeबराबर है import


2
"एक ही सीपीपी फ़ाइल के भीतर" को "एक ही अनुवाद इकाई के भीतर" पढ़ना चाहिए।
ड्रीमलैक्स

@dreamlax: अच्छी बात है - यही कि मैं मूल रूप से लिखने जा रहा था, लेकिन फिर मुझे लगा कि किसी को जो समझ में नहीं आता है, वह केवल "अनुवाद इकाई" शब्द से भ्रमित होने वाला है। मैं कोष्ठक में "अनुवाद इकाई" जोड़ने के उत्तर को संपादित करूँगा - जो कि दोनों दुनिया में सबसे अच्छा होना चाहिए।
मार्टिन बी

6

यह संकलन चरण के दौरान कम से कम नहीं है।

स्रोत कोड से मशीन कोड तक c ++ प्रोग्राम का अनुवाद तीन चरणों में किया जाता है:

  1. Preprocessing - Preprocessor सभी सोर्स कोड को # से शुरू होने वाली लाइनों के लिए पार्स करता है और निर्देशों को निष्पादित करता है। आपके मामले में, class.hलाइन के स्थान पर आपकी फ़ाइल की सामग्री डाली गई है #include "class.h। चूँकि आप अपनी हेडर फ़ाइल को कई स्थानों पर शामिल कर सकते हैं, #ifndefक्लॉज़ डुप्लिकेट डिक्लेरेशन-एरर्स से बचते हैं, क्योंकि प्रीप्रोसेसर निर्देश अपरिभाषित होता है, जब पहली बार हेडर फ़ाइल शामिल होती है।
  2. संकलन - संकलनकर्ता अब सभी प्रीप्रोसेस्ड स्रोत कोड फ़ाइलों को बाइनरी ऑब्जेक्ट फ़ाइलों में अनुवाद करता है।
  3. लिंकिंग - लिंकर लिंक (इसलिए नाम) ऑब्जेक्ट फ़ाइलों को एक साथ करता है। आपकी कक्षा या उसके तरीकों में से एक का संदर्भ (जिसे class.h में घोषित किया जाना चाहिए और class.cpp में परिभाषित किया गया है) ऑब्जेक्ट फ़ाइलों में से एक में संबंधित ऑफसेट के लिए हल हो गया है। मैं write आपकी एक ऑब्जेक्ट फाइल ’लिखता हूं क्योंकि आपकी क्लास को class.cpp नामक फाइल में परिभाषित करने की आवश्यकता नहीं है , यह एक पुस्तकालय में हो सकता है जो आपकी परियोजना से जुड़ा हुआ है।

सारांश में, घोषणाओं को हेडर फ़ाइल के माध्यम से साझा किया जा सकता है, जबकि परिभाषाओं की घोषणा की मैपिंग लिंकर द्वारा की जाती है।


4

यह घोषणा और परिभाषा के बीच का अंतर है। हेडर फ़ाइलों में आमतौर पर सिर्फ घोषणा शामिल होती है, और स्रोत फ़ाइल में परिभाषा होती है।

कुछ का उपयोग करने के लिए आपको केवल यह जानना होगा कि यह घोषणा नहीं है, यह परिभाषा है। केवल लिंकर को परिभाषा जानने की जरूरत है।

इसलिए यह है कि आप एक या अधिक स्रोत फ़ाइलों के अंदर एक हेडर फ़ाइल शामिल करेंगे, लेकिन आप दूसरे के अंदर एक स्रोत फ़ाइल शामिल नहीं करेंगे।

इसके अलावा आप का मतलब है #includeऔर आयात नहीं है।


3

यह शीर्ष लेख फ़ाइलों के लिए किया जाता है ताकि सामग्री केवल प्रत्येक प्रीप्रोसेस्ड स्रोत फ़ाइल में एक बार दिखाई दे, भले ही वह एक से अधिक बार शामिल हो (आमतौर पर क्योंकि यह अन्य हेडर फ़ाइलों से शामिल है)। पहली बार इसे शामिल किया गया है, प्रतीक CLASS_H(एक शामिल गार्ड के रूप में जाना जाता है ) को अभी तक परिभाषित नहीं किया गया है, इसलिए फ़ाइल की सभी सामग्री शामिल हैं। ऐसा करना प्रतीक को परिभाषित करता है, इसलिए यदि इसे फिर से शामिल किया गया है, तो फ़ाइल की सामग्री ( #ifndef/ के अंदर )#endif ब्लॉक के ) छोड़ दी जाती है।

स्रोत फ़ाइल के लिए ऐसा करने की कोई आवश्यकता नहीं है क्योंकि (सामान्य रूप से) किसी भी अन्य फ़ाइल द्वारा शामिल नहीं है।

आपके अंतिम प्रश्न के लिए, class.hवर्ग की परिभाषा होनी चाहिए, और इसके सभी सदस्यों की घोषणाओं, संबद्ध कार्यों, और जो कुछ भी हो, ताकि किसी भी फ़ाइल में जो इसमें शामिल है उसमें कक्षा का उपयोग करने के लिए पर्याप्त जानकारी हो। कार्यों के कार्यान्वयन एक अलग स्रोत फ़ाइल में जा सकते हैं; आपको केवल उन्हें कॉल करने के लिए घोषणाओं की आवश्यकता है।


2

main.cpp को पता है कि में है नहीं है class.cpp । यह केवल उन कार्यों / वर्गों की घोषणाओं को जानना है जो इसका उपयोग करने के लिए जाते हैं, और ये घोषणाएँ class.h में हैं

लिंकर उन स्थानों के बीच लिंक करता है, जहां class.h में घोषित फ़ंक्शन / कक्षाएं उपयोग की जाती हैं और class.cpp में उनके कार्यान्वयन हैं


1

.cppफ़ाइलें #includeअन्य फ़ाइलों में शामिल (उपयोग ) नहीं हैं । इसलिए उन्हें रखवाली की आवश्यकता नहीं है। Main.cppउस वर्ग के नाम और हस्ताक्षर को जान सकेंगे जिसे आपने class.cppकेवल इसलिए लागू किया है क्योंकि आपने वह सब निर्दिष्ट कर दिया है class.h- यह एक हेडर फ़ाइल का उद्देश्य है। (यह सुनिश्चित करना आपके ऊपर है कि class.hआप जिस कोड को लागू करते हैं उसका सही-सही वर्णन करते हैं class.cpp।) निष्पादन योग्य कोड लिंकर के प्रयासों की बदौलत class.cppनिष्पादन योग्य कोड को उपलब्ध कराया जाएगा main.cpp


1

आमतौर पर यह उम्मीद की जाती है कि कोड के मॉड्यूल जैसे कि .cpp तर्क के अनावश्यक दोहराव के संकलन से बचने के लिए, एक बार संकलित किए जाते हैं और कई परियोजनाओं में लिंक किए जाते हैं। उदाहरण के लिए, g++ -o class.cppउत्पादन होगा class.oजिसे आप कई परियोजनाओं से लिंक करके उपयोग कर सकते हैं g++ main.cpp class.o

हम #includeअपने लिंकर के रूप में उपयोग कर सकते हैं , जैसा कि आप आसन्न प्रतीत होते हैं, लेकिन यह सिर्फ मूर्खतापूर्ण होगा जब हम जानते हैं कि कम केस्ट्रोक्स और संकलन के कम अपशिष्ट पुनरावृत्ति के साथ हमारे कंपाइलर का उपयोग करके ठीक से लिंक कैसे करें, बजाय हमारे कोड के बजाय अधिक कीस्ट्रोक्स और अधिक बेकार। संकलन की पुनरावृत्ति ...

शीर्ष लेख फ़ाइलों को अभी भी कई परियोजनाओं में से प्रत्येक में शामिल किया जाना आवश्यक है, हालांकि, क्योंकि यह प्रत्येक मॉड्यूल के लिए इंटरफ़ेस प्रदान करता है। इन हेडर के बिना कंपाइलर द्वारा शुरू किए गए किसी भी प्रतीक के बारे में नहीं जानते होंगे.o फाइलों ।

यह महसूस करना महत्वपूर्ण है कि हेडर फाइलें वे हैं जो उन मॉड्यूल के लिए प्रतीकों की परिभाषा पेश करती हैं; एक बार जब यह महसूस किया जाता है तो यह समझ में आता है कि कई समावेशन प्रतीकों के पुनर्परिवर्तन का कारण बन सकते हैं (जो त्रुटियों का कारण बनता है), इसलिए हम ऐसे पुनर्निर्देशनों को रोकने के लिए गार्डों का उपयोग करते हैं।


0

हैडरफाइल्स की वजह से यह परिभाषित होता है कि कक्षा में क्या है (सदस्य, डेटा-संरचनाएं) और सीपीपी फाइलें इसे लागू करती हैं।

और हां, इसका मुख्य कारण यह है कि आप एक .h फ़ाइल को कई बार अन्य .h फ़ाइलों में शामिल कर सकते हैं, लेकिन इसके परिणामस्वरूप एक वर्ग की कई परिभाषाएँ होंगी, जो अमान्य है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.