c++ - सीपीपी फ़ाइलों में एक वर्ग को परिभाषित करता है, लिंकर त्रुटि क्यों नहीं पैदा करता है?




class c++11 (3)

अगर मेरे पास निम्न कोड वाला फ़ाइल है foo.cpp :

class Foo {
};

class Foo {
};

int main() {
    return 0;
}

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

class Foo {
};

int main() {
    return 0;
}

और साथ bar.cpp

class Foo {
};

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

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

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

संपादित करें: इस वेबसाइट के अनुसार, नामांकित वर्गों में बाहरी संबंध हैं। तो फिर क्यों सीपी फ़ाइलों दोनों में 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 एक अलग प्रकार का है इन दोनों में बाहरी संबंध होना चाहिए, फिर भी यह कोड ए-ओक है।


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


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

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


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

इसके अलावा ध्यान दें कि definition C ++ में declaration से कैसे अलग है

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

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

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

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

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

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

$ clang -E foo.cpp >foo.ii




linker