C ++ में हेडर फाइलें और .cpp फाइलें क्यों होती हैं?
C ++ में हेडर फाइलें और .cpp फाइलें क्यों होती हैं?
जवाबों:
खैर, इसका मुख्य कारण इंटरफ़ेस को कार्यान्वयन से अलग करना होगा। शीर्ष लेख "क्या" को एक वर्ग घोषित करता है (या जो कुछ भी कार्यान्वित किया जा रहा है) करेगा, जबकि सीपीपी फ़ाइल उन विशेषताओं को "कैसे" परिभाषित करेगी।
यह निर्भरता कम करता है ताकि कोड का उपयोग करने वाले कोड को कार्यान्वयन के सभी विवरणों और किसी अन्य वर्ग / हेडर को केवल उसी के बारे में जानने की आवश्यकता न हो। यह संकलन समय को कम करेगा और कार्यान्वयन में कुछ बदलाव होने पर आवश्यक पुन: प्राप्ति की मात्रा भी।
यह बिल्कुल सही नहीं है, और आप आमतौर पर इंटरफ़ेस और कार्यान्वयन को ठीक से अलग करने के लिए Pimpl Idiom जैसी तकनीकों का सहारा लेंगे , लेकिन यह एक अच्छी शुरुआत है।
C ++ में एक संकलन 2 प्रमुख चरणों में किया जाता है:
पहला "स्रोत" टेक्स्ट फ़ाइलों को बाइनरी "ऑब्जेक्ट" फ़ाइलों में संकलित करना है: सीपीपी फ़ाइल संकलित फ़ाइल है और अन्य सीपीपी फ़ाइलों (या यहां तक कि पुस्तकालयों) के बारे में किसी भी ज्ञान के बिना संकलित की जाती है, जब तक कि इसे कच्ची घोषणा के माध्यम से नहीं खिलाया जाता है या हेडर समावेश। CPP फ़ाइल को आमतौर पर .OBJ या a .O "ऑब्जेक्ट" फ़ाइल में संकलित किया जाता है।
दूसरी सभी "ऑब्जेक्ट" फ़ाइलों को एक साथ जोड़ना है, और इस प्रकार, अंतिम बाइनरी फ़ाइल (या तो एक पुस्तकालय या एक निष्पादन योग्य) का निर्माण।
इस सारी प्रक्रिया में एचपीपी कहाँ फिट बैठता है?
प्रत्येक CPP फ़ाइल का संकलन अन्य सभी CPP फ़ाइलों से स्वतंत्र है, जिसका अर्थ है कि यदि A.CPP को B.CPP में परिभाषित प्रतीक की आवश्यकता है, जैसे:
// A.CPP
void doSomething()
{
doSomethingElse(); // Defined in B.CPP
}
// B.CPP
void doSomethingElse()
{
// Etc.
}
यह संकलित नहीं करेगा क्योंकि A.CPP के पास "doSomethingElse" को जानने का कोई तरीका नहीं है ... जब तक A.CPP में कोई घोषणा नहीं होती, जैसे:
// A.CPP
void doSomethingElse() ; // From B.CPP
void doSomething()
{
doSomethingElse() ; // Defined in B.CPP
}
फिर, यदि आपके पास C.CPP है जो समान प्रतीक का उपयोग करता है, तो आप घोषणा को कॉपी / पेस्ट करते हैं ...
हां, एक समस्या है। कॉपी / पेस्ट खतरनाक हैं, और बनाए रखना मुश्किल है। जिसका अर्थ है कि अगर हम कॉपी / पेस्ट न करने और अभी भी प्रतीक घोषित करने का कोई तरीका है तो यह अच्छा होगा ... हम इसे कैसे कर सकते हैं? कुछ टेक्स्ट फ़ाइल को शामिल करके, जो आमतौर पर .h, .hxx, .h ++ या, मेरी पसंदीदा है, C ++ फ़ाइलों के लिए, .hpp:
// B.HPP (here, we decided to declare every symbol defined in B.CPP)
void doSomethingElse() ;
// A.CPP
#include "B.HPP"
void doSomething()
{
doSomethingElse() ; // Defined in B.CPP
}
// B.CPP
#include "B.HPP"
void doSomethingElse()
{
// Etc.
}
// C.CPP
#include "B.HPP"
void doSomethingAgain()
{
doSomethingElse() ; // Defined in B.CPP
}
include
काम करता है ?एक फ़ाइल सहित, संक्षेप में, पार्स और फिर सीपीपी फ़ाइल में इसकी सामग्री को कॉपी-पेस्ट करें।
उदाहरण के लिए, A.HPP हेडर के साथ निम्नलिखित कोड में:
// A.HPP
void someFunction();
void someOtherFunction();
... स्रोत B.CPP:
// B.CPP
#include "A.HPP"
void doSomething()
{
// Etc.
}
... समावेश के बाद बन जाएगा:
// B.CPP
void someFunction();
void someOtherFunction();
void doSomething()
{
// Etc.
}
वर्तमान मामले में, इसकी आवश्यकता नहीं है, और B.HPP में doSomethingElse
फ़ंक्शन डिक्लेरेशन है, और B.CPP के पास doSomethingElse
फंक्शन डेफिनेशन है (जो कि स्वयं एक डिक्लेरेशन है)। लेकिन एक अधिक सामान्य मामले में, जहां B.HPP का उपयोग घोषणाओं (और इनलाइन कोड) के लिए किया जाता है, कोई संगत परिभाषा नहीं हो सकती है (उदाहरण के लिए, enums, सादे संरचनाएं, आदि), इसलिए यदि बीसीपीपी शामिल हो तो इसकी आवश्यकता हो सकती है। B.HPP से उन घोषणाओं का उपयोग करता है। सभी में, यह एक स्रोत के लिए "अच्छा स्वाद" है जो डिफ़ॉल्ट रूप से अपने हेडर को शामिल करता है।
हेडर फ़ाइल इस प्रकार आवश्यक है, क्योंकि C ++ कंपाइलर अकेले प्रतीक घोषणाओं की खोज करने में असमर्थ है, और इस प्रकार, आपको उन घोषणाओं को शामिल करके इसकी मदद करनी चाहिए।
एक अंतिम शब्द: आपको अपनी एचपीपी फ़ाइलों की सामग्री के चारों ओर हेडर गार्ड्स लगाने चाहिए, यह सुनिश्चित करने के लिए कि कई समावेश कुछ भी नहीं तोड़ेंगे, लेकिन सभी में, मेरा मानना है कि एचपीपी फाइलों के अस्तित्व का मुख्य कारण ऊपर बताया गया है।
#ifndef B_HPP_
#define B_HPP_
// The declarations in the B.hpp file
#endif // B_HPP_
या सरल भी
#pragma once
// The declarations in the B.hpp file
You still have to copy paste the signature from header file to cpp file, don't you?
कोई ज़रूरत नहीं है। जब तक CPP "HPP" को शामिल करता है, तब तक Precompiler स्वचालित रूप से HPP फ़ाइल की सामग्री की कॉपी-पेस्ट CPP फ़ाइल में करेगा। मैंने स्पष्ट करने के लिए उत्तर को अद्यतन किया।
While compiling A.cpp, compiler knows the types of arguments and return value of doSomethingElse from the call itself
। नहीं, यह नहीं है। यह केवल उपयोगकर्ता द्वारा प्रदान किए गए प्रकारों को जानता है, जो कि आधा समय, वापसी मूल्य को पढ़ने के लिए परेशान नहीं करेगा। फिर, निहितार्थ रूपांतरण होते हैं। और फिर, जब आपके पास कोड होगा: foo(bar)
आप यह भी सुनिश्चित नहीं कर सकते हैं कि foo
यह एक फ़ंक्शन है। इसलिए संकलक को हेडर में जानकारी तक पहुंचना होगा ताकि यह तय किया जा सके कि स्रोत सही तरीके से संकलित करता है या नहीं ... फिर, कोड संकलित होने के बाद, लिंकर सिर्फ एक साथ फ़ंक्शन कॉल करेगा।
Seems, they're just a pretty ugly arbitrary design.
: यदि C ++ 2012 में बनाया गया था, तो वास्तव में। लेकिन याद रखें कि C ++ को 1980 के दशक में C पर बनाया गया था, और उस समय, बाधाएं उस समय (IIRC) से काफी अलग थीं, इसे सी के बजाय उसी लिंकर्स को रखने के लिए गोद लेने के उद्देश्यों के लिए तय किया गया था।
foo(bar)
यह एक फ़ंक्शन है - अगर यह एक पॉइंटर के रूप में प्राप्त किया गया है? वास्तव में, खराब डिज़ाइन की बात करते हुए, मैं C को दोष देता हूं, C ++ को नहीं। मुझे वास्तव में शुद्ध C की कुछ अड़चनें पसंद नहीं हैं, जैसे कि हेडर फाइल्स होना या फ़ंक्शंस का एक और केवल एक मान होना, जबकि इनपुट पर कई तर्क देना (इनपुट और आउटपुट का एक समान व्यवहार करना स्वाभाविक नहीं लगता है) ; कई तर्क क्यों, लेकिन एकल आउटपुट?) :)
Why can't I be sure, that foo(bar) is a function
फू एक प्रकार हो सकता है, इसलिए आपके पास एक क्लास कंस्ट्रक्टर होगा। In fact, speaking of bad design, I blame C, not C++
: मैं बहुत सी चीजों के लिए सी को दोष दे सकता हूं, लेकिन 70 के दशक में डिजाइन किया गया है, उनमें से एक नहीं होगा। फिर से, उस समय की अड़चनें ... such as having header files or having functions return one and only one value
: ट्यूपल्स को कम करने में मदद मिल सकती है, साथ ही संदर्भ द्वारा तर्क पारित कर सकते हैं। अब, कई मानों को वापस लाने के लिए सिंटैक्स क्या होगा, और क्या यह भाषा को बदलने के लिए इसके लायक होगा?
क्योंकि C, जहां अवधारणा उत्पन्न हुई, 30 साल पुरानी है, और फिर, यह कई फ़ाइलों से कोड को एक साथ जोड़ने का एकमात्र व्यवहार्य तरीका था।
आज, यह एक भयानक हैक है जो पूरी तरह से C ++ में संकलन समय को नष्ट कर देता है, अनगिनत अनावश्यक निर्भरता का कारण बनता है (क्योंकि हेडर फ़ाइल में वर्ग परिभाषाएं कार्यान्वयन के बारे में बहुत अधिक जानकारी उजागर करती हैं), और इसी तरह।
क्योंकि सी ++ ने उन्हें सी। दुर्भाग्य से विरासत में मिला।
क्योंकि पुस्तकालय के प्रारूप को डिजाइन करने वाले लोग C प्रीप्रोसेसर मैक्रोज़ और फ़ंक्शन घोषणाओं जैसी शायद ही कभी उपयोग की जाने वाली जानकारी के लिए "बेकार" स्थान नहीं चाहते थे।
चूंकि आपको अपने संकलक को यह बताने के लिए उस जानकारी की आवश्यकता है "यह फ़ंक्शन बाद में उपलब्ध है जब लिंकर अपना काम कर रहा है", उन्हें दूसरी फाइल के साथ आना पड़ा जहां इस साझा जानकारी को संग्रहीत किया जा सकता है।
C / C ++ के बाद अधिकांश भाषाएं इस जानकारी को आउटपुट (उदाहरण के लिए जावा बाइटकोड) में संग्रहीत करती हैं, या वे पहले से तैयार किए गए प्रारूप का उपयोग नहीं करते हैं, हमेशा स्रोत रूप में वितरित किए जाते हैं और मक्खी (पायथन, पर्ल) पर संकलित करते हैं।
यह इंटरफेस घोषित करने का पूर्व तरीका है। आपने हेडर फ़ाइल में इंटरफ़ेस (विधि घोषणाओं), और कार्यान्वयन को सीपीपी में डाल दिया। आपके पुस्तकालय का उपयोग करने वाले अनुप्रयोगों को केवल इंटरफ़ेस को जानना होगा, जिसे वे #include के माध्यम से एक्सेस कर सकते हैं।
अक्सर आप पूरे कोड को शिप किए बिना एक इंटरफ़ेस की परिभाषा रखना चाहेंगे। उदाहरण के लिए, यदि आपके पास एक साझा पुस्तकालय है, तो आप इसके साथ एक हेडर फ़ाइल को शिप करेंगे, जो साझा लाइब्रेरी में उपयोग किए जाने वाले सभी कार्यों और प्रतीकों को परिभाषित करता है। हेडर फ़ाइलों के बिना, आपको स्रोत को शिप करना होगा।
एक ही परियोजना के भीतर, हेडर फ़ाइलों का उपयोग किया जाता है, कम से कम दो उद्देश्यों के लिए IMHO:
MadKeithV के उत्तर का जवाब देते हुए ,
यह निर्भरता कम करता है ताकि कोड का उपयोग करने वाले कोड को कार्यान्वयन के सभी विवरणों और किसी अन्य वर्ग / हेडर को केवल उसी के बारे में जानने की आवश्यकता न हो। यह संकलन समय को कम करेगा, और कार्यान्वयन में कुछ बदलाव होने पर आवश्यक पुन: प्राप्ति की मात्रा भी।
एक और कारण यह है कि एक हेडर प्रत्येक वर्ग को एक अद्वितीय आईडी देता है।
तो अगर हमारे पास ऐसा कुछ है
class A {..};
class B : public A {...};
class C {
include A.cpp;
include B.cpp;
.....
};
हम त्रुटि करेंगे, जब हम प्रोजेक्ट बनाने की कोशिश करते हैं, क्योंकि ए बी का हिस्सा है, हेडर के साथ हम इस तरह के सिरदर्द से बचेंगे ...