2017-04-18 9 views
7

अगर मैं निम्नलिखित कोड के साथ एक फ़ाइल foo.cpp है:सीपीपी फाइलों में एक वर्ग को परिभाषित करने से लिंकर त्रुटि क्यों नहीं होती है?

class Foo { 
}; 

class Foo { 
}; 

int main() { 
    return 0; 
} 

तो स्वाभाविक रूप से मैं error: redefinition of 'Foo' मिलता है। हालांकि, अगर मैं foo.cpp

class Foo { 
}; 

int main() { 
    return 0; 
} 

और bar.cpp साथ

class Foo { 
}; 

साथ class Foo के बावजूद कार्यक्रम भर में दो बार परिभाषित किया जा रहा है, यह पूरी बात ठीक संकलित करता है।

यदि मैंने वैश्विक नामस्थान में दोनों फ़ाइलों में int something; डाल दिया था, तो मुझे एक लिंकर त्रुटि (विशेष रूप से duplicate symbol) मिल गई होगी, लेकिन कक्षा परिभाषाओं के लिए, यह कभी नहीं होता है।

मुझे पता है कि int doIt(); जैसे फ़ंक्शन घोषणाएं दोनों सीपीपी फ़ाइलों में डुप्लिकेट की जा सकती हैं, लेकिन परिभाषा, उदा। int doIt() {} नहीं हो सकता है। अब पहली कंपाइलर त्रुटि में (class Foo{}; एक सीपीपी फ़ाइल में दो बार), यह redefinition of foo कहा गया, इसलिए class Foo{}; एक परिभाषा है। तो क्यों, कार्यों के विपरीत, क्या इसे एक कार्यक्रम में दो बार परिभाषित किया जा सकता है?

संपादित करें:this website के अनुसार, नामित वर्गों में बाहरी संबंध है। तो फिर दोनों सीपीपी फाइलों में class Foo के बीच कोई संघर्ष क्यों नहीं है?

EDIT2: उपरोक्त वेबसाइट के अनुसार, न केवल नामित वर्गों के बाहरी संबंध हैं, बल्कि यह स्थिर सदस्य भी हैं। फिर भी यह सब ठीक संकलित:

foo.cpp:

class Foo { 
public: 
    int foo(); 
    static int x; 
}; 

int Foo::foo() { 
    return 5; 
} 

int main() { 
    return 0; 
} 

bar.cpp:

class Foo { 
public: 
    int foo(int); 
    static bool x; 
}; 

int Foo::foo(int i) { 
    return i * 2; 
} 

इतना ही नहीं Foo::foo एक अलग हस्ताक्षर के साथ नए सिरे से परिभाषित किया गया है, लेकिन Foo::x एक अलग प्रकार की है। इन दोनों में बाहरी संबंध होना चाहिए, फिर भी यह कोड ए-ओके है।

+0

वह कंपाइलर क्या है? –

+3

इस प्रश्न पर चर्चा की गई है [http://stackoverflow.com/questions/6465325/do-classes-have-external-linkage), tl; dr; जब एक ही नाम के साथ कक्षाएं (एनम्स, यूनियन, इत्यादि) अलग-अलग अनुवाद इकाइयों में अलग-अलग परिभाषित की जाती हैं, तो उनके पास आंतरिक या कोई संबंध नहीं होता है, इसलिए कोई त्रुटि नहीं होती है। ध्यान दें, स्वीकार किए गए उत्तर में गलत है और अधिक अपवर्तित वाला एक सही है। – VTT

+0

@ ΦXocę 웃 Пepeúpa ツ ऐप्पल एलएलवीएम संस्करण 7.3.0 (क्लैंग -703.0.31) – rcplusplus

उत्तर

2

, कि स्पष्ट रूप से ओडीआर द्वारा अनुमति दी गई है, क्योंकि अन्यथा भाषा बेकार हो सकता है।

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

तीसरे प्रश्न के बारे में, static डेटा सदस्यों के साथ, वे घोषणाएं हैं, परिभाषाएं नहीं। उन्हें एक अद्वितीय परिभाषा की आवश्यकता है, जैसे:

TheType ClassName::VariableName; 

जो आम तौर पर साथ में .cpp फ़ाइल में रखे जाते हैं।

conststatic इनलाइन प्रारंभकर्ताओं के साथ डेटा सदस्यों के साथ अपवाद है।


ओडीआर = एक परिभाषा नियम
टीयू = अनुवाद यूनिट
NDR = नहीं निदान आवश्यक

NDR के बारे में एक नोट; कंपाइलर के लिए कुछ प्रकार की त्रुटियां मुश्किल होती हैं, और आमतौर पर मानक की आवश्यकता नहीं होती है कि संकलक उन मामलों में नैदानिक ​​(यानी चेतावनी या त्रुटि) जारी करें। उपकरण हैं, जैसे कि CppLint, जो संकलक त्रुटियों में से कई त्रुटियों का पता लगा सकते हैं। जब ओडीआर-उल्लंघनों की बात आती है, तो आमतौर पर उन शीर्षकों में परिभाषित करके से बचा जा सकता है।

+0

कोई डायग्नोस्टिक आवश्यक नहीं है, इसका क्या अर्थ है? – rcplusplus

+0

@rcplusplus मेरा अद्यतन उत्तर देखें –

-1

आम तौर पर कक्षाओं को हेडर फ़ाइलों में परिभाषित किया जाता है और यदि आप ऐसा करते हैं तो हेडर फ़ाइल सहित आपको त्रुटियां मिलेंगी।

+4

सीखता हूं जो प्रश्न का उत्तर नहीं देता है। –

+1

यह प्रश्न का उत्तर देता है: यदि इसने एक लिंकर त्रुटि उत्पन्न की है तो एक इकाई में कई इकाइयों के साथ कक्षाओं का उपयोग करना असंभव होगा, इसलिए यह कोई त्रुटि उत्पन्न नहीं कर सकता है! –

+0

मुझे पता था कि हेडर फाइलें ऐसा करती हैं इसलिए मैंने पूछा। यदि आप एक हेडर फ़ाइल में एक चर डालते हैं तो आपको कई परिभाषा त्रुटि मिलती है, फिर भी कक्षा के साथ नहीं। – rcplusplus

0

प्रत्येक सीपीपी फ़ाइल स्वतंत्र रूप से सभी परिभाषाओं को संकलित करती है। कंपाइलर के परिप्रेक्ष्य से उन्हें दो अलग-अलग सीपीपी फाइलों में दो बार परिभाषित करने से कोई फर्क नहीं पड़ता है, या एक बार साझा में शामिल किया जाता है।

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

+0

अंतिम वाक्य पूरी तरह से सही नहीं है, क्योंकि आप मैन्युअल रूप से 'इनलाइन' के रूप में कार्यों को चिह्नित कर सकते हैं और उन्हें कक्षा निकाय के बाहर लागू कर सकते हैं। – chtz

1

सी ++ में "One Definition Rule" के कारण। आप एक अनुवाद इकाई के भीतर कक्षा को फिर से परिभाषित नहीं कर सकते हैं, लेकिन वर्ग प्रत्येक अनुवाद इकाई में परिभाषित (और होना चाहिए) जो इसका उपयोग करता है। यही कारण है कि सी/सी ++ में हेडर और # शामिल हैं। आपको हेडर में क्लास परिभाषा रखना चाहिए और इसे प्रत्येक .cpp में शामिल करना चाहिए जो इसका उपयोग करता है। यह ओडीआर उल्लंघन को रोकता है लेकिन तकनीकी रूप से # अंतर्निहित का उपयोग प्रत्येक .cpp फ़ाइल में कक्षा परिभाषा के समान होता है (प्रीप्रोसेसर बस शामिल फ़ाइल को संकलित फ़ाइल का एक हिस्सा बनाता है)।

यह भी ध्यान दें कि definitiondeclaration से C++ में भिन्न है।

अद्यतन। अपने नए उदाहरण में स्थिर सदस्य चर के साथ आप बिना परिभाषा केवल घोषणाओं है:

class Foo { 
public: 
    static int x; // <-- variable declaration 
}; 
int Foo::x; // <-- variable definition 

एक अनुवाद इकाई के भीतर घोषणाओं नहीं बल्कि परिभाषाओं नकल कर सकते हैं।

प्रकारों (कक्षाओं समेत) की परिभाषा अलग-अलग अनुवाद इकाइयों, कार्यों और बाह्य संबंधों के साथ चर में डुप्लिकेट की जा सकती है - नहीं।

एक ही नाम के साथ दो अनुवाद इकाइयों में दो प्रकार की परिभाषा, लेकिन विभिन्न संरचना ओडीआर उल्लंघन है जो आमतौर पर निदानकर्ता नहीं निदान कर सकते हैं - आपका प्रोग्राम गलत है लेकिन सभी "बस ठीक बनाते हैं"।

अनुवाद इकाई प्रीप्रोसेसिंग के बाद कंपाइलर इनपुट के रूप में मिलता है। बजना या जीसीसी का उपयोग करके आप इसे इस तरह प्राप्त कर सकते हैं: कई टीयू के में एक जैसी परिभाषा के साथ अपने पहले प्रश्न के बारे में

$ clang -E foo.cpp >foo.ii 
+0

परिभाषा और घोषणा मैं सहज महसूस करता हूं, यह सिर्फ वर्ग परिभाषा नहीं है जो मुझे लगता है कि यह किसी कारण से है। ओडीआर के मुताबिक, कई फाइलों में एक वर्ग को फिर से परिभाषित करना ठीक है _only if_ परिभाषा वही बना है। फिर भी मेरे संपादन में, मैंने कोड दिखाया कि (मुझे विश्वास है) इसका उल्लंघन करता है। – rcplusplus

+0

@rcplusplus हाँ, आप मानक लिख सकते हैं जो मानक का उल्लंघन करते हैं और संकलक से पूर्ण चुप्पी प्राप्त करते हैं, यदि मानक निदान को अनिवार्य नहीं करता है और संकलक ने एक रिपोर्ट नहीं करना चुना है। इससे कोड कम नहीं होता है। –

संबंधित मुद्दे