एक .h फ़ाइल में क्या जाना चाहिए?


93

अपने कोड को कई फ़ाइलों में विभाजित करते समय बस एक .h फ़ाइल में क्या जाना चाहिए और एक .cpp फ़ाइल में क्या जाना चाहिए?


संबंधित प्रश्न: stackoverflow.com/questions/333889/…
Spoike

7
यह एक शुद्ध शैली का मुद्दा है, लेकिन मेरा मानना ​​है कि C ++ घोषणाएँ एक .hppफ़ाइल में जाती हैं जबकि C घोषणाएँ .hफ़ाइल में जाती हैं। C और C ++ कोड को मिलाते समय यह बहुत मददगार है (उदाहरण के लिए C में लीगेसी मॉड्यूल)।
थॉमस मैथ्यूज

@ThomasMatthews समझ में आता है। क्या उस अभ्यास का उपयोग किया जाता है?
Ty

@ लाइटनिंगलीफ: हां, अभ्यास का उपयोग विशेष रूप से सी + + और सी भाषाओं को मिलाकर करते समय किया जाता है।
थॉमस मैथ्यूज

जवाबों:


113

हैडर फाइलें ( .h) कई फाइलों में आवश्यक जानकारी प्रदान करने के लिए डिज़ाइन की गई हैं। क्लास डिक्लेरेशन, फंक्शन प्रोटोटाइप और एनुमरेशन जैसी चीजें आमतौर पर हेडर फाइलों में जाती हैं। एक शब्द में, "परिभाषाएं"।

कोड फ़ाइलों ( .cpp) को कार्यान्वयन जानकारी प्रदान करने के लिए डिज़ाइन किया गया है जिसे केवल एक फ़ाइल में जानने की आवश्यकता है। सामान्य तौर पर, फ़ंक्शन बॉडीज़, और आंतरिक चर जिन्हें अन्य मॉड्यूल द्वारा कभी एक्सेस नहीं किया जाना चाहिए, वे हैं जो .cppफाइलों में हैं। एक शब्द में, "कार्यान्वयन"।

अपने आप से यह पूछने का सबसे सरल प्रश्न यह है कि "अगर मैं इसे बदलता हूं तो क्या मुझे चीजों को फिर से संकलित करने के लिए अन्य फ़ाइलों में कोड बदलना होगा?" यदि उत्तर "हाँ" है तो संभवतः यह हेडर फ़ाइल में है; यदि उत्तर "नहीं" है, तो संभवतः यह कोड फ़ाइल में है।


4
निजी वर्ग के डेटा को छोड़कर हेडर में जाना होगा। टेम्प्लेट को पूरी तरह से हेडर डिफाइन किया जाना चाहिए (जब तक कि आप समर्थन करने वाले कुछ कंपाइलरों में से एक का उपयोग न करें export)। # 1 के आसपास एकमात्र तरीका PIMPL है। # 2 संभव होगा यदि exportसमर्थन किया गया था और c ++ 0x और externटेम्पलेट्स का उपयोग करना संभव हो सकता है । IMO, c ++ में हेडर फाइलें अपनी उपयोगिता का बहुत कुछ खो देती हैं।
KitsuneYMG

23
सभी अच्छे हैं, लेकिन गलत शब्दावली के साथ। एक शब्द में, "घोषणाएं" - "परिभाषा" शब्द "कार्यान्वयन" का पर्याय है। हेडर में केवल घोषणात्मक कोड, इनलाइन कोड, मैक्रो परिभाषाएँ और टेम्प्लेट कोड होना चाहिए; यानी ऐसा कुछ भी नहीं जो कोड या डेटा को तत्काल बना दे।
क्लिफोर्ड

8
मुझे क्लिफोर्ड से सहमत होना होगा। आप शब्दों की घोषणा और परिभाषा का उपयोग शिथिल और कुछ हद तक परस्पर रूप से करते हैं। लेकिन सी ++ में उनके सटीक अर्थ हैं। उदाहरण: एक वर्ग घोषणा एक वर्ग का नाम प्रस्तुत करता है, लेकिन यह नहीं कहता कि इसमें क्या है। एक वर्ग परिभाषा सभी सदस्यों और मित्र कार्यों को सूचीबद्ध करती है। दोनों को समस्याओं के बिना हेडर फाइलों में डाला जा सकता है। जिसे आप "फ़ंक्शन प्रोटोटाइप" कहते हैं, एक फ़ंक्शन घोषणा है । लेकिन एक फंक्शन डेफिनिशन वह चीज़ होती है जिसमें फ़ंक्शन का कोड होता है और इसे एक cpp-file में रखा जाना चाहिए - जब तक कि यह इनलाइन या टेम्पलेट का हिस्सा न हो।
बिक्री

5
सी ++ में उनके सटीक अर्थ हैं, उनके पास अंग्रेजी में सटीक अर्थ नहीं हैं। मेरा उत्तर बाद में लिखा गया था।
अंबर

54

तथ्य यह है कि C ++ में, यह कुछ अधिक जटिल है कि C शीर्ष लेख / स्रोत संगठन।

कंपाइलर क्या देखता है?

कंपाइलर एक बड़े स्रोत (.cpp) फ़ाइल को अपने हेडर के साथ ठीक से शामिल देखता है। स्रोत फ़ाइल संकलन इकाई है जिसे एक ऑब्जेक्ट फ़ाइल में संकलित किया जाएगा।

तो, हेडर क्यों आवश्यक हैं?

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

इस मामले में, एक ही जानकारी की दो प्रतियां हैं। जो बुराई है ...

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

हेडर का उपयोग उन साझा विवरणों को रखने के लिए किया जाता है।

कई स्रोतों के बीच साझा करने की आवश्यकता की घोषणाओं को हेडर पर ले जाएं

और कुछ नहीं?

C ++ में, कुछ अन्य चीजें हैं जिन्हें हेडर में रखा जा सकता है क्योंकि, उन्हें भी साझा करने की आवश्यकता है:

  • इनलाइन कोड
  • टेम्पलेट्स
  • स्थिरांक (आमतौर पर जिन्हें आप स्विच के अंदर उपयोग करना चाहते हैं ...)

साझा किए जाने वाले शीर्षकों पर जाएं जो साझा किए जाने वाले कार्यान्वयन सहित साझा किए जाने की आवश्यकता है

क्या इसका मतलब यह है कि हेडर के अंदर स्रोत हो सकते हैं?

हाँ। वास्तव में, बहुत सी अलग-अलग चीजें हैं जो एक "हेडर" के अंदर हो सकती हैं (यानी स्रोतों के बीच साझा की जाती हैं)।

  • आगे की घोषणा
  • कार्य / संरचना / वर्ग / टेम्पलेट की घोषणा / परिभाषा
  • इनलाइन और टेम्प्लेटेड कोड का कार्यान्वयन

यह जटिल हो जाता है, और कुछ मामलों में (प्रतीकों के बीच परिपत्र निर्भरता), इसे एक हेडर में रखना असंभव है।

हेडर को तीन भागों में विभाजित किया जा सकता है

इसका मतलब है कि, एक चरम मामले में, आपके पास हो सकता है:

  • एक आगे की घोषणा हैडर
  • एक घोषणा / परिभाषा हेडर
  • एक कार्यान्वयन हेडर
  • एक कार्यान्वयन स्रोत

आइए कल्पना करें कि हमारे पास एक अस्थायी MyObject है। हम कर सकते थे:

// - - - - MyObject_forward.hpp - - - - 
// This header is included by the code which need to know MyObject
// does exist, but nothing more.
template<typename T>
class MyObject ;

// - - - - MyObject_declaration.hpp - - - - 
// This header is included by the code which need to know how
// MyObject is defined, but nothing more.
#include <MyObject_forward.hpp>

template<typename T>
class MyObject
{
   public :
      MyObject() ;
   // Etc.
} ;

void doSomething() ;

// - - - - MyObject_implementation.hpp - - - - 
// This header is included by the code which need to see
// the implementation of the methods/functions of MyObject,
// but nothing more.
#include <MyObject_declaration.hpp>

template<typename T>
MyObject<T>::MyObject()
{
   doSomething() ;
}

// etc.

// - - - - MyObject_source.cpp - - - - 
// This source will have implementation that does not need to
// be shared, which, for templated code, usually means nothing...
#include <MyObject_implementation.hpp>

void doSomething()
{
   // etc.
} ;

// etc.

वाह!

"वास्तविक जीवन" में, यह आमतौर पर कम जटिल होता है। अधिकांश कोड में केवल एक साधारण हेडर / स्रोत संगठन होगा, जिसके स्रोत में कुछ इनबिल्ट कोड होंगे।

लेकिन अन्य मामलों में (एक-दूसरे को जानने वाले टेम्पर्ड ऑब्जेक्ट्स), मुझे प्रत्येक ऑब्जेक्ट के लिए अलग-अलग घोषणा और कार्यान्वयन हेडर के लिए होना चाहिए, उन हेडर सहित एक खाली स्रोत के साथ बस कुछ संकलन त्रुटियों को देखने में मेरी मदद करने के लिए।

हेडर को अलग हेडर में तोड़ने का एक अन्य कारण संकलन को गति देना हो सकता है, प्रतीकों की मात्रा को सीमित करना आवश्यक है, और एक स्रोत के आवश्यक पुनर्संयोजन से बचने के लिए, जो केवल इनलाइन घोषणा के लिए परवाह करता है जब एक इनलाइन विधि कार्यान्वयन बदल गया।

निष्कर्ष

आपको अपने कोड संगठन को यथासंभव सरल और यथासंभव मॉड्यूलर बनाना चाहिए। जितना संभव हो उतना स्रोत फ़ाइल में रखें। केवल हेडर में उजागर करें जिसे साझा करने की आवश्यकता है।

लेकिन जिस दिन आपके पास टेम्प्लेटेड ऑब्जेक्ट्स के बीच परिपत्र निर्भरताएं होंगी, आश्चर्यचकित न हों कि आपका कोड संगठन कुछ और "दिलचस्प" हो गया है, जो सादे हेडर / स्रोत संगठन ...

^ _ ^


17

अन्य सभी उत्तरों के अलावा, मैं आपको बताऊंगा कि आप हेडर फ़ाइल में क्या नहीं करते हैं:
usingघोषणा (सबसे आम using namespace std;) को हेडर फ़ाइल में नहीं दिखाई देना चाहिए क्योंकि वे स्रोत फ़ाइल के नाम स्थान को प्रदूषित करते हैं जिसमें यह शामिल है ।


+1 एक कैवेट के साथ जो आप तब तक कर सकते हैं जब तक यह कुछ विवरण नामस्थान (या अनाम नाम स्थान) में हो। लेकिन हाँ, कभी भी usingहेडर में वैश्विक नाम स्थान में सामान लाने के लिए उपयोग न करें ।
KitsuneYMG 12

+1 यह उत्तर देने में बहुत आसान है। :) इसके अलावा, हेडर फ़ाइलों में अनाम नाम स्थान नहीं होना चाहिए ।
सेलिबिट्ज़

अनाम नाम स्थान रखने के लिए हेडर फ़ाइलों के लिए यह ठीक है, इसलिए जब तक आप समझते हैं कि इसका मतलब क्या है, अर्थात प्रत्येक अनुवाद इकाई में आपके द्वारा नामस्थान को परिभाषित करने वाले सामान की एक अलग प्रतिलिपि होगी। अनाम नामस्थान में इनलाइन फ़ंक्शन उन मामलों के लिए C ++ में अनुशंसित हैं, जहां आप उपयोग करेंगेstatic inline C99 में , क्योंकि जब आप टेम्प्लेट के साथ आंतरिक लिंकेज को जोड़ते हैं, तो उसके साथ क्या करना है। Anon नेमस्पेस आपको बाहरी लिंकेज को संरक्षित करते हुए "कार्य" को छिपाने देता है।
स्टीव जेसोप

स्टीव, आपने जो लिखा वह मुझे नहीं समझा। कृपया एक ठोस उदाहरण चुनें, जहाँ आपको लगता है कि anon नाम स्थान एक हेडर फ़ाइल में कुल समझ में आता है।
बिके

6

क्या कुछ नहीं में संकलन करता है (शून्य द्विआधारी पदचिह्न) हेडर फाइल में चला जाता है।

चर कुछ भी नहीं संकलित करते हैं, लेकिन प्रकार की घोषणाएं करते हैं (coz वे केवल वर्णन करते हैं कि चर कैसे व्यवहार करते हैं)।

फ़ंक्शंस नहीं करते, लेकिन इनलाइन फ़ंक्शंस करते हैं (या मैक्रोज़), क्योंकि वे केवल कोड का उत्पादन करते हैं जहां कहा जाता है।

टेम्पलेट्स कोड नहीं हैं, वे केवल कोड बनाने के लिए एक नुस्खा हैं। इसलिए वे भी h फ़ाइलों में जाते हैं।


1
"इनलाइन फ़ंक्शंस ... केवल कोड का उत्पादन करते हैं जहां कहा जाता है"। यह सच नहीं है। इनलाइन फ़ंक्शंस कॉल साइटों पर इनबिल्ट हो सकते हैं या नहीं भी हो सकते हैं, लेकिन भले ही वे इनलाइन हों, असली फ़ंक्शन बॉडी अभी भी मौजूद है जैसे कि यह एक इनलाइन फ़ंक्शन के लिए है। हेडर में इनलाइन फ़ंक्शंस करने के लिए यह ठीक है कारण यह है कि क्या वे कोड उत्पन्न करते हैं, इससे कोई लेना-देना नहीं है, क्योंकि इनलाइन फ़ंक्शंस एक परिभाषा नियम को ट्रिगर नहीं करते हैं, इसलिए गैर-इनलाइन फ़ंक्शंस के विपरीत दो अलग-अलग अनुवाद इकाइयों को एक साथ जोड़ने में कोई समस्या नहीं है जिसमें दोनों हेडर शामिल हैं।
स्टीव जेसोप

3

सामान्य तौर पर, आप हेडर फ़ाइल में घोषणाएँ और कार्यान्वयन (.cpp) फ़ाइल में परिभाषाएँ डालते हैं। इसका अपवाद टेम्प्लेट है, जहां परिभाषा को हेडर में भी जाना चाहिए।

यह सवाल और इसके समान वाले एसओ पर अक्सर पूछा गया है - देखें कि हेडर फाइलें और .cpp फाइलें C ++ में क्यों हैं? और C ++ हैडर फाइलें, उदाहरण के लिए कोड पृथक्करण


बेशक, आप हेडर फ़ाइलों में वर्ग परिभाषाएं भी डाल सकते हैं । उन्हें टेम्प्लेट भी नहीं करना है।
बिक्री

1

आपकी कक्षा और फ़ंक्शन घोषणाओं के साथ-साथ दस्तावेज़ीकरण और इनलाइन फ़ंक्शंस / विधियों की परिभाषाएँ (हालाँकि कुछ उन्हें अलग .inl फ़ाइलों में रखना पसंद करते हैं)।


1

मुख्य रूप से हेडर फाइल में क्लास कंकाल या डिक्लेरेशन होता है (बार-बार नहीं बदलता है)

और cpp फ़ाइल में वर्ग कार्यान्वयन (अक्सर परिवर्तन) होता है।


5
कृपया गैर-मानक शब्दावली का उपयोग करने से बचें। "वर्ग कंकाल" क्या है, "वर्ग कार्यान्वयन" क्या है? साथ ही, जिसे आप कक्षाओं के संदर्भ में घोषणा कहते हैं, उसमें संभवतः कक्षा की परिभाषाएं शामिल हैं।
16

0

हेडर फ़ाइल (.h) वर्गों, संरचनाओं और उसके तरीकों, प्रोटोटाइप आदि की घोषणाओं के लिए होनी चाहिए। उन वस्तुओं का कार्यान्वयन cpp में किया जाता है।

एच में

    class Foo {
    int j;

    Foo();
    Foo(int)
    void DoSomething();
}

0

मुझे देखने की उम्मीद है:

  • घोषणाओं
  • टिप्पणियाँ
  • परिभाषाएँ इनलाइन
  • टेम्पलेट्स

वास्तव में जवाब है कि क्या में डाल नहीं है:

  • निश्चितता (चीजों को कई गुना परिभाषित किया जा सकता है)
  • घोषणाओं / निर्देशों का उपयोग करते हुए (उन्हें आपके हेडर सहित किसी पर भी लागू किया जा सकता है, नामर्दी पैदा कर सकता है)

1
आप निश्चित रूप से श्रेणी परिभाषाओं को हेडर फ़ाइलों में भी डाल सकते हैं । एक कक्षा घोषणा अपने सदस्यों के बारे में कुछ नहीं कहती है।
बिक्री

0

हेडर कुछ परिभाषित करता है लेकिन कार्यान्वयन के बारे में कुछ नहीं बताता है। (इस "मेटाफ़र" में टेम्पलेट को छोड़कर)

उस कहा के साथ, आपको "परिभाषाओं" को उप-समूहों में विभाजित करने की आवश्यकता है, इस मामले में, दो प्रकार की परिभाषाएं हैं।

  • आप अपने strucutre के "लेआउट" को परिभाषित करते हैं, केवल यह बताते हैं कि आसपास के उपयोग समूहों द्वारा जितना आवश्यक है।
  • एक चर, कार्य और एक वर्ग की परिभाषाएँ।

अब, मैं पहले उपसमूह के बारे में बात कर रहा हूँ।

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

जैसा कि पिछले पोस्टरों ने कहा है और आपने निजी और सार्वजनिक उपयोग क्षेत्रों और उनके हेडर की घोषणा की है, इसमें निजी और सार्वजनिक चर भी शामिल हैं। अब, मैं यहाँ कोड के डिज़ाइन में नहीं जाना चाहता, लेकिन, आप अपने हेडर में जो कुछ भी डाल सकते हैं, उस पर विचार करना चाहते हैं, क्योंकि वह अंतिम उपयोगकर्ता और कार्यान्वयन के बीच का लेयर है।


0
  • हेडर फाइलें - अक्सर विकास के दौरान नहीं बदलनी चाहिए -> आपको सोचना चाहिए, और उन्हें एक बार (आदर्श मामले में) लिखना चाहिए
  • स्रोत फाइलें - कार्यान्वयन के दौरान परिवर्तन

यह एक अभ्यास है। कुछ छोटी परियोजनाओं के लिए, यह जाने का रास्ता हो सकता है। लेकिन आप अपने हस्ताक्षर बदलने या उन्हें हटाने के बजाय कार्यों और उनके प्रोटोटाइप (हेडर फ़ाइलों में) को हटाने की कोशिश कर सकते हैं। कम से कम जब तक प्रमुख संख्या नहीं बदलती। जब 1.9.2 को 2.0.0 बीटा से टकराया जाता है।
तमसुराजॉय

0

हैडर (.h)

  • मैक्रोज़ और इंटरफेस के लिए आवश्यक (यथासंभव कम)
  • कार्यों और वर्गों की घोषणा
  • इंटरफ़ेस का प्रलेखन
  • इनलाइन कार्यों / विधियों की घोषणा, यदि कोई हो
  • वैश्विक चर के लिए बाहरी (यदि कोई हो)

शरीर (.cpp)

  • बाकी मैक्रोज़ और शामिल हैं
  • मॉड्यूल के हेडर को शामिल करें
  • कार्यों और विधियों की परिभाषा
  • वैश्विक चर (यदि कोई हो)

अंगूठे के एक नियम के रूप में, आप मॉड्यूल के "साझा" भाग को .h पर डालते हैं (वह भाग जिसे अन्य मॉड्यूल को देखने में सक्षम होना चाहिए) और .cpp पर "साझा नहीं किया गया" भाग।

पीडी: हां, मैंने वैश्विक चर शामिल किए हैं। मैंने उन्हें कुछ समय का उपयोग किया है और हेडर पर उन्हें परिभाषित नहीं करना महत्वपूर्ण है, या आपको बहुत सारे मॉड्यूल मिलेंगे, प्रत्येक अपने स्वयं के चर को परिभाषित करेगा।

EDIT: डेविड की टिप्पणी के बाद संशोधित


अंगूठे के एक नियम के रूप में, संभव के रूप में कुछ .h फ़ाइल में शामिल होना चाहिए, और .cpp फ़ाइल में जो कुछ भी हेडर की आवश्यकता है उसे शामिल करना चाहिए। यह छोटा समय संकलन करता है और नामस्थान को प्रदूषित नहीं करता है।
डेविड थॉर्नले 16
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.