कक्षाओं के बीच परिपत्र निर्भरता के कारण त्रुटियों का समाधान करें


353

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

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


  • A.h

    class B;
    class A
    {
        int _val;
        B *_b;
    public:
    
        A(int val)
            :_val(val)
        {
        }
    
        void SetB(B *b)
        {
            _b = b;
            _b->Print(); // COMPILER ERROR: C2027: use of undefined type 'B'
        }
    
        void Print()
        {
            cout<<"Type:A val="<<_val<<endl;
        }
    };

  • B.h

    #include "A.h"
    class B
    {
        double _val;
        A* _a;
    public:
    
        B(double val)
            :_val(val)
        {
        }
    
        void SetA(A *a)
        {
            _a = a;
            _a->Print();
        }
    
        void Print()
        {
            cout<<"Type:B val="<<_val<<endl;
        }
    };

  • main.cpp

    #include "B.h"
    #include <iostream>
    
    int main(int argc, char* argv[])
    {
        A a(10);
        B b(3.14);
        a.Print();
        a.SetB(&b);
        b.Print();
        b.SetA(&a);
        return 0;
    }

23
जब Visual Studio के साथ काम करते हैं, तो / showIncludes ध्वज इस तरह की समस्याओं को डीबग करने में बहुत मदद करता है।
WIP

जवाबों:


288

इस बारे में सोचने का तरीका "एक कंपाइलर की तरह सोचना" है।

कल्पना कीजिए कि आप एक कंपाइलर लिख रहे हैं। और आप इस तरह से कोड देखें।

// file: A.h
class A {
  B _b;
};

// file: B.h
class B {
  A _a;
};

// file main.cc
#include "A.h"
#include "B.h"
int main(...) {
  A a;
}

जब आप .cc फ़ाइल संकलित कर रहे हैं (याद रखें कि .cc और .h .h संकलन की इकाई नहीं है), तो आपको ऑब्जेक्ट के लिए स्थान आवंटित करने की आवश्यकता है A। तो, ठीक है, फिर कितनी जगह? स्टोर करने के लिए पर्याप्त B! Bतब का आकार क्या है ? स्टोर करने के लिए पर्याप्त A! उफ़।

स्पष्ट रूप से एक परिपत्र संदर्भ जिसे आपको तोड़ना होगा।

आप इसे संकलक के बजाय आरक्षित स्थान की अनुमति देकर इसे तोड़ सकते हैं, क्योंकि यह अग्रिम-बिंदुओं और संदर्भों के बारे में जानता है, उदाहरण के लिए, हमेशा 32 या 64 बिट्स (वास्तुकला के आधार पर) होंगे और इसलिए यदि आप (या तो एक) द्वारा प्रतिस्थापित किया गया है एक सूचक या संदर्भ, चीजें बहुत अच्छी होंगी। मान लीजिए कि हम इसमें प्रतिस्थापित करते हैं A:

// file: A.h
class A {
  // both these are fine, so are various const versions of the same.
  B& _b_ref;
  B* _b_ptr;
};

अब हालात बेहतर हैं। कुछ हद तक। main()अभी भी कहता है:

// file: main.cc
#include "A.h"  // <-- Houston, we have a problem

#include, सभी विस्तार और उद्देश्यों के लिए (यदि आप प्रीप्रोसेसर को बाहर निकालते हैं) बस फ़ाइल को .cc में कॉपी करता है । तो वास्तव में, .cc ऐसा दिखता है:

// file: partially_pre_processed_main.cc
class A {
  B& _b_ref;
  B* _b_ptr;
};
#include "B.h"
int main (...) {
  A a;
}

आप देख सकते हैं कि संकलक इससे क्यों नहीं निपट सकता है - इसका कोई पता नहीं है कि Bयह क्या है - यह पहले कभी प्रतीक भी नहीं देखा है।

तो चलिए कंपाइलर के बारे में बताते हैं B। इसे आगे की घोषणा के रूप में जाना जाता है , और इस उत्तर में आगे चर्चा की जाती है ।

// main.cc
class B;
#include "A.h"
#include "B.h"
int main (...) {
  A a;
}

यह काम करता है । यह महान नहीं है । लेकिन इस बिंदु पर आपको परिपत्र संदर्भ समस्या की समझ होनी चाहिए और हमने इसे "ठीक" करने के लिए क्या किया है, भले ही यह फिक्स खराब हो।

यह फिक्स खराब होने का कारण यह है कि अगले व्यक्ति को इसका उपयोग #include "A.h"करने Bसे पहले घोषित करना होगा और एक भयानक #includeत्रुटि प्राप्त होगी । तो आइए घोषणा को आह में ही आगे बढ़ाते हैं।

// file: A.h
class B;
class A {
  B* _b; // or any of the other variants.
};

और भा में , इस बिंदु पर, आप बस #include "A.h"सीधे कर सकते हैं।

// file: B.h
#include "A.h"
class B {
  // note that this is cool because the compiler knows by this time
  // how much space A will need.
  A _a; 
}

HTH।


20
"बी के बारे में संकलक को बताना" बी की एक आगे की घोषणा के रूप में जाना जाता है
पीटर अज़ताई

8
हे भगवान! पूरी तरह से इस तथ्य से चूक गए कि कब्जे वाली जगह के संदर्भ में संदर्भ जाना जाता है। अंत में, अब मैं ठीक से डिजाइन कर सकता हूं!
kellogs

47
लेकिन फिर भी आप B पर किसी भी फ़ंक्शन का उपयोग नहीं कर सकते हैं (जैसा कि प्रश्न में _b-> Printt ())
रैंक 1

3
यह मुद्दा मैं कर रहा हूँ। आप हेडर फ़ाइल को पूरी तरह से फिर से लिखने के बिना आगे की घोषणा के साथ कार्यों को कैसे लाते हैं?
sydan 13


101

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

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

//A.h
#ifndef A_H
#define A_H
class B;
class A
{
    int _val;
    B* _b;
public:

    A(int val);
    void SetB(B *b);
    void Print();
};
#endif

//B.h
#ifndef B_H
#define B_H
class A;
class B
{
    double _val;
    A* _a;
public:

    B(double val);
    void SetA(A *a);
    void Print();
};
#endif

//A.cpp
#include "A.h"
#include "B.h"

#include <iostream>

using namespace std;

A::A(int val)
:_val(val)
{
}

void A::SetB(B *b)
{
    _b = b;
    cout<<"Inside SetB()"<<endl;
    _b->Print();
}

void A::Print()
{
    cout<<"Type:A val="<<_val<<endl;
}

//B.cpp
#include "B.h"
#include "A.h"
#include <iostream>

using namespace std;

B::B(double val)
:_val(val)
{
}

void B::SetA(A *a)
{
    _a = a;
    cout<<"Inside SetA()"<<endl;
    _a->Print();
}

void B::Print()
{
    cout<<"Type:B val="<<_val<<endl;
}

//main.cpp
#include "A.h"
#include "B.h"

int main(int argc, char* argv[])
{
    A a(10);
    B b(3.14);
    a.Print();
    a.SetB(&b);
    b.Print();
    b.SetA(&a);
    return 0;
}

धन्यवाद। इससे समस्या आसानी से हल हो गई। मैं बस परिपत्र ले जाया गया .cpp फ़ाइलों में शामिल हैं।
होयट

3
यदि आपके पास टेम्पलेट विधि है तो क्या होगा? तब तक आप वास्तव में इसे CPP फ़ाइल में स्थानांतरित नहीं कर सकते, जब तक कि आप टेम्प्लेट को मैन्युअल रूप से तुरंत नहीं करते।
मैल्कम

आप हमेशा "आह" और "भ" को एक साथ शामिल करते हैं। आप "आह" में "आह" को शामिल क्यों नहीं करते हैं और फिर "ए.केपी" और "बीकैप" दोनों में केवल "बी" शामिल हैं?
गुसेव स्लाव

28

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

सर्वोत्तम अभ्यास: आगे की घोषणा हेडर

जैसा कि स्टैंडर्ड लाइब्रेरी के <iosfwd>हेडर द्वारा दर्शाया गया है , दूसरों के लिए फॉरवर्ड डिक्लेरेशन प्रदान करने का उचित तरीका फॉरवर्ड डिक्लेरेशन हेडर है । उदाहरण के लिए:

a.fwd.h:

#pragma once
class A;

आह:

#pragma once
#include "a.fwd.h"
#include "b.fwd.h"

class A
{
  public:
    void f(B*);
};

b.fwd.h:

#pragma once
class B;

bh:

#pragma once
#include "b.fwd.h"
#include "a.fwd.h"

class B
{
  public:
    void f(A*);
};

Aऔर Bपुस्तकालयों के अनुरक्षकों को प्रत्येक को अपने हेडर और कार्यान्वयन फ़ाइलों के साथ अपने आगे की घोषणा के हेडर को रखने के लिए जिम्मेदार होना चाहिए, इसलिए - उदाहरण के लिए - यदि "बी" का अनुचर साथ आता है और होने वाले कोड को फिर से लिखता है ...

b.fwd.h:

template <typename T> class Basic_B;
typedef Basic_B<char> B;

bh:

template <typename T>
class Basic_B
{
    ...class definition...
};
typedef Basic_B<char> B;

... फिर "ए" के लिए कोड को फिर से तैयार b.fwd.hकरना शामिल किए गए परिवर्तनों से शुरू हो जाएगा और सफाई से पूरा होना चाहिए।


खराब लेकिन सामान्य व्यवहार: आगे अन्य कामों में सामान की घोषणा

ऊपर बताए अनुसार - आगे घोषित घोषणा पत्र का उपयोग करने के बजाय - कोड में a.hया a.ccइसके बजाय class B;स्वयं आगे की घोषणा :

  • अगर a.hया बाद a.ccमें शामिल किया b.hगया था:
    • A का संकलन एक बार एक त्रुटि के साथ समाप्त हो जाएगा, जब यह परस्पर विरोधी घोषणा / परिभाषा के लिए हो जाता है B(यानी B के लिए उपरोक्त परिवर्तन ए और किसी भी अन्य ग्राहकों को पारदर्शी रूप से काम करने के बजाय, आगे की घोषणाओं का दुरुपयोग करते हुए)।
  • अन्यथा (यदि A ने अंत में शामिल नहीं किया है b.h- संभव है यदि A बस पॉइंटर और / या संदर्भ द्वारा Bs के आसपास से गुजरता है)
    • B पर परिवर्तन के बाद #includeविश्लेषण और बदले हुए फ़ाइल टाइमस्टैम्प पर निर्भर होने वाले टूल A(और इसके आगे निर्भर कोड) का निर्माण नहीं होगा , जिससे लिंक टाइम या रन टाइम में त्रुटियाँ हो सकती हैं। यदि बी को रनटाइम लोड किए गए डीएलएल के रूप में वितरित किया जाता है, तो "ए" में कोड रनटाइम पर अलग-अलग-किए गए प्रतीकों को खोजने में विफल हो सकता है, जो क्रमिक शटडाउन या स्वीकार्य रूप से कम कार्यक्षमता को ट्रिगर करने के लिए पर्याप्त रूप से नियंत्रित नहीं किया जा सकता है।

यदि A के कोड में पुराने के लिए टेम्पलेट विशेषज्ञता / "लक्षण" हैं B, तो वे प्रभावी नहीं होंगे।


2
यह आगे की घोषणाओं को संभालने के लिए एक बहुत साफ तरीका है। केवल "नुकसान" अतिरिक्त फ़ाइलों में होगा। मुझे लगता है कि आप हमेशा शामिल a.fwd.hमें a.hवे समन्वयित रहते हैं आश्वस्त करने के लिए,। उदाहरण कोड गायब है जहां इन वर्गों का उपयोग किया जाता है। a.hऔर b.hदोनों को शामिल होने की आवश्यकता नहीं होगी क्योंकि वे अलगाव में कार्य नहीं करेंगे: `` // //ain.cpp #include "आह" #include "bh" int main () {...} `` `या उनमें से एक प्रारंभिक प्रश्न में अन्य की तरह पूरी तरह से शामिल करने की आवश्यकता है। कहाँ b.hशामिल a.hऔर main.cppभी शामिल हैb.h
Farway

2
सभी मार्गों पर @Farway राइट। मुझे दिखाने में कोई दिक्कत नहीं हुई main.cpp, लेकिन अच्छा है कि आपने अपनी टिप्पणी में इसे शामिल किया है। चीयर्स
टोनी डेलरो

1
पेशेवरों और विपक्ष के कारण क्यों और क्या नहीं करता है के साथ एक अच्छा विस्तृत विवरण के साथ बेहतर उत्तर में से एक ...
फ्रांसिस कुग्लर

1
@RezaHajianpour: यह समझ में आता है कि आप सभी वर्गों के लिए एक अग्रगामी घोषणा पत्र है, जो आप चाहते हैं, परिपत्र या आगे की घोषणाएं करना चाहते हैं। उस ने कहा, आप केवल उन्हें तब चाहेंगे जब: 1) वास्तविक घोषणा सहित (या बाद में बनने के लिए प्रत्याशित हो सकता है) महंगा (जैसे इसमें बहुत सारे हेडर शामिल हैं आपकी अनुवाद इकाई को अन्यथा आवश्यकता नहीं हो सकती है), और 2) ग्राहक कोड है वस्तुओं के संदर्भों या संदर्भों का उपयोग करने में सक्षम होने की संभावना। <iosfwd>एक क्लासिक उदाहरण है: कई स्थानों से संदर्भित कुछ स्ट्रीम ऑब्जेक्ट हो सकते हैं, और <iostream>इसमें बहुत कुछ शामिल है।
टोनी डेलरो

1
@RezaHajianpour: मुझे लगता है कि आपके पास सही विचार है, लेकिन आपके कथन के साथ एक पारिभाषिक मुद्दा है: "हमें केवल घोषित किए जाने वाले प्रकार की आवश्यकता है " सही होगा। घोषित किए जा रहे प्रकार का अर्थ है कि आगे की घोषणा देखी गई है; एक बार पूर्ण परिभाषा पार्स हो जाने के बाद इसे परिभाषित किया गया है (और इसके लिए आपको और अधिक की आवश्यकता हो सकती है #include)।
टोनी डेलरो

20

याद रखने वाली चीज़ें:

  • यदि सदस्य या इसके विपरीत class Aकोई वस्तु है तो यह काम नहीं करेगा class B
  • आगे की घोषणा रास्ता तय करना है।
  • घोषणा के मामलों का क्रम (जिसके कारण आप परिभाषाएँ आगे बढ़ा रहे हैं)।
    • यदि दोनों वर्ग दूसरे के कार्यों को कहते हैं, तो आपको परिभाषाएँ बदलनी होंगी।

एफएक्यू पढ़ें:


1
आपके द्वारा दिए गए लिंक अब काम नहीं करते हैं, क्या आप नए लोगों को संदर्भित करने के लिए जानते हैं?
राम्या राव

11

मैंने एक बार इस तरह की समस्या को वर्ग परिभाषा के बाद सभी इनलाइनों को आगे बढ़ाते हुए और हेडर फ़ाइल में इंसुलेशन#include से ठीक पहले अन्य कक्षाओं के लिए डाल दिया था । इस तरह से यह सुनिश्चित किया जाता है कि सभी परिभाषाएं + इनलाइनों को निर्धारित की गई हैं, इनलाइनों को पार्स किया गया है।

इस तरह से करना अभी भी दोनों (या एकाधिक) हेडर फ़ाइलों में झुंडों का एक समूह होना संभव बनाता है। लेकिन इसमें गार्ड्स को शामिल करना आवश्यक है ।

ऐशे ही

// File: A.h
#ifndef __A_H__
#define __A_H__
class B;
class A
{
    int _val;
    B *_b;
public:
    A(int val);
    void SetB(B *b);
    void Print();
};

// Including class B for inline usage here 
#include "B.h"

inline A::A(int val) : _val(val)
{
}

inline void A::SetB(B *b)
{
    _b = b;
    _b->Print();
}

inline void A::Print()
{
    cout<<"Type:A val="<<_val<<endl;
}

#endif /* __A_H__ */

... और उसी में कर रहे हैं B.h


क्यों? मुझे लगता है कि यह एक मुश्किल समस्या का एक सुंदर समाधान है ... जब कोई इनलाइन चाहता है। अगर कोई नहीं चाहता है कि एक को कोड नहीं लिखना चाहिए था जैसे कि यह शुरू से लिखा गया था ...
epatel

यदि कोई उपयोगकर्ता B.hपहले शामिल होता है तो क्या होता है ?
श्री फूज

3
ध्यान दें कि आपका हेडर गार्ड आरक्षित पहचानकर्ता का उपयोग कर रहा है, डबल आसन्न अंडरस्कोर के साथ कुछ भी आरक्षित है।
लार्स विकलंड

6

मैंने एक बार इस बारे में एक पोस्ट लिखी है: c ++ में परिपत्र निर्भरता का समाधान

बुनियादी तकनीक इंटरफेस का उपयोग करके कक्षाओं को अपघटित करना है। तो आपके मामले में:

//Printer.h
class Printer {
public:
    virtual Print() = 0;
}

//A.h
#include "Printer.h"
class A: public Printer
{
    int _val;
    Printer *_b;
public:

    A(int val)
        :_val(val)
    {
    }

    void SetB(Printer *b)
    {
        _b = b;
        _b->Print();
    }

    void Print()
    {
        cout<<"Type:A val="<<_val<<endl;
    }
};

//B.h
#include "Printer.h"
class B: public Printer
{
    double _val;
    Printer* _a;
public:

    B(double val)
        :_val(val)
    {
    }

    void SetA(Printer *a)
    {
        _a = a;
        _a->Print();
    }

    void Print()
    {
        cout<<"Type:B val="<<_val<<endl;
    }
};

//main.cpp
#include <iostream>
#include "A.h"
#include "B.h"

int main(int argc, char* argv[])
{
    A a(10);
    B b(3.14);
    a.Print();
    a.SetB(&b);
    b.Print();
    b.SetA(&a);
    return 0;
}

2
कृपया ध्यान दें कि इंटरफेस का उपयोग और virtualरनटाइम प्रदर्शन प्रभाव है।
cemper93

4

यहां टेम्प्लेट का समाधान दिया गया है: टेम्प्लेट के साथ परिपत्र निर्भरता कैसे संभालें

इस समस्या को हल करने का सुराग परिभाषाओं (कार्यान्वयन) प्रदान करने से पहले दोनों वर्गों को घोषित करना है। घोषणा और परिभाषा को अलग-अलग फ़ाइलों में विभाजित करना संभव नहीं है, लेकिन आप उन्हें संरचना कर सकते हैं जैसे कि वे अलग-अलग फ़ाइलों में थे।


2

विकिपीडिया पर प्रस्तुत सरल उदाहरण ने मेरे लिए काम किया। (आप पूरा विवरण http://en.wikipedia.org/wiki/Circular_d dependency#Example_of_circular_d dependencies_in_C.2B.2B पर पढ़ सकते हैं )

फ़ाइल '' 'a.h' '':

#ifndef A_H
#define A_H

class B;    //forward declaration

class A {
public:
    B* b;
};
#endif //A_H

फ़ाइल '' 'b.h' '':

#ifndef B_H
#define B_H

class A;    //forward declaration

class B {
public:
    A* a;
};
#endif //B_H

फ़ाइल '' 'main.cpp' '':

#include "a.h"
#include "b.h"

int main() {
    A a;
    B b;
    a.b = &b;
    b.a = &a;
}

1

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

यहां बताया गया है कि आप ऐसा कैसे कर सकते हैं, सभी विवरणों को बनाए रखना, और प्रयोज्यता:

  • समाधान वास्तव में मूल रूप से उसी के समान है
  • इनलाइन कार्य अभी भी इनलाइन
  • के उपयोगकर्ताओं Aऔर Bकिसी भी क्रम में आह और Bh शामिल कर सकते हैं

दो फाइलें बनाएं, A_def.h, B_def.h। ये केवल शामिल होंगे Aकी और B'परिभाषा रों:

// A_def.h
#ifndef A_DEF_H
#define A_DEF_H

class B;
class A
{
    int _val;
    B *_b;

public:
    A(int val);
    void SetB(B *b);
    void Print();
};
#endif

// B_def.h
#ifndef B_DEF_H
#define B_DEF_H

class A;
class B
{
    double _val;
    A* _a;

public:
    B(double val);
    void SetA(A *a);
    void Print();
};
#endif

और फिर, आह और भ में यह शामिल होगा:

// A.h
#ifndef A_H
#define A_H

#include "A_def.h"
#include "B_def.h"

inline A::A(int val) :_val(val)
{
}

inline void A::SetB(B *b)
{
    _b = b;
    _b->Print();
}

inline void A::Print()
{
    cout<<"Type:A val="<<_val<<endl;
}

#endif

// B.h
#ifndef B_H
#define B_H

#include "A_def.h"
#include "B_def.h"

inline B::B(double val) :_val(val)
{
}

inline void B::SetA(A *a)
{
    _a = a;
    _a->Print();
}

inline void B::Print()
{
    cout<<"Type:B val="<<_val<<endl;
}

#endif

ध्यान दें कि A_def.h और B_def.h "निजी" हेडर हैं, उपयोगकर्ताओं Aऔर Bउन्हें उपयोग नहीं करना चाहिए। सार्वजनिक हेडर आह और भ है


1
क्या टोनी डेलरॉय के समाधान पर इसका कोई लाभ है ? दोनों "सहायक" हेडर पर आधारित हैं, लेकिन टोनी छोटे हैं (उनमें सिर्फ आगे की घोषणा शामिल है) और वे उसी तरह काम कर रहे हैं (कम से कम पहली नज़र में)।
फैबियो का कहना है कि

1
वह जवाब मूल समस्या को हल नहीं करता है। यह सिर्फ कहता है "आगे की घोषणाओं को एक अलग शीर्षक में रखें"। परिपत्र निर्भरता को हल करने के बारे में कुछ भी नहीं (प्रश्न को एक समाधान की आवश्यकता है जहां A'की Bपरिभाषा उपलब्ध है, आगे की घोषणा पर्याप्त नहीं है)।
भूजा

0

कुछ मामलों में परिभाषाओं को शामिल करने वाली परिपत्र निर्भरता को हल करने के लिए कक्षा ए की हेडर फ़ाइल में एक विधि या कक्षा बी के एक निर्माता को परिभाषित करना संभव है । इस तरह आप .ccफ़ाइलों में परिभाषाएँ रखने से बच सकते हैं , उदाहरण के लिए यदि आप केवल हेडर पुस्तकालय में लागू करना चाहते हैं।

// file: a.h
#include "b.h"
struct A {
  A(const B& b) : _b(b) { }
  B get() { return _b; }
  B _b;
};

// note that the get method of class B is defined in a.h
A B::get() {
  return A(*this);
}

// file: b.h
class A;
struct B {
  // here the get method is only declared
  A get();
};

// file: main.cc
#include "a.h"
int main(...) {
  B b;
  A a = b.get();
}

0

दुर्भाग्य से मैं भूजा से जवाब नहीं दे सकता।

वह सिर्फ यह नहीं कह रहा है "आगे की घोषणाओं को एक अलग शीर्षक में रखें"। वह कहते हैं कि आपको "स्थगित निर्भरता" की अनुमति देने के लिए विभिन्न हेडर फ़ाइलों में वर्ग परिभाषा हेडर और इनलाइन फ़ंक्शन परिभाषा को पूरा करना होगा।

लेकिन उनका दृष्टांत वास्तव में अच्छा नहीं है। क्योंकि दोनों वर्गों (ए और बी) को केवल एक दूसरे के अपूर्ण प्रकार (पॉइंटर फ़ील्ड / पैरामीटर) की आवश्यकता होती है।

इसे बेहतर ढंग से समझने के लिए कि कक्षा A में B * B का प्रकार नहीं है। क्लास ए और बी के अलावा एक इनलाइन फ़ंक्शन को अन्य प्रकार के मापदंडों के साथ परिभाषित करना चाहते हैं:

यह सरल कोड काम नहीं करेगा:

// A.h
#pragme once
#include "B.h"

class A{
  B b;
  inline void Do(B b);
}

inline void A::Do(B b){
  //do something with B
}

// B.h
#pragme once
class A;

class B{
  A* b;
  inline void Do(A a);
}

#include "A.h"

inline void B::Do(A a){
  //do something with A
}

//main.cpp
#include "A.h"
#include "B.h"

यह निम्नलिखित कोड में परिणाम होगा:

//main.cpp
//#include "A.h"

class A;

class B{
  A* b;
  inline void Do(A a);
}

inline void B::Do(A a){
  //do something with A
}

class A{
  B b;
  inline void Do(B b);
}

inline void A::Do(B b){
  //do something with B
}
//#include "B.h"

यह कोड संकलित नहीं करता है क्योंकि B :: क्या एक पूर्ण प्रकार की A की आवश्यकता है जिसे बाद में परिभाषित किया गया है।

यह सुनिश्चित करने के लिए कि यह स्रोत कोड को संकलित करता है, इस तरह दिखना चाहिए:

//main.cpp
class A;

class B{
  A* b;
  inline void Do(A a);
}

class A{
  B b;
  inline void Do(B b);
}

inline void B::Do(A a){
  //do something with A
}

inline void A::Do(B b){
  //do something with B
}

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

इस समस्या को हल करने के लिए मैं एक प्रीप्रोसेसर विस्तार का सुझाव देना चाहूंगा: #pragma process_pending_includes

इस निर्देश को वर्तमान फ़ाइल के प्रसंस्करण को स्थगित करना चाहिए और सभी लंबित कार्यों को पूरा करना चाहिए।

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