C++ हेडर फ़ाइल में कार्यान्वयन कैसे शामिल हो सकता है?




header-files (5)

ठीक है, किसी भी तरह से C / C ++ विशेषज्ञ नहीं है, लेकिन मुझे लगा कि हेडर फ़ाइल का कार्य फ़ंक्शन को घोषित करना है, फिर C / CPP फ़ाइल को कार्यान्वयन को परिभाषित करना था।

हालांकि, आज रात कुछ C ++ कोड की समीक्षा करते हुए, मुझे यह एक क्लास की हेडर फ़ाइल में मिला ...

public:
    UInt32 GetNumberChannels() const { return _numberChannels; } // <-- Huh??

private:
    UInt32 _numberChannels;

तो हेडर में कार्यान्वयन क्यों है? क्या इसे const कीवर्ड के साथ करना है? क्या वह इनलाइन क्लास विधि है? सीपीपी फ़ाइल में कार्यान्वयन को परिभाषित करने के लिए इस तरह से करने का लाभ / बिंदु क्या है?


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

हालांकि, एक अपवाद है। यदि आप किसी फ़ंक्शन को इनलाइन होने की घोषणा करते हैं, तो यह एक-परिभाषा-नियम से मुक्त है। यह वही है जो यहां हो रहा है, क्योंकि एक वर्ग परिभाषा के अंदर परिभाषित सदस्य फ़ंक्शन अंतर्निहित रूप से इनलाइन हैं।

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

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


जहां तक ​​मुझे पता है, दो तरह के तरीके हैं, जिन्हें हेडर फाइल के अंदर सुरक्षित रूप से लागू किया जा सकता है।

  • इनलाइन विधियों - उनके कार्यान्वयन को उन स्थानों पर कॉपी किया जाता है, जहां उनका उपयोग किया जाता है, इसलिए डबल-डेफिनिशन लिंकर त्रुटियों के साथ कोई समस्या नहीं है;
  • टेम्पलेट के तरीके - वे वास्तव में टेम्पलेट तात्कालिकता के क्षण में संकलित किए जाते हैं (जैसे। जब कोई टेम्पलेट के स्थान पर एक प्रकार का इनपुट करता है), तो फिर से डबल-डेफिनिशन समस्या की संभावना नहीं है।

मुझे विश्वास है, आपका उदाहरण पहले मामले में फिट बैठता है।


ठीक है, किसी भी तरह से C / C ++ विशेषज्ञ नहीं है, लेकिन मुझे लगा कि हेडर फ़ाइल का कार्य फ़ंक्शन को घोषित करना है, फिर C / CPP फ़ाइल को कार्यान्वयन को परिभाषित करना था।

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

इसलिए, उदाहरण के लिए, यदि आपके पास निम्नलिखित फाइलें हैं:

Foo.h:

#ifndef FooH
#define FooH

class Foo
{
public:
    UInt32 GetNumberChannels() const;

private:
    UInt32 _numberChannels;
};

#endif

Foo.cpp:

#include "Foo.h"

UInt32 Foo::GetNumberChannels() const
{
    return _numberChannels;
}

Bar.cpp:

#include "Foo.h"

Foo f;
UInt32 chans = f.GetNumberChannels();

पूर्वप्रक्रमक Foo.cpp और Bar.cpp को अलग-अलग पार्स करता है और निम्नलिखित कोड उत्पन्न करता है जो संकलक तब पार्स करता है:

Foo.cpp:

class Foo
{
public:
    UInt32 GetNumberChannels() const;

private:
    UInt32 _numberChannels;
};

UInt32 Foo::GetNumberChannels() const
{
    return _numberChannels;
}

Bar.cpp:

class Foo
{
public:
    UInt32 GetNumberChannels() const;

private:
    UInt32 _numberChannels;
};

Foo f;
UInt32 chans = f.GetNumberChannels();

Bar.cpp Bar.obj में संकलित करता है और इसमें Foo::GetNumberChannels() कॉल करने के लिए एक संदर्भ होता है। Foo.cpp Foo.obj में संकलित करता है और इसमें Foo::GetNumberChannels() का वास्तविक कार्यान्वयन शामिल है Foo::GetNumberChannels() । संकलन के बाद, लिंकर फिर .obj फ़ाइलों से मेल खाता है और उन्हें अंतिम निष्पादन योग्य बनाने के लिए एक साथ जोड़ता है।

तो हेडर में कार्यान्वयन क्यों है?

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

क्या इसे कॉन्स्टेबल कीवर्ड के साथ करना है?

सं। const कीवर्ड केवल कंपाइलर को इंगित करता है कि विधि उस वस्तु की स्थिति को नहीं बदलेगी, जिसे रनटाइम पर कॉल किया जा रहा है।

सीपीपी फ़ाइल में कार्यान्वयन को परिभाषित करने के लिए इस तरह से करने का लाभ / बिंदु क्या है?

जब प्रभावी ढंग से उपयोग किया जाता है, तो यह कंपाइलर को आमतौर पर तेज और बेहतर अनुकूलित मशीन कोड का उत्पादन करने की अनुमति देता है।


सादे सी में भी, हेडर फ़ाइल में कोड डालना संभव है। यदि आप ऐसा करते हैं, तो आपको आमतौर पर इसे static या अन्य घोषित करने की आवश्यकता होती है। एक ही हेडर सहित कई .c फाइलें "बहुधा परिभाषित फ़ंक्शन" त्रुटि का कारण बनेंगी।

पूर्वप्रक्रमक में शामिल फ़ाइल शामिल होती है, इसलिए शामिल फ़ाइल में कोड स्रोत फ़ाइल का हिस्सा बन जाता है (कम से कम कंपाइलर के दृष्टिकोण से)।

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

यह एक कंप्यूटर भाषा बनाना संभव है जिसमें हेडर फ़ाइल / स्रोत फ़ाइल भेद नहीं है, लेकिन बस वास्तविक "मॉड्यूल" है जिसे कंपाइलर समझता है। (C ++ ने ऐसा नहीं किया; वे सिर्फ स्रोत फ़ाइलों के सफल सी मॉडल के शीर्ष पर बने हैं और पाठयक्रम में हेडर फाइलें शामिल हैं।) यदि स्रोत फाइलें मॉड्यूल हैं, तो संकलक के लिए मॉड्यूल से कोड निकालना और फिर संभव होगा। उस कोड को इनलाइन करें। लेकिन C ++ ने जिस तरह से इसे लागू किया है वह सरल है।


क्लास हेडर फ़ाइल में कार्यान्वयन को ध्यान में रखते हुए काम करता है, क्योंकि मुझे यकीन है कि आपको पता है कि आपने अपना कोड संकलित किया है। const कीवर्ड यह सुनिश्चित करता है कि आप किसी भी सदस्य को न बदलें, यह विधि कॉल की अवधि के लिए उदाहरण को immutable रखता है।





header-files