मैं आगे की घोषणा का उपयोग कब कर सकता हूं?


602

मैं उस परिभाषा की तलाश कर रहा हूं जब मुझे किसी अन्य श्रेणी की हेडर फ़ाइल में एक वर्ग की घोषणा करने की अनुमति दी जाती है:

क्या मैंने इसे आधार वर्ग के लिए करने की अनुमति दी है, एक सदस्य के रूप में रखी गई कक्षा के लिए, संदर्भ के आधार पर सदस्य समारोह के लिए पारित एक वर्ग के लिए, आदि?


14
मैं चाहता हूं कि इसका नाम बदलकर " मुझे कब होना चाहिए ", और उत्तर उचित रूप से अपडेट किए गए ...
डेवॉर्ड

12
@deworde जब आप कहते हैं कि "कब" आपको राय मांगनी चाहिए।
AturSams

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

@OhadSchneider व्यावहारिक दृष्टिकोण से, मैं हेडर का एक बड़ा प्रशंसक नहीं हूं कि मेरे। ÷
deworde

मूल रूप से हमेशा उन्हें उपयोग करने के लिए आपको एक अलग हेडर शामिल करने की आवश्यकता होती है (आगे चलकर कंस्ट्रक्टर पैरामीटर की
घोषणा

जवाबों:


962

अपने आप को संकलक की स्थिति में रखें: जब आप आगे एक प्रकार की घोषणा करते हैं, तो सभी संकलक जानते हैं कि यह प्रकार मौजूद है; यह अपने आकार, सदस्यों या विधियों के बारे में कुछ नहीं जानता है। यही कारण है कि इसे अपूर्ण प्रकार कहा जाता है । इसलिए, आप किसी सदस्य या बेस क्लास को घोषित करने के लिए टाइप का उपयोग नहीं कर सकते, क्योंकि कंपाइलर को टाइप के लेआउट को जानना होगा।

आगे की घोषणा को मानते हुए।

class X;

यहाँ आप क्या कर सकते हैं और क्या नहीं कर सकते।

अपूर्ण प्रकार से आप क्या कर सकते हैं:

  • किसी सदस्य को पॉइंटर या अधूरे प्रकार के संदर्भ के रूप में घोषित करें:

    class Foo {
        X *p;
        X &r;
    };
  • अधूरे प्रकारों को स्वीकार / वापस करने वाले कार्य या विधियाँ घोषित करें :

    void f1(X);
    X    f2();
  • कार्यों या विधियों को परिभाषित करें जो अपूर्ण प्रकार (लेकिन इसके सदस्यों का उपयोग किए बिना) को संकेत / वापसी को स्वीकार करते हैं / करती हैं:

    void f3(X*, X&) {}
    X&   f4()       {}
    X*   f5()       {}

अपूर्ण प्रकार से आप क्या नहीं कर सकते हैं:

  • इसे बेस क्लास की तरह इस्तेमाल करें

    class Foo : X {} // compiler error!
  • सदस्य घोषित करने के लिए इसका उपयोग करें:

    class Foo {
        X m; // compiler error!
    };
  • इस प्रकार का उपयोग करके कार्यों या विधियों को परिभाषित करें

    void f1(X x) {} // compiler error!
    X    f2()    {} // compiler error!
  • वास्तव में इसके तरीकों या क्षेत्रों का उपयोग करें, वास्तव में अपूर्ण प्रकार के साथ एक चर को कम करने की कोशिश कर रहा है

    class Foo {
        X *m;            
        void method()            
        {
            m->someMethod();      // compiler error!
            int i = m->someField; // compiler error!
        }
    };

जब टेम्प्लेट की बात आती है, तो कोई पूर्ण नियम नहीं है: क्या आप एक अपूर्ण प्रकार का उपयोग कर सकते हैं क्योंकि टेम्पलेट पैरामीटर उस प्रकार पर निर्भर करता है जिस तरह से टेम्पलेट का उपयोग किया जाता है।

उदाहरण के लिए, std::vector<T>इसके पैरामीटर की आवश्यकता एक पूर्ण प्रकार है, जबकि boost::container::vector<T>ऐसा नहीं है। कभी-कभी, एक पूर्ण प्रकार की आवश्यकता केवल तभी होती है जब आप कुछ सदस्य कार्यों का उपयोग करते हैं; उदाहरण के लिए यह मामला हैstd::unique_ptr<T>

एक अच्छी तरह से प्रलेखित टेम्पलेट को अपने दस्तावेज़ों में अपने मापदंडों की सभी आवश्यकताओं को इंगित करना चाहिए, जिसमें यह भी शामिल है कि उन्हें पूर्ण प्रकार की आवश्यकता है या नहीं।


4
शानदार जवाब लेकिन कृपया मुझे उस इंजीनियरिंग बिंदु के लिए नीचे देखें जिस पर मैं असहमत हूं। संक्षेप में, यदि आप उन अधूरे प्रकारों के लिए हेडर शामिल नहीं करते हैं जिन्हें आप स्वीकार करते हैं या वापस करते हैं, तो आप अपने हेडर के उपभोक्ता पर एक अदृश्य निर्भरता को लागू करते हैं, यह जानने के लिए कि उन्हें किन अन्य की आवश्यकता है।
एंडी डेंट

2
@AndyDent: सही है, लेकिन हैडर के उपभोक्ता को केवल उसके द्वारा उपयोग की जाने वाली निर्भरता (एस) को शामिल करने की आवश्यकता होती है, इसलिए यह "आप जो भी उपयोग करते हैं उसके लिए भुगतान करते हैं" के C ++ सिद्धांत का पालन करता है। लेकिन वास्तव में, यह उस उपयोगकर्ता के लिए असुविधाजनक हो सकता है जो हेडर को स्टैंडअलोन होने की उम्मीद करेगा।
ल्यूक टॉरिल

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

12
"कम्पाइलर की स्थिति में खुद को रखें" के लिए +1। मैं कल्पना करता हूँ "संकलक" की मूंछें होती हैं।
पास्कलवीकूटेन

3
@JesusChrist: बिल्कुल: जब आप किसी वस्तु को मूल्य से पास करते हैं, तो उचित स्टैक हेरफेर करने के लिए कंपाइलर को उसके आकार को जानना पड़ता है; पॉइंटर या रेफरेंस पास करते समय, कंपाइलर को ऑब्जेक्ट के आकार या लेआउट की आवश्यकता नहीं होती है, केवल एक पते का आकार (यानी पॉइंटर का आकार), जो इंगित किए गए प्रकार पर निर्भर नहीं करता है।
ल्यूक टॉरिल

45

मुख्य नियम यह है कि आप केवल उन वर्गों को अग्रेषित-घोषित कर सकते हैं जिनकी मेमोरी लेआउट (और इस प्रकार सदस्य कार्य और डेटा सदस्य) को आपके द्वारा अग्रेषित-घोषित की गई फ़ाइल में ज्ञात होने की आवश्यकता नहीं है।

यह आधार वर्गों और कुछ भी लेकिन संदर्भ और संकेत के माध्यम से उपयोग की जाने वाली कक्षाओं को नियंत्रित करेगा।


6
लगभग। आप फ़ंक्शन के प्रोटोटाइप में "सादे" (अर्थात गैर-पॉइंटर / संदर्भ) को अपूर्ण प्रकार के पैरामीटर या रिटर्न प्रकार के रूप में संदर्भित कर सकते हैं।
j_random_hacker

उन कक्षाओं के बारे में क्या जो मैं उस वर्ग के सदस्यों के रूप में उपयोग करना चाहता हूं जिन्हें मैं हेडर फ़ाइल में परिभाषित करता हूं? क्या मैं उन्हें घोषित कर सकता हूं?
इगोर ओक्स

1
हां, लेकिन उस स्थिति में आप केवल फॉरवर्ड घोषित वर्ग के लिए एक संदर्भ या सूचक का उपयोग कर सकते हैं। लेकिन यह आपको सदस्य बनाने देता है, फिर भी।
पुनर्नवीन

32

Lakos वर्ग उपयोग के बीच अंतर करता है

  1. इन-नाम-केवल (जिसके लिए एक आगे की घोषणा पर्याप्त है) और
  2. इन-आकार (जिसके लिए वर्ग परिभाषा की आवश्यकता है)।

मैंने कभी नहीं देखा कि यह अधिक स्पष्ट रूप से स्पष्ट है :)


2
केवल नाम का मतलब क्या है?
बून

4
@ बून: हिम्मत तो मैं कहता हूँ ...? यदि आप केवल वर्ग के नाम का उपयोग करते हैं ?
मार्क मुत्ज़ -

1
इसके अलावा Lakos, मार्क के लिए एक
mlvljr

28

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

उदाहरण:

struct X;              // Forward declaration of X

void f1(X* px) {}      // Legal: can always use a pointer
void f2(X&  x) {}      // Legal: can always use a reference
X f3(int);             // Legal: return value in function prototype
void f4(X);            // Legal: parameter in function prototype
void f5(X) {}          // ILLEGAL: *definitions* require complete types

19

उत्तर में से कोई भी अब तक का वर्णन नहीं करता है जब कोई वर्ग टेम्पलेट के आगे की घोषणा का उपयोग कर सकता है। तो, यहाँ यह जाता है।

एक वर्ग टेम्पलेट को आगे के रूप में घोषित किया जा सकता है:

template <typename> struct X;

स्वीकृत उत्तर की संरचना के बाद ,

यहाँ आप क्या कर सकते हैं और क्या नहीं कर सकते।

अपूर्ण प्रकार से आप क्या कर सकते हैं:

  • एक सदस्य को एक पॉइंटर होने के लिए या किसी अन्य वर्ग टेम्पलेट में अपूर्ण प्रकार के संदर्भ में घोषित करें:

    template <typename T>
    class Foo {
        X<T>* ptr;
        X<T>& ref;
    };
  • किसी सदस्य को पॉइंटर या उसकी अपूर्ण तात्कालिकताओं में से एक के संदर्भ में घोषित करें:

    class Foo {
        X<int>* ptr;
        X<int>& ref;
    };
  • फ़ंक्शन टेम्प्लेट या सदस्य फ़ंक्शन टेम्प्लेट घोषित करें जो अपूर्ण प्रकारों को स्वीकार / वापस करते हैं:

    template <typename T>
       void      f1(X<T>);
    template <typename T>
       X<T>    f2();
  • उन कार्यों या सदस्य कार्यों की घोषणा करें, जो इसकी अपूर्ण तात्कालिकताओं को स्वीकार / वापस करते हैं:

    void      f1(X<int>);
    X<int>    f2();
  • फ़ंक्शन टेम्प्लेट या सदस्य फ़ंक्शन टेम्प्लेट परिभाषित करें, जो अपूर्ण प्रकार (लेकिन इसके सदस्यों का उपयोग किए बिना) के लिए संकेत / वापसी संदर्भ / स्वीकार करते हैं:

    template <typename T>
       void      f3(X<T>*, X<T>&) {}
    template <typename T>
       X<T>&   f4(X<T>& in) { return in; }
    template <typename T>
       X<T>*   f5(X<T>* in) { return in; }
  • ऐसे कार्यों या विधियों को परिभाषित करें, जो एक अपूर्ण अपूर्णताओं (लेकिन अपने सदस्यों का उपयोग किए बिना) में से किसी एक को संदर्भित / वापसी / स्वीकार करते हैं:

    void      f3(X<int>*, X<int>&) {}
    X<int>&   f4(X<int>& in) { return in; }
    X<int>*   f5(X<int>* in) { return in; }
  • इसे अन्य टेम्पलेट क्लास के बेस क्लास के रूप में उपयोग करें

    template <typename T>
    class Foo : X<T> {} // OK as long as X is defined before
                        // Foo is instantiated.
    
    Foo<int> a1; // Compiler error.
    
    template <typename T> struct X {};
    Foo<int> a2; // OK since X is now defined.
  • इसका उपयोग किसी अन्य वर्ग टेम्पलेट के सदस्य को घोषित करने के लिए करें:

    template <typename T>
    class Foo {
        X<T> m; // OK as long as X is defined before
                // Foo is instantiated. 
    };
    
    Foo<int> a1; // Compiler error.
    
    template <typename T> struct X {};
    Foo<int> a2; // OK since X is now defined.
  • फ़ंक्शन टेम्पलेट या विधियों को इस प्रकार का उपयोग करके परिभाषित करें

    template <typename T>
      void    f1(X<T> x) {}    // OK if X is defined before calling f1
    template <typename T>
      X<T>    f2(){return X<T>(); }  // OK if X is defined before calling f2
    
    void test1()
    {
       f1(X<int>());  // Compiler error
       f2<int>();     // Compiler error
    }
    
    template <typename T> struct X {};
    
    void test2()
    {
       f1(X<int>());  // OK since X is defined now
       f2<int>();     // OK since X is defined now
    }

अपूर्ण प्रकार से आप क्या नहीं कर सकते हैं:

  • बेस क्लास के रूप में इसके इंस्टेंटीएशन में से एक का उपयोग करें

    class Foo : X<int> {} // compiler error!
  • किसी सदस्य को घोषित करने के लिए इसके एक इंस्टेंटिएशन का उपयोग करें:

    class Foo {
        X<int> m; // compiler error!
    };
  • इसकी एक तात्कालिकता का उपयोग करके कार्यों या विधियों को परिभाषित करें

    void      f1(X<int> x) {}            // compiler error!
    X<int>    f2() {return X<int>(); }   // compiler error!
  • वास्तव में अपूर्ण प्रकार के साथ एक चर को रोकने की कोशिश करते हुए, इसकी एक तात्कालिकता के तरीकों या क्षेत्रों का उपयोग करें

    class Foo {
        X<int>* m;            
        void method()            
        {
            m->someMethod();      // compiler error!
            int i = m->someField; // compiler error!
        }
    };
  • वर्ग टेम्पलेट के स्पष्ट तात्कालिकता बनाएं

    template struct X<int>;

2
"उत्तर में से कोई भी अब तक का वर्णन नहीं करता है जब कोई वर्ग टेम्पलेट की आगे की घोषणा कर सकता है।" नहीं है कि बस क्योंकि के शब्दों Xऔर X<int>बिल्कुल वैसा ही कर रहे हैं, और केवल किसी भी ठोस तरीके से आगे की घोषणा वाक्य रचना अलग है, सभी के साथ लेकिन सिर्फ ल्यूक है और लेने के लिए अपने जवाब राशि का 1 लाइन s/X/X<int>/g? क्या वाकई जरूरत है? या मैं एक छोटे से विस्तार से चूक गया है? यह संभव है, लेकिन मैंने नेत्रहीन कुछ बार तुलना की है और कोई भी नहीं देख सकता है ...
अंडरस्कोर

धन्यवाद! यह संपादित बहुमूल्य जानकारी का एक टन जोड़ता है। मुझे इसे पूरी तरह से समझने के लिए कई बार पढ़ना होगा ... या शायद प्रतीक्षा की अक्सर बेहतर रणनीति का उपयोग करें जब तक कि मैं वास्तविक कोड में बुरी तरह से भ्रमित न हो जाऊं और यहां वापस आऊं! मुझे संदेह है कि मैं विभिन्न स्थानों पर निर्भरता को कम करने के लिए इसका उपयोग करने में सक्षम हूं।
अंडरस्कोर_ड

4

उस फ़ाइल में जिसमें आप केवल पॉइंटर या संदर्भ का उपयोग किसी वर्ग के लिए करते हैं। और किसी भी सदस्य / सदस्य के कार्य को उन पॉइंटर या संदर्भ को नहीं माना जाना चाहिए।

साथ class Foo;// आगे घोषणा

हम Foo * या Foo & प्रकार के डेटा सदस्यों की घोषणा कर सकते हैं।

हम घोषणा कर सकते हैं (लेकिन परिभाषित नहीं) कार्यों के साथ तर्क, और / या वापसी मान, प्रकार फू के।

हम Foo प्रकार के स्थैतिक डेटा सदस्यों की घोषणा कर सकते हैं। ऐसा इसलिए है क्योंकि स्थिर डेटा सदस्यों को वर्ग परिभाषा के बाहर परिभाषित किया गया है।


4

मैं इसे केवल एक टिप्पणी के बजाय एक अलग उत्तर के रूप में लिख रहा हूं क्योंकि मैं ल्यूक टॉरिल के उत्तर से असहमत हूं, न कि वैधता के आधार पर बल्कि मजबूत सॉफ्टवेयर और गलत व्याख्या के खतरे के लिए।

विशेष रूप से, मेरे पास एक अनुबंध है जो आपके इंटरफ़ेस के उपयोगकर्ताओं को जानने की अपेक्षा करता है।

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

जब आप एक अपूर्ण प्रकार वापस कर रहे हैं X f2();तो आप कह रहे हैं कि आपके कॉलर के पास X का पूर्ण प्रकार विनिर्देश होना चाहिए । कॉल साइट पर LHS या अस्थायी ऑब्जेक्ट बनाने के लिए उन्हें इसकी आवश्यकता होती है।

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

class X;  // forward for two legal declarations 
X returnsX();
void XAcceptor(X);

XAcepptor( returnsX() );  // X declaration needs to be known here

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

के सिवाय

  1. यदि यह बाहरी निर्भरता वांछित व्यवहार है। सशर्त संकलन का उपयोग करने के बजाय आपके पास एक्स की घोषणा करने वाले अपने स्वयं के हेडर की आपूर्ति करने के लिए उनके लिए एक अच्छी तरह से प्रलेखित आवश्यकता हो सकती है। यह #ifdefs का उपयोग करने का एक विकल्प है और मॉक या अन्य वेरिएंट पेश करने का एक उपयोगी तरीका हो सकता है।

  2. महत्वपूर्ण अंतर कुछ टेम्प्लेट तकनीकों का होना, जहाँ आपको स्पष्ट रूप से उनसे इंट्रस्ट करने की उम्मीद नहीं है, केवल इस बात का उल्लेख किया गया है कि कोई मेरे साथ नहीं आता है।


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

3
जब आप के बारे में बात कर रहे हैं चाहिए (या नहीं होना चाहिए) आगे declarion का उपयोग करें। यह पूरी तरह से इस सवाल का मुद्दा नहीं है, हालांकि। यह तकनीकी संभावनाओं को जानने के बारे में है जब (उदाहरण के लिए) एक परिपत्र निर्भरता समस्या को तोड़ना चाहते हैं।
जॉनीजेड जूल

1
I disagree with Luc Touraille's answerइसलिए उसे एक टिप्पणी लिखें, जिसमें एक ब्लॉग पोस्ट का लिंक शामिल है यदि आपको लंबाई की आवश्यकता है। यह पूछे गए प्रश्न का उत्तर नहीं देता है। अगर हर कोई इस बारे में सवाल करता है कि एक्स कैसे एक्स के साथ असहमत जवाबों को सही ठहराने का काम करता है या ऐसी सीमाएं हैं जिनके भीतर हमें एक्स का उपयोग करने की अपनी स्वतंत्रता को रोकना चाहिए - हमारे पास लगभग कोई वास्तविक जवाब नहीं होगा।
अंडरस्कोर_ड

3

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


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

@AdrianMcCarthy सही है, और एक उचित समाधान के लिए एक अग्रगामी घोषणा शीर्ष लेख है जिसमें शीर्ष लेख द्वारा शामिल किया गया है जिसकी सामग्री इसे आगे घोषित करती है, जिसे स्वामित्व होना चाहिए / जिसे अनुरक्षित किया जाना चाहिए / भेज दिया जाना चाहिए जो उस शीर्षक का भी स्वामी हो। उदाहरण के लिए: iosfwd स्टैण्डर्ड लाइब्रेरी हेडर, जिसमें iostream कंटेंट की घोषणाएँ शामिल हैं।
टोनी डेलरो

3

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


0

जब आप कक्षा के सदस्य के रूप में अन्य प्रकार (वर्ग) का उपयोग करना चाहते हैं, तो आप आमतौर पर एक क्लास हेडर फ़ाइल में आगे की घोषणा का उपयोग करना चाहेंगे। आप हेडर फ़ाइल में आगे घोषित वर्गों के तरीकों का उपयोग नहीं कर सकते क्योंकि C ++ उस बिंदु पर उस वर्ग की परिभाषा नहीं जानता है। यह तर्क है कि आपको .cpp-files में जाना है, लेकिन यदि आप टेम्प्लेट-फ़ंक्शंस का उपयोग कर रहे हैं, तो आपको उन्हें केवल उस भाग को कम करना चाहिए जो टेम्पलेट का उपयोग करता है और उस फ़ंक्शन को हेडर में स्थानांतरित करता है।


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

0

यह लें कि आगे की घोषणा को आपके कोड को संकलन (obj बनाया गया है) मिलेगा। हालाँकि, जब तक कि परिभाषाएँ नहीं मिल जाती हैं तब तक (exe निर्माण) को जोड़ना सफल नहीं होगा।


2
कभी 2 लोगों ने इसे क्यों उकसाया? आप इस बारे में बात नहीं कर रहे हैं कि प्रश्न किस बारे में बात कर रहा है। आप सामान्य मतलब है - आगे नहीं - कार्यों की घोषणा । सवाल कक्षाओं के अग्रेषण-घोषणा के बारे में है । जैसा कि आपने कहा था "आगे की घोषणा आपके कोड को संकलित करने के लिए मिल जाएगी", मुझे एक एहसान करें: संकलन करें class A; class B { A a; }; int main(){}, और मुझे बताएं कि यह कैसे जाता है। बेशक यह संकलन नहीं करेगा। सभी उचित जवाब यहां क्यों और सटीक, सीमित संदर्भों, जिसमें आगे की घोषणा की व्याख्या है मान्य। इसके बजाय आपने इसके बारे में कुछ अलग तरह से लिखा है।
अंडरस्कोर_ड

0

मैं सिर्फ एक महत्वपूर्ण बात जोड़ना चाहता हूं जो आप एक अग्रेषित वर्ग के साथ कर सकते हैं जो ल्यूक टॉरिल के उत्तर में उल्लेखित नहीं है।

अपूर्ण प्रकार से आप क्या कर सकते हैं:

फ़ंक्शंस या विधियों को परिभाषित करें जो अपूर्ण प्रकार को स्वीकार / वापसी / संदर्भ करते हैं और उस बिंदु / संदर्भ को किसी अन्य फ़ंक्शन को अग्रेषित करते हैं।

void  f6(X*)       {}
void  f7(X&)       {}
void  f8(X* x_ptr, X& x_ref) { f6(x_ptr); f7(x_ref); }

एक मॉड्यूल एक अन्य घोषित मॉड्यूल के आगे घोषित वर्ग के ऑब्जेक्ट से गुजर सकता है।


"एक अग्रेषित वर्ग" और "एक आगे घोषित वर्ग" दो बहुत अलग चीजों का संदर्भ देने के लिए गलत हो सकता है। आपने जो कुछ भी लिखा है, वह सीधे ल्यूक के उत्तर में निहित अवधारणाओं से है, इसलिए जब उसने ओवरट स्पष्टीकरण को जोड़ने के लिए एक अच्छी टिप्पणी की होगी, तो मुझे यकीन नहीं है कि यह एक उत्तर को सही ठहराएगा।
अंडरस्कोर_ड

0

जैसा कि, ल्यूक टॉरिल ने पहले ही यह अच्छी तरह से समझाया है कि कक्षा के आगे की घोषणा का उपयोग और उपयोग नहीं करना है।

मैं सिर्फ इतना जोड़ूंगा कि हमें इसका उपयोग करने की आवश्यकता क्यों है।

अवांछित आश्रितता इंजेक्शन से बचने के लिए हमें जहाँ भी संभव हो आगे की घोषणा का उपयोग करना चाहिए।

जैसा कि #includeहेडर फ़ाइलों को कई फ़ाइलों पर जोड़ा जाता है, इसलिए यदि हम किसी अन्य हेडर फ़ाइल में एक हेडर जोड़ते हैं, तो यह स्रोत कोड के विभिन्न भागों में अवांछित निर्भरता इंजेक्शन जोड़ देगा, जो #includeहेडर .cppको किसी अन्य हेडर फ़ाइल में जोड़ने के बजाय जहाँ भी संभव हो फ़ाइलों में जोड़कर बचा जा सकता है। हेडर .hफ़ाइलों में जहाँ भी संभव हो क्लास फ़ॉर डिक्लेरेशन का उपयोग करें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.