C में, आप हेडर फ़ाइल के अंदर फ़ंक्शन की परिभाषा / कार्यान्वयन नहीं कर सकते हैं। हालाँकि, C ++ में आप हेडर फ़ाइल के अंदर पूर्ण विधि कार्यान्वयन कर सकते हैं। व्यवहार अलग क्यों है?
C में, आप हेडर फ़ाइल के अंदर फ़ंक्शन की परिभाषा / कार्यान्वयन नहीं कर सकते हैं। हालाँकि, C ++ में आप हेडर फ़ाइल के अंदर पूर्ण विधि कार्यान्वयन कर सकते हैं। व्यवहार अलग क्यों है?
जवाबों:
C में, यदि आप किसी हेडर फ़ाइल में किसी फ़ंक्शन को परिभाषित करते हैं, तो वह फ़ंक्शन प्रत्येक मॉड्यूल में दिखाई देगा जो संकलित है जिसमें उस हेडर फ़ाइल शामिल है, और फ़ंक्शन के लिए एक सार्वजनिक प्रतीक निर्यात किया जाएगा। इसलिए यदि फंक्शन एडिटअप को हेडर.एच में परिभाषित किया गया है, और foo.c और bar.c दोनों में हेडर शामिल हैं। h, तो foo.o और bar.o दोनों में एडिटअप की प्रतियां शामिल होंगी।
जब आप उन दो ऑब्जेक्ट फ़ाइलों को एक साथ लिंक करने के लिए जाते हैं, तो लिंकर देखेंगे कि प्रतीक एडिटअप को एक से अधिक बार परिभाषित किया गया है, और इसे अनुमति नहीं देगा।
यदि आप फ़ंक्शन को स्थिर होने की घोषणा करते हैं, तो कोई प्रतीक निर्यात नहीं किया जाएगा। ऑब्जेक्ट फ़ाइलें foo.o और bar.o दोनों में अभी भी फ़ंक्शन के लिए कोड की अलग-अलग प्रतियां होंगी, और वे उनका उपयोग करने में सक्षम होंगे, लेकिन लिंकर फ़ंक्शन की किसी भी प्रतिलिपि को देखने में सक्षम नहीं होगा, इसलिए यह शिकायत नहीं करेंगे। बेशक, कोई अन्य मॉड्यूल फ़ंक्शन को देखने में सक्षम नहीं होगा, या तो। और आपका कार्यक्रम एक ही फ़ंक्शन की दो समान प्रतियों के साथ फूला हुआ होगा।
यदि आप केवल हेडर फ़ाइल में फ़ंक्शन की घोषणा करते हैं, लेकिन इसे परिभाषित नहीं करते हैं, और फिर इसे केवल एक मॉड्यूल में परिभाषित करते हैं, तो लिंकर फ़ंक्शन की एक प्रति देखेगा, और आपके प्रोग्राम का प्रत्येक मॉड्यूल इसे देख सकेगा और इसका इस्तेमाल करें। और आपके संकलित कार्यक्रम में फ़ंक्शन की सिर्फ एक प्रति शामिल होगी।
तो, आप C में हेडर फाइल में फंक्शन डेफिनिशन रख सकते हैं, यह सिर्फ खराब स्टाइल, खराब फॉर्म और ऑल-अराउंड बैड आइडिया है।
("घोषित" करके, मेरा मतलब है कि एक निकाय के बिना एक फ़ंक्शन प्रोटोटाइप प्रदान करना; "परिभाषित" से मेरा मतलब है कि फ़ंक्शन बॉडी का वास्तविक कोड प्रदान करता है; यह मानक सी शब्दावली है।)
#ifndef HEADER_H
है जिसे रोकने के लिए माना जाता है?
C और C ++ इस संबंध में बहुत समान व्यवहार करते हैं - आप inline
हेडर में कार्य कर सकते हैं । C ++ में, किसी भी विधि जिसका शरीर वर्ग परिभाषा के अंदर है, अंतर्निहित है inline
। यदि आप C में समान करना चाहते हैं, तो फ़ंक्शन घोषित करें static inline
।
static inline
" ... और आपके पास अभी भी इसका उपयोग करने वाली प्रत्येक अनुवाद इकाई में फ़ंक्शन की कई प्रतियां होंगी। गैर- static
inline
फ़ंक्शन के साथ C ++ में आपके पास केवल एक प्रति होगी। वास्तव में सी में हेडर में कार्यान्वयन है, तो आपको 1) कार्यान्वयन को चिह्नित करना होगा inline
(जैसे inline void func(){do_something();}
), और 2) वास्तव में कहते हैं कि यह फ़ंक्शन कुछ विशेष अनुवाद इकाई (जैसे void func();
) में होगा।
हेडर फ़ाइल की अवधारणा को थोड़ा स्पष्टीकरण की आवश्यकता है:
या तो आप संकलक की कमांड लाइन पर एक फ़ाइल देते हैं, या '#include' करते हैं। अधिकांश संकलक एक कमांड फ़ाइल को एक्सटेंशन फ़ाइल के रूप में विस्तार सी, सी, सीपीपी, सी ++, आदि के साथ स्वीकार करते हैं। हालाँकि, वे आम तौर पर एक स्रोत फ़ाइल के लिए किसी भी मनमाना एक्सटेंशन के उपयोग को सक्षम करने के लिए एक कमांड-लाइन विकल्प भी शामिल करते हैं।
आम तौर पर कमांड लाइन पर दी गई फ़ाइल को 'सोर्स' कहा जाता है, और इसमें शामिल एक को 'हेडर' कहा जाता है।
प्रीप्रोसेसर कदम वास्तव में उन सभी को ले जाता है और सब कुछ कंपाइलर को एक बड़ी फाइल की तरह दिखाई देता है। हेडर या स्रोत में क्या था वास्तव में इस बिंदु पर प्रासंगिक नहीं है। आमतौर पर एक कंपाइलर का एक विकल्प होता है जो इस चरण के आउटपुट को दिखा सकता है।
इसलिए कंपाइलर कमांड लाइन पर दी गई प्रत्येक फाइल के लिए, कंपाइलर को एक बड़ी फाइल दी जाती है। इसमें कोड / डेटा हो सकता है जो मेमोरी पर कब्जा कर लेगा और / या अन्य फाइलों से संदर्भित होने के लिए एक प्रतीक बना देगा। अब इनमें से प्रत्येक एक 'ऑब्जेक्ट' छवि उत्पन्न करेगा। लिंकर एक 'डुप्लिकेट प्रतीक' दे सकता है यदि एक ही प्रतीक दो से अधिक ऑब्जेक्ट फ़ाइलों में एक साथ पाया जाता है। शायद यही कारण है; यह हेडर फ़ाइल में कोड डालने की सलाह नहीं दी गई है, जो ऑब्जेक्ट फ़ाइल में प्रतीक बना सकता है।
'इनलाइन' आमतौर पर इनलेटेड होते हैं .. लेकिन डिबगिंग के दौरान वे इनबिल्ड नहीं हो पाते हैं। तो क्यों लिंकर गुणा परिभाषित त्रुटियों को नहीं देता है? सरल ... ये 'कमजोर' प्रतीक हैं, और जब तक सभी वस्तुओं से कमजोर प्रतीक के लिए सभी डेटा / कोड समान आकार और सामग्री नहीं होते हैं, तब तक लिंक एक प्रतिलिपि और अन्य वस्तुओं से प्रतिलिपि छोड़ देगा। यह काम करता हैं।
C ++ मानक उद्धरण
सी ++ 17 N4659 मानक प्रारूप 10.1.6 "इनलाइन विनिर्देशक" का कहना है कि तरीकों परोक्ष इनलाइन हैं:
4 एक वर्ग परिभाषा के भीतर परिभाषित एक फ़ंक्शन एक इनलाइन फ़ंक्शन है।
और फिर नीचे हम देखते हैं कि इनलाइन विधियां न केवल कर सकती हैं, बल्कि सभी अनुवाद इकाइयों पर परिभाषित की जानी चाहिए:
6 प्रत्येक इनलाइन यूनिट में एक इनलाइन फ़ंक्शन या वेरिएबल को परिभाषित किया जाएगा जिसमें यह ओड-यूज़ किया गया है और हर मामले में इसकी परिभाषा समान होगी (6.2)।
यह भी स्पष्ट रूप से 12.2.1 "सदस्य कार्यों" पर एक नोट में उल्लिखित है:
1 एक सदस्य फ़ंक्शन को उसकी कक्षा परिभाषा में परिभाषित किया जा सकता है (11.4), जिस स्थिति में यह एक इनलाइन सदस्य फ़ंक्शन है (10.1.6) [...]
3 [नोट: एक कार्यक्रम में एक गैर-इनलाइन सदस्य फ़ंक्शन की अधिकांश एक परिभाषा हो सकती है। एक कार्यक्रम में एक से अधिक इनलाइन सदस्य फ़ंक्शन परिभाषा हो सकती है। 6.2 और 10.1.6 देखें। - अंतिम नोट]
जीसीसी 8.3 कार्यान्वयन
main.cpp
struct MyClass {
void myMethod() {}
};
int main() {
MyClass().myMethod();
}
संकलन और देखने के प्रतीक:
g++ -c main.cpp
nm -C main.o
उत्पादन:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 W MyClass::myMethod()
U __stack_chk_fail
0000000000000000 T main
फिर हम देखते हैं man nm
कि MyClass::myMethod
प्रतीक को ELF ऑब्जेक्ट फ़ाइलों पर कमजोर के रूप में चिह्नित किया गया है, जिसका अर्थ है कि यह कई ऑब्जेक्ट फ़ाइलों पर दिखाई दे सकता है:
"डब्ल्यू" "डब्ल्यू" प्रतीक एक कमजोर प्रतीक है जिसे विशेष रूप से एक कमजोर वस्तु प्रतीक के रूप में टैग नहीं किया गया है। जब एक कमजोर परिभाषित प्रतीक सामान्य परिभाषित प्रतीक से जुड़ा होता है, तो सामान्य परिभाषित प्रतीक का उपयोग बिना किसी त्रुटि के किया जाता है। जब एक कमजोर अपरिभाषित प्रतीक जुड़ा होता है और प्रतीक को परिभाषित नहीं किया जाता है, तो त्रुटि के बिना सिस्टम-विशिष्ट तरीके से प्रतीक का मूल्य निर्धारित किया जाता है। कुछ प्रणालियों पर, अपरकेस यह दर्शाता है कि एक डिफ़ॉल्ट मान निर्दिष्ट किया गया है।
संभवतः उसी कारण से कि आपको जावा में वर्ग परिभाषा के अंदर पूर्ण विधि कार्यान्वयन को लागू करना होगा।
हो सकता है कि वे समान रूप से स्क्वीजी ब्रैकेट और कई समान कीवर्ड के साथ दिखें, लेकिन वे अलग-अलग भाषाएं हैं।