मुझे cpp फ़ाइलों को शामिल क्यों नहीं करना चाहिए और इसके बजाय हेडर का उपयोग करना चाहिए?


147

इसलिए मैंने अपना पहला C ++ प्रोग्रामिंग असाइनमेंट पूरा किया और अपना ग्रेड प्राप्त किया। लेकिन ग्रेडिंग के हिसाब से मैंने अंक खो दिए including cpp files instead of compiling and linking them। मैं बहुत स्पष्ट नहीं हूं कि इसका क्या मतलब है।

अपने कोड पर एक नज़र डालते हुए, मैंने अपनी कक्षाओं के लिए हेडर फ़ाइलों को बनाने के लिए नहीं चुना, लेकिन सीपीपी फ़ाइलों में सब कुछ किया (यह हेडर फ़ाइलों के बिना ठीक काम करता था ...)। मैं अनुमान लगा रहा हूं कि ग्रेडर का मतलब है कि मैंने '#include "mycppfile.cpp" लिखा है;' मेरी कुछ फाइलों में।

#includeसीपीपी फाइल को आईएनजी करने के लिए मेरा तर्क यह था: - हेडर फ़ाइल में जाने वाली हर चीज मेरी सीपीपी फाइल में थी, इसलिए मैंने इसका दावा किया कि यह हेडर फाइल की तरह था - बंदर-देखे-बंदर में फैशन करते हुए, मैंने उस दूसरे को देखा हेडर फाइलें फाइलों में #includeघ थीं , इसलिए मैंने अपनी सीपीपी फाइल के लिए भी ऐसा ही किया।

तो वास्तव में मैंने क्या गलत किया, और यह बुरा क्यों है?


36
यह वास्तव में एक अच्छा सवाल है। मुझे उम्मीद है कि बहुत सारे c ++ newbies को इससे मदद मिलेगी।
मिया क्लार्क

जवाबों:


174

मेरे ज्ञान का सबसे अच्छा करने के लिए, सी ++ मानक हेडर फ़ाइलों और स्रोत फ़ाइलों के बीच कोई अंतर नहीं जानता है। जहां तक ​​भाषा का सवाल है, कानूनी कोड वाली कोई भी टेक्स्ट फ़ाइल किसी भी अन्य के समान है। हालाँकि, हालाँकि आपके प्रोग्राम में स्रोत फ़ाइलों सहित गैर-कानूनी नहीं है, जो आपके स्रोत फ़ाइलों को पहली जगह में अलग करने से आपको मिलने वाले किसी भी फ़ायदे को ख़त्म कर देगा।

अनिवार्य रूप से, जो आपके द्वारा निर्दिष्ट संपूर्ण फ़ाइल को लेने के #includeलिए प्रीप्रोसेसर को बताता है, और कंपाइलर को उस पर अपना हाथ आने से पहले इसे अपनी सक्रिय फ़ाइल में कॉपी करें । इसलिए जब आप अपनी परियोजना की सभी स्रोत फ़ाइलों को एक साथ शामिल करते हैं, तो आपने जो कुछ किया है, उसके बीच मौलिक रूप से कोई अंतर नहीं है, और बस एक विशाल स्रोत फ़ाइल को बिना किसी अलगाव के बना रहा है।

"ओह, यह कोई बड़ी बात नहीं है। अगर यह चलता है, तो यह ठीक है," मैं आपको रोता सुनता हूं। और एक मायने में, आप सही होंगे। लेकिन अभी आप एक छोटे से छोटे कार्यक्रम के साथ काम कर रहे हैं, और एक अच्छा और अपेक्षाकृत unencumbered CPU आपके लिए इसे संकलित करने के लिए। आप हमेशा इतने भाग्यशाली नहीं होंगे।

यदि आप कभी भी गंभीर कंप्यूटर प्रोग्रामिंग के दायरे में आते हैं, तो आप दर्जनों की बजाए लाखों की संख्या में लाइन काउंट देख सकते हैं। यह बहुत सारी लाइनें हैं। और अगर आप आधुनिक डेस्कटॉप कंप्यूटर पर इनमें से किसी एक को संकलित करने की कोशिश करते हैं, तो यह सेकंड के बजाय घंटों का समय ले सकता है।

"ओह नहीं! यह भयानक लगता है! हालांकि मैं इस भयानक भाग्य को रोक सकता हूँ?" दुर्भाग्य से, आप इसके बारे में बहुत कुछ नहीं कर सकते हैं। यदि संकलन करने में घंटों लगते हैं, तो संकलन में घंटे लगते हैं। लेकिन यह केवल वास्तव में पहली बार मायने रखता है - एक बार जब आप इसे एक बार संकलित कर लेते हैं, तो इसे फिर से संकलित करने का कोई कारण नहीं है।

जब तक आप कुछ नहीं बदलते।

अब, यदि आपके पास कोड की दो मिलियन पंक्तियाँ एक साथ एक विशाल बीहेम में विलीन हो गई थीं, और एक साधारण बग फिक्स करने की आवश्यकता थी, जैसे कि, तो x = y + 1, इसका मतलब है कि आपको इसे परीक्षण करने के लिए सभी दो मिलियन लाइनों को फिर से संकलित करना होगा। और अगर आपको पता चलता है कि आप x = y - 1इसके बजाय करना चाहते थे, तो फिर से, संकलन की दो मिलियन लाइनें आपका इंतजार कर रही हैं। उस समय के कई घंटे बर्बाद हो जाते हैं जो कुछ और करने में बेहतर खर्च हो सकते हैं।

"लेकिन मुझे अनुत्पादक होने से नफरत है! अगर केवल मेरे कोडबेस के अलग-अलग हिस्सों को अलग-अलग संकलित करने का कोई तरीका था , और किसी तरह उन्हें बाद में एक साथ जोड़ दें!" एक उत्कृष्ट विचार, सिद्धांत रूप में। लेकिन क्या होगा अगर आपके प्रोग्राम को यह जानना होगा कि एक अलग फाइल में क्या हो रहा है? जब तक आप इसके बजाय छोटे छोटे .exe फ़ाइलों का एक गुच्छा चलाना चाहते हैं, तब तक अपने कोडबेस को पूरी तरह से अलग करना असंभव है।

"लेकिन निश्चित रूप से यह संभव होना चाहिए! प्रोग्रामिंग अन्यथा शुद्ध यातना की तरह लगता है! क्या होगा अगर मुझे कार्यान्वयन से इंटरफ़ेस को अलग करने का कोई तरीका मिला ? इन अलग-अलग कोड खंडों से सिर्फ पर्याप्त जानकारी लेकर, उन्हें कार्यक्रम के बाकी हिस्सों की पहचान करने, और डालने के लिए। इसके बजाय किसी प्रकार की हेडर फ़ाइल में? और इस तरह, मैं केवल संकलन करने के लिए आवश्यक जानकारी लाने के लिए #include प्रीप्रोसेसर निर्देश का उपयोग कर सकता हूं ! "

हम्म। तुम वहाँ कुछ करने के लिए हो सकता है। मुझे बताएं कि वह आपके लिए कैसा रहा।


13
अच्छा जवाब, सर। यह एक मज़ेदार रीड था, और समझने में आसान था। काश मेरी पाठ्यपुस्तक इस तरह लिखी जाती।
इलियम

@veol किताबों की पहली श्रृंखला के लिए खोज - मुझे नहीं पता कि उनके पास C ++ संस्करण है या नहीं। headfirstlabs.com
अमरघोष

2
यह (निश्चित) आज तक का सबसे अच्छा शब्द है जिसे मैंने सुना या माना है। जस्टिन केस, एक निपुण शुरुआत, ने एक मिलियन कीस्ट्रोक्स में एक ऐसी परियोजना को देखा, जिसे अभी तक शिप नहीं किया गया है और एक सराहनीय "पहला प्रोजेक्ट" जो एक वास्तविक उपयोगकर्ता आधार में एप्लिकेशन की रोशनी देख रहा है, ने क्लोज़र द्वारा संबोधित एक समस्या को पहचान लिया है। ओपी की मूल समस्या के उन्नत राज्यों के समान उल्लेखनीय रूप से लगता है कि "यह लगभग एक सौ बार कोडित किया गया था और यह अपवाद के बिना प्रोग्रामिंग का उपयोग किए बिना शून्य (जैसा कि कोई वस्तु) बनाम अशक्त (भतीजे के रूप में) के लिए क्या करना है, यह समझ नहीं सकता।"
निकोलस जॉर्डन

बेशक यह सब टेम्पलेट्स के लिए अलग हो जाता है क्योंकि अधिकांश संकलक 'निर्यात' कीवर्ड का समर्थन / कार्यान्वयन नहीं करते हैं।
KitsuneYMG

1
एक और मुद्दा यह है कि आपके पास कई राज्य हैं कला पुस्तकालय (यदि BOOST के बारे में सोचें) जो केवल हेडर का उपयोग करता है तो कक्षाएं ... हो, रुको? अनुभवी प्रोग्रामर कार्यान्वयन से अलग इंटरफ़ेस क्यों नहीं करता है? उत्तर का हिस्सा हो सकता है कि ब्लाइंडली ने क्या कहा, एक और हिस्सा यह हो सकता है कि एक फ़ाइल दो से बेहतर है जब यह संभव हो, और एक अन्य हिस्सा यह है कि लिंक करने की लागत काफी अधिक हो सकती है। मैंने देखा है कि कार्यक्रम स्रोत और संकलक के प्रत्यक्ष समावेश के साथ दस गुना तेजी से चलते हैं। क्योंकि लिंकिंग ज्यादातर ऑप्टिमाइज़ेशन को ब्लॉक करता है।
क्रिस्स

45

यह शायद आप की तुलना में अधिक विस्तृत जवाब है, लेकिन मुझे लगता है कि एक उचित स्पष्टीकरण उचित है।

C और C ++ में, एक स्रोत फ़ाइल को एक अनुवाद इकाई के रूप में परिभाषित किया गया है । अधिवेशन के द्वारा, हेडर फाइलें फंक्शन डिक्लेरेशन रखती हैं, परिभाषाएँ और क्लास परिभाषाएँ टाइप करती हैं। वास्तविक कार्य कार्यान्वयन अनुवाद इकाइयों, यानी .cpp फ़ाइलों में रहते हैं।

इसके पीछे विचार यह है कि फ़ंक्शंस और क्लास / स्ट्रक्चर मेंबर फ़ंक्शंस को एक बार संकलित और इकट्ठा किया जाता है, फिर अन्य फ़ंक्शंस डुप्लिकेट बनाए बिना उस कोड को एक स्थान से कॉल कर सकते हैं। आपके कार्यों को स्पष्ट रूप से "बाहरी" घोषित किया जाता है।

/* Function declaration, usually found in headers. */
/* Implicitly 'extern', i.e the symbol is visible everywhere, not just locally.*/
int add(int, int);

/* function body, or function definition. */
int add(int a, int b) 
{
   return a + b;
}

यदि आप किसी कार्य को अनुवाद इकाई के लिए स्थानीय बनाना चाहते हैं, तो आप इसे 'स्थैतिक' के रूप में परिभाषित करते हैं। इसका क्या मतलब है? इसका अर्थ है कि यदि आप बाह्य कार्यों के साथ स्रोत फ़ाइलों को शामिल करते हैं, तो आपको पुनर्निर्धारण त्रुटियां मिलेंगी, क्योंकि संकलक एक ही बार में एक ही कार्यान्वयन में आता है। इसलिए, आप चाहते हैं कि आपकी सभी अनुवाद इकाइयां फ़ंक्शन की घोषणा देखें, लेकिन फ़ंक्शन निकाय नहीं

तो यह सब अंत में एक साथ मैश्ड कैसे हो जाता है? यह लिंकर का काम है। एक लिंकर सभी ऑब्जेक्ट फ़ाइलों को पढ़ता है जो कोडांतरक चरण द्वारा उत्पन्न होता है और प्रतीकों को हल करता है। जैसा कि मैंने पहले कहा था, एक प्रतीक सिर्फ एक नाम है। उदाहरण के लिए, एक चर या एक फ़ंक्शन का नाम। जब अनुवाद इकाइयाँ जो कार्य करती हैं या प्रकारों की घोषणा करती हैं, उन कार्यों या प्रकारों के कार्यान्वयन को नहीं जानती हैं, तो उन प्रतीकों को अनसुलझे कहा जाता है। लिंकर अनुवाद इकाई को जोड़कर अनसुलझे प्रतीक को हल करता है जो अपरिभाषित प्रतीक को एक साथ रखता है जिसमें कार्यान्वयन होता है। ओह। यह बाहरी रूप से दिखाई देने वाले सभी प्रतीकों के लिए सही है, चाहे वे आपके कोड में लागू किए गए हों, या एक अतिरिक्त पुस्तकालय द्वारा प्रदान किए गए हों। एक पुस्तकालय वास्तव में पुन: प्रयोज्य कोड के साथ सिर्फ एक संग्रह है।

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

अन्य अपवाद टेम्पलेट है। चूंकि कंपाइलर को इंस्टेंट करते समय पूरे टेम्प्लेट टाइप डेफिनिशन को देखने की जरूरत होती है, इसलिए स्टैंडअलोन फ़ंक्शंस या नॉर्मल क्लासेस के साथ-साथ इंप्लीमेंटेशन को डिफाइन करना संभव नहीं है। खैर, शायद अब यह संभव है, लेकिन "निर्यात" कीवर्ड के लिए व्यापक संकलक समर्थन प्राप्त करना एक लंबा, लंबा समय लगा। तो 'निर्यात' के लिए समर्थन के बिना, अनुवाद इकाइयां तात्कालिक रूप से संबंधित प्रकार और कार्यों की अपनी स्थानीय प्रतियां प्राप्त करती हैं, इनलाइन काम कैसे करती हैं। 'निर्यात' के समर्थन के साथ, यह मामला नहीं है।

दो अपवादों के लिए, कुछ लोग इनलाइन फ़ंक्शंस, टेंपलेटेड फ़ंक्शंस और टेम्प्लेटेड प्रकारों के कार्यान्वयन को .cpp फ़ाइलों में, और फिर #include .cpp फ़ाइल में डालने के लिए इसे "अच्छे" पाते हैं। क्या यह हेडर है या स्रोत फ़ाइल वास्तव में मायने नहीं रखती है; प्रीप्रोसेसर की परवाह नहीं है और सिर्फ एक सम्मेलन है।

C ++ कोड (कई फाइलें) और अंतिम निष्पादन योग्य से पूरी प्रक्रिया का एक त्वरित सारांश:

  • पूर्वप्रक्रमक चलाया जाता है, जो सभी ऐसे निर्देश हैं जो एक '#' के साथ शुरू होता पार्स करता है। उदाहरण के लिए, #include निर्देश सम्मिलित फ़ाइल को अवर के साथ समाप्‍त करता है। यह मैक्रो-रिप्लेसमेंट और टोकन-पेस्टिंग भी करता है।
  • वास्तविक संकलक मध्यवर्ती पाठ फ़ाइल पर प्रीप्रोसेसर चरण के बाद चलता है, और कोडांतरक कोड का उत्सर्जन करता है।
  • कोडांतरक विधानसभा फ़ाइल और उत्सर्जन करता है मशीन कोड पर रन, यह आम तौर पर एक कहा जाता है वस्तु फ़ाइल और प्रश्न में ऑपरेटिव सिस्टम की बाइनरी निष्पादन प्रारूप इस प्रकार है। उदाहरण के लिए, विंडोज पीई (पोर्टेबल निष्पादन योग्य प्रारूप) का उपयोग करता है, जबकि लिनक्स जीएनयू एक्सटेंशन के साथ यूनिक्स सिस्टम वी ईएलएफ प्रारूप का उपयोग करता है। इस स्तर पर, प्रतीकों को अभी भी अपरिभाषित के रूप में चिह्नित किया जाता है।
  • अंत में, लिंकर चलाया जाता है। पिछले सभी चरण क्रम में प्रत्येक अनुवाद इकाई पर चलाए गए थे। हालाँकि, लिंकर चरण सभी उत्पन्न ऑब्जेक्ट फ़ाइलों पर काम करता है जो कोडांतरक द्वारा उत्पन्न किए गए थे। लिंकर प्रतीकों को हल करता है और अनुभागों और खंडों को बनाने जैसा बहुत सा जादू करता है, जो लक्ष्य प्लेटफ़ॉर्म और बाइनरी प्रारूप पर निर्भर है। प्रोग्रामर को इसे सामान्य रूप से जानना आवश्यक नहीं है, लेकिन यह निश्चित रूप से कुछ मामलों में मदद करता है।

फिर, यह निश्चित रूप से आपके द्वारा पूछे जाने से अधिक था, लेकिन मुझे उम्मीद है कि नॉटी-ग्रिटि विवरण आपको बड़ी तस्वीर देखने में मदद करता है।


2
आपके संपूर्ण स्पष्टीकरण के लिए धन्यवाद। मैं मानता हूं, यह सब मेरे लिए अभी तक समझ में नहीं आया है, और मुझे लगता है कि मुझे आपके उत्तर पर (और फिर से) पढ़ने की आवश्यकता होगी।
आईआईएलएम

1
एक उत्कृष्ट स्पष्टीकरण के लिए +1। बहुत बुरा यह शायद सभी C ++ newbies दूर डराने जाएगा। :)
गोल्डपीसो

1
हे, बुरा नहीं लग रहा है veol। स्टैक ओवरफ्लो पर, सबसे लंबा उत्तर शायद ही कभी सबसे अच्छा जवाब होता है।

int add(int, int);एक फ़ंक्शन घोषणा है । इसका प्रोटोटाइप हिस्सा बस है int, int। हालाँकि, C ++ में सभी फ़ंक्शंस में एक प्रोटोटाइप है, इसलिए यह शब्द वास्तव में केवल सी में समझ में आता है। मैंने इस आशय का उत्तर दिया है।
मेलपोमेन

exportटेम्प्लेट के लिए 2011 में भाषा से हटा दिया गया है। यह वास्तव में कंपाइलरों द्वारा समर्थित नहीं था।
मेलपोमीन

10

विशिष्ट समाधान .hकेवल घोषणाओं के .cppलिए फ़ाइलों और कार्यान्वयन के लिए फ़ाइलों का उपयोग करना है। यदि आपको कार्यान्वयन का पुन: उपयोग करने की आवश्यकता है, तो आप उस .hफ़ाइल को उसी फ़ाइल में शामिल करते .cppहैं जहाँ आवश्यक वर्ग / फ़ंक्शन / जो कुछ भी उपयोग किया जाता है और पहले से संकलित .cppफ़ाइल के खिलाफ लिंक होता है (या तो .objफ़ाइल - आमतौर पर एक परियोजना के भीतर उपयोग की जाती है - या .lib फ़ाइल - आमतौर पर उपयोग की जाती है। कई परियोजनाओं से पुन: उपयोग के लिए)। इस तरह से आपको केवल कार्यान्वयन में परिवर्तन होने पर सब कुछ फिर से खोलने की आवश्यकता नहीं है।


6

एक काले बॉक्स के रूप में सीपीपी फ़ाइलों के बारे में सोचें और उन काले बक्से का उपयोग कैसे करें, इस पर गाइड के रूप में .h फाइलें।

Cpp फ़ाइलों को समय से पहले संकलित किया जा सकता है। यह आपके साथ काम नहीं करता है # उन्हें हटा दें, क्योंकि जब भी वह इसे संकलित करता है, तो उसे वास्तविक रूप से कोड को "अपने प्रोग्राम में" शामिल करना होगा। यदि आप सिर्फ हेडर को शामिल करते हैं, तो यह केवल हेडर फ़ाइल का उपयोग करके यह निर्धारित कर सकता है कि प्री-कैप्ड सीपीपी फ़ाइल का उपयोग कैसे किया जाए।

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

इसे भी पढ़ें: हैडर फ़ाइल में पैटर्न शामिल करें


अधिक ठोस उदाहरण के लिए धन्यवाद। मैंने आपके लिंक के माध्यम से पढ़ने की कोशिश की, लेकिन अब मैं उलझन में हूं ... हेडर सहित स्पष्ट रूप से और आगे की घोषणा के बीच क्या अंतर है?
इलियम

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

आगे की घोषणा: int someFunction (int needValue); सूचना के उपयोग की सूचना और (ususally) कोई घुंघराले ब्रेसिज़ नहीं। यह, जैसा कि दिया गया है, संकलक को बताता है कि कुछ बिंदु पर आपको एक फ़ंक्शन की आवश्यकता होगी जो एक इंट लेता है और एक इंट रिटर्न देता है, कंपाइलर इस जानकारी का उपयोग करके इसके लिए कॉल आरक्षित कर सकता है। इसे अग्रगामी घोषणा कहा जाएगा। फैनसीयर कंपाइलर को इसकी आवश्यकता के बिना फ़ंक्शन को खोजने में सक्षम होना चाहिए, जिसमें हेडर भी शामिल है जो आगे की घोषणाओं का एक गुच्छा घोषित करने का एक आसान तरीका हो सकता है।
निकोलस जॉर्डन

6

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

इस पृथक्करण के लाभ: यदि आप अपने प्रोजेक्ट में .cpp फ़ाइलों में से एक को पुनः प्राप्त कर रहे हैं, तो आपको अन्य सभी को पुनः स्थापित करने की आवश्यकता नहीं है। आप बस उस विशेष .cpp फ़ाइल के लिए नई ऑब्जेक्ट फ़ाइल बनाएँ। संकलक को अन्य .cpp फ़ाइलों को देखने की आवश्यकता नहीं है। हालाँकि, यदि आप अपनी वर्तमान .cpp फ़ाइल में फ़ंक्शन को कॉल करना चाहते हैं जो अन्य .cpp फ़ाइलों में लागू की गई थीं, तो आपको संकलक को बताना होगा कि वे क्या तर्क लेते हैं; यह हेडर फ़ाइलों को शामिल करने का उद्देश्य है।

नुकसान: किसी .cpp फ़ाइल को संकलित करते समय, कंपाइलर अन्य .cpp फ़ाइलों के अंदर 'नहीं' देख सकता है। इसलिए यह पता नहीं है कि वहाँ कैसे कार्य कार्यान्वित किए जाते हैं, और परिणामस्वरूप आक्रामक तरीके से अनुकूलन नहीं किया जा सकता है। लेकिन मुझे लगता है कि आपको अभी खुद से परेशान होने की जरूरत नहीं है (:


5

मूल विचार जो हेडर केवल शामिल हैं और cpp फाइलें केवल संकलित हैं। एक बार जब आपके पास कई cpp फाइलें होंगी, तो यह अधिक उपयोगी हो जाएगा, और जब आप उनमें से केवल एक को संशोधित करते हैं, तो संपूर्ण अनुप्रयोग को फिर से जमा करना। या जब फ़ाइलों में फ़ंक्शन एक दूसरे के आधार पर शुरू होंगे। तो, आपको अपनी हेडर फ़ाइलों में वर्ग की घोषणाओं को अलग करना चाहिए, सीपीपी फाइलों में कार्यान्वयन को छोड़ना चाहिए और सीपीएफ फाइलों को संकलित करने और प्रोग्राम में परिणामी ऑब्जेक्ट फ़ाइलों को लिंक करने के लिए एक मेकफाइल (या कुछ और उपकरण जो आप उपयोग कर रहे हैं, उसके आधार पर) लिखें।


3

यदि आप अपने प्रोग्राम में कई अन्य फाइलों में एक cpp फ़ाइल को #include करते हैं, तो कंपाइलर कई बार cpp फाइल को संकलित करने की कोशिश करेगा, और एक त्रुटि उत्पन्न करेगा क्योंकि एक ही तरीके के कई कार्यान्वयन होंगे।

संकलन में अधिक समय लगेगा (जो बड़ी परियोजनाओं पर एक समस्या बन जाता है), यदि आप # cincluded cpp फ़ाइलों में संपादन करते हैं, जो तब किसी भी फाइल के recompilation को मजबूर करते हैं # उन्हें हटा दें।

बस अपनी घोषणाओं को हेडर फाइलों में डालें और उनमें शामिल करें (जैसा कि वे वास्तव में प्रति se कोड उत्पन्न नहीं करते हैं), और लिंकर संबंधित सीपीपी कोड के साथ घोषणाओं को पूरा करेगा (जो तब केवल एक बार संकलित होता है)।


इसलिए, अब और संकलन समय होने के अलावा, मुझे तब समस्याएँ आनी शुरू हो जाएँगी जब मैं अपनी cpp फ़ाइल को बहुत सारी अलग-अलग फ़ाइलों में शामिल करूँगा, जिसमें शामिल cpp फ़ाइलों में फ़ंक्शन का उपयोग किया जाता है?
१ial:59 बजे ialm

हां, इसे नेमस्पेस टकराव कहा जाता है। यहां रुचि यह है कि क्या लिबास के खिलाफ लिंकिंग नेमस्पेस मुद्दों का परिचय दिया है। सामान्य तौर पर, मुझे लगता है कि कंपाइलर्स ट्रांसलेशन यूनिट स्कोप (सभी एक फ़ाइल में) के लिए बेहतर संकलन समय का उत्पादन करते हैं जो नेमस्पेस मुद्दों का परिचय देता है - जिससे फिर से अलग हो जाता है .... आप प्रत्येक ट्रांसलेशन यूनिट में फ़ाइल को शामिल कर सकते हैं, (माना जाता है) यहां तक ​​कि एक प्राग्मा (# प्रपामा एक बार) है जो इसे लागू करने के लिए माना जाता है लेकिन यह एक सपोसिटरी सपोजिशन है। जहां भी 32-बिट लिंक लागू नहीं हैं, वहां से लिबास (.O फ़ाइलों) पर आंख मूंदकर भरोसा न करें।
निकोलस जॉर्डन

2

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

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


2

पुन: प्रयोज्य, वास्तुकला और डेटा इनकैप्सुलेशन

यहाँ एक उदाहरण है:

कहते हैं कि आप एक cpp फ़ाइल बनाते हैं जिसमें एक क्लास मिस्ट्रिंग में स्ट्रिंग रूटीन का एक सरल रूप होता है, आप इसके लिए क्लास का ऐलान एक रहस्मयी तरीके से करते हैं। मैं a .ringj में।

अब आपके मुख्य कार्यक्रम (जैसे main.cpp) में आप हेडर और मिस्ट्रींग के साथ लिंक शामिल करते हैं। अपने कार्यक्रम में रहस्य का उपयोग करने के लिए आप विवरण के बारे में परवाह नहीं करते हैं कि शीर्ष लेख के बाद से रहस्य का कार्यान्वयन कैसे होता है

अब यदि कोई दोस्त आपकी मिस्ट्रीिंग क्लास का उपयोग करना चाहता है, तो आप उसे मिस्ट्रिंग.एच और मिस्ट्रिंग.बॉज देते हैं, उसे यह जानना भी जरूरी नहीं है कि यह तब तक काम करता है जब तक यह काम करता है।

बाद में अगर आपके पास ऐसी .obj फाइलें हैं, तो आप उन्हें एक .lib फ़ाइल में जोड़ सकते हैं और इसके बजाय उससे लिंक कर सकते हैं।

आप मिस्ट्रिंग.कैप फ़ाइल को बदलने और इसे और अधिक प्रभावी ढंग से लागू करने का निर्णय भी ले सकते हैं, इससे आपके main.cpp या आपके दोस्त प्रोग्राम प्रभावित नहीं होंगे।


2

यदि यह आपके लिए काम करता है तो इसमें कुछ भी गलत नहीं है - सिवाय इसके कि यह उन लोगों के पंखों को कुतर देगा जो सोचते हैं कि चीजों को करने का एक ही तरीका है।

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

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

कई उत्तर कार्यान्वयन से इंटरफ़ेस को अलग करने पर चर्चा करते हैं। हालाँकि, यह शामिल फ़ाइलों की एक अंतर्निहित विशेषता नहीं है, और यह #include "हैडर" फ़ाइलों के लिए काफी आम है जो सीधे उनके कार्यान्वयन को शामिल करते हैं (यहां तक ​​कि सी ++ मानक लाइब्रेरी भी एक महत्वपूर्ण डिग्री तक ऐसा करती है)।

आपने जो कुछ भी किया है, उसके बारे में केवल "अपरंपरागत" एकमात्र चीज आपकी सम्मिलित फ़ाइलों का नामकरण कर रही है। ".hpp" या ".hpp" के बजाय "।"


1

जब आप किसी प्रोग्राम को कंपाइल और लिंक करते हैं तो कंपाइलर पहले अलग-अलग cpp फाइलों को कंपाइल करता है और फिर उन्हें लिंक (कनेक्ट) करता है। हेडर कभी संकलित नहीं होंगे, जब तक कि पहले एक सीपीपी फ़ाइल में शामिल नहीं किया जाता है।

आमतौर पर हेडर घोषणाएं हैं और सीपीपी कार्यान्वयन फाइलें हैं। हेडर में आप एक वर्ग या फ़ंक्शन के लिए एक इंटरफ़ेस परिभाषित करते हैं, लेकिन आप यह छोड़ देते हैं कि आप वास्तव में विवरण कैसे लागू करते हैं। यदि आप एक में बदलाव करते हैं तो इस तरह आपको हर cpp फ़ाइल को फिर से नहीं करना है।


यदि आप हेडर फ़ाइल से कार्यान्वयन को छोड़ देते हैं तो मुझे माफ करना चाहिए लेकिन यह एक जावा इंटरफ़ेस की तरह लगता है?
गन्सूब

1

मैं आपको जॉन लैकोस द्वारा लार्ज स्केल C ++ सॉफ्टवेयर डिज़ाइन के माध्यम से जाने का सुझाव दूंगा । कॉलेज में, हम आम तौर पर छोटे प्रोजेक्ट लिखते हैं, जहां हम इस तरह की समस्याओं को नहीं आते हैं। पुस्तक इंटरफेस और कार्यान्वयन को अलग करने के महत्व पर प्रकाश डालती है।

हेडर फ़ाइलों में आमतौर पर इंटरफेस होते हैं जिन्हें माना नहीं जाता है कि उन्हें बार-बार बदला जाए। इसी तरह वर्चुअल कंस्ट्रक्टर आइडियल जैसे पैटर्न पर एक नज़र आपको अवधारणा को और अधिक समझने में मदद करेगी।

मैं अभी भी तुम्हारी तरह सीख रहा हूँ :)


पुस्तक सुझाव के लिए धन्यवाद। मुझे नहीं पता कि मैं कभी भी बड़े पैमाने पर C ++ प्रोग्राम बनाने के चरण में
पहुँचूँगा

बड़े पैमाने पर कार्यक्रमों को कोड करना और कई चुनौती के लिए यह मजेदार है। मुझे यह पसंद आने लगा है :)
पंकज

1

यह एक किताब लिखने जैसा है, आप केवल एक बार समाप्त अध्यायों को प्रिंट करना चाहते हैं

कहते हैं आप एक किताब लिख रहे हैं। यदि आप अध्यायों को अलग-अलग फाइलों में रखते हैं तो आपको केवल एक अध्याय का प्रिंट आउट निकालने की आवश्यकता है यदि आपने इसे बदल दिया है। एक अध्याय पर काम करने से दूसरों में कोई बदलाव नहीं आता है।

लेकिन सीपीपी फ़ाइलों सहित, संकलक के दृष्टिकोण से है, जैसे पुस्तक के सभी अध्यायों को एक फ़ाइल में संपादित करना। फिर यदि आप इसे बदलते हैं, तो आपको अपने संशोधित अध्याय को मुद्रित करने के लिए पूरी पुस्तक के सभी पृष्ठों को प्रिंट करना होगा। ऑब्जेक्ट कोड पीढ़ी में कोई "प्रिंट चयनित पृष्ठ" विकल्प नहीं है।

सॉफ्टवेयर पर वापस: मेरे पास लिनक्स और रूबी एसकेटी पड़े हुए हैं। कोड की लाइनों का एक मोटा माप ...

     Linux       Ruby
   100,000    100,000   core functionality (just kernel/*, ruby top level dir)
10,000,000    200,000   everything 

उन चार श्रेणियों में से किसी एक में बहुत सारे कोड हैं, इसलिए प्रतिरूपकता की आवश्यकता है। इस तरह का कोड आधार आश्चर्यजनक रूप से वास्तविक-विश्व प्रणालियों के लिए विशिष्ट है।

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