শ্রেণীর মধ্যে বিজ্ঞপ্তি নির্ভরতার কারণে বিল্ড ত্রুটিগুলি সমাধান করুন


353

আমি প্রায়শই নিজেকে এমন পরিস্থিতিতে দেখতে পাই যেখানে কিছু খারাপ ডিজাইনের সিদ্ধান্তের কারণে (অন্য কারও দ্বারা তৈরি করা হয়েছে :) কারণে আমি সি ++ প্রকল্পে একাধিক সংকলন / লিংকার ত্রুটির মুখোমুখি হয়ে থাকি যা বিভিন্ন হেডার ফাইলগুলিতে সি ++ শ্রেণির মধ্যে বিজ্ঞপ্তি নির্ভরতা বাড়ে (এটিও ঘটতে পারে) একই ফাইলে) । তবে ভাগ্যক্রমে (?) পরের বারের মতো এটি হওয়ার পরে এই সমস্যার সমাধান মনে করার জন্য এটি প্রায়শই আমার পক্ষে ঘটে না।

ভবিষ্যতে সহজ প্রত্যাহার করার উদ্দেশ্যে আমি একটি প্রতিনিধি সমস্যা এবং এর সাথে একটি সমাধান পোস্ট করতে যাচ্ছি। আরও ভাল সমাধান অবশ্যই স্বাগত।


  • 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
ভিজ্যুয়াল স্টুডিওতে কাজ করার সময় / শোIncludes পতাকা এই ধরণের সমস্যাগুলি ডিবাগ করতে অনেক সহায়তা করে।
মুছুন

উত্তর:


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;
}

আপনি যখন সিসি ফাইলটি সংকলন করছেন (মনে রাখবেন যে .সিসি এবং না। এইচ সংকলনের একক), আপনাকে অবজেক্টের জন্য স্থান বরাদ্দ করতে হবে 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 তে অনুলিপি করে । সত্যিই, .সিসি দেখতে দেখতে:

// 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
ঈশ্বর! রেফারেন্সগুলি দখলকৃত স্থানের দিক দিয়ে জানা যায় তা পুরোপুরি মিস হয়ে গেল। অবশেষে, এখন আমি সঠিকভাবে ডিজাইন করতে পারি!
কেলোগগুলি

47
তবে তবুও আপনি বি তে কোনও ফাংশন ব্যবহার করতে পারবেন না (প্রশ্ন হিসাবে _b-> মুদ্রণ ())
র‌্যাঙ্ক 1

3
এই আমি সমস্যা হচ্ছে। সম্পূর্ণরূপে শিরোনাম ফাইলটি পুনরায় লিখিত না করে আপনি কীভাবে সামনে ঘোষণার মাধ্যমে ফাংশনগুলি আনবেন?
sydan


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
আপনার যদি কোনও টেম্পলেট পদ্ধতি থাকে? তারপরে আপনি টেমপ্লেটগুলিকে ম্যানুয়ালি ইনস্ট্যান্ট না করলে আপনি এটিকে সত্যই সিপিপি ফাইলে স্থানান্তর করতে পারবেন না।
ম্যালকম

আপনি সর্বদা "আহ" এবং "ভি" একসাথে অন্তর্ভুক্ত করেন। আপনি কেন "আঃ" কে "ভ" তে অন্তর্ভুক্ত করবেন না এবং তারপরে "এসিপিপি" এবং "বিসিপিপি" উভয়ই কেবল "ভ" অন্তর্ভুক্ত করবেন না?
গুসেভ স্লাভা 30:25

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:
    • সংঘাতবিরোধী ঘোষণা / সংজ্ঞা B(যেমন, উপরের বি বি ভেঙে A এবং অন্য যে কোনও ক্লায়েন্ট স্বচ্ছতার সাথে কাজ করার পরিবর্তে ফরোয়ার্ড ঘোষণাপত্রকে গালাগাল করে) এর সংঘাতের সাথে সংক্ষিপ্ত হয়ে এ এর ​​সংকলনটি একটি ত্রুটির সাথে সমাপ্ত হবে ।
  • অন্যথায় (যদি শেষ পর্যন্ত A অন্তর্ভুক্ত না করা হয় b.h- যদি A কেবলমাত্র পয়েন্টার এবং / বা রেফারেন্স দ্বারা বিএসের চারপাশে সঞ্চয় করে / পাস করে)
    • #includeবিশ্লেষণের উপর নির্ভর করে তৈরি সরঞ্জাম নির্মান এবং পরিবর্তিত ফাইল টাইমস্ট্যাম্পগুলি Aবি তে পরিবর্তনের পরে (এবং এর আরও নির্ভরশীল কোড) পুনর্নির্মাণ করবে না , লিঙ্ক টাইম বা রান টাইমে ত্রুটি সৃষ্টি করবে। যদি বি রানটাইম লোডড ডিএলএল হিসাবে বিতরণ করা হয়, "এ" কোডটি রানটাইমের সময় আলাদাভাবে মঙ্গলের চিহ্নগুলি খুঁজে পেতে ব্যর্থ হতে পারে, যা সুশৃঙ্খল শাটডাউন বা গ্রহণযোগ্যভাবে হ্রাস কার্যকারিতাটি কার্যকর করতে যথেষ্টভাবে পরিচালিত হতে পারে বা নাও হতে পারে।

যদি এগুলির কোডটিতে পুরানোগুলির জন্য টেমপ্লেট বিশেষায়িতকরণ / "বৈশিষ্ট্য" থাকে তবে Bসেগুলি কার্যকর হবে না।


2
অগ্রিম ঘোষণাগুলি হ্যান্ডেল করার এটি একটি সত্যিই পরিষ্কার উপায়। অতিরিক্ত ফাইলগুলিতে কেবলমাত্র "অসুবিধা" থাকবে। আমি অনুমান আপনি সবসময় অন্তর্ভুক্ত a.fwd.hমধ্যে a.hতারা সুসংগত থাকার আশ্বাস। এই ক্লাসগুলি কোথায় ব্যবহৃত হচ্ছে উদাহরণ কোডটি অনুপস্থিত। a.hএবং b.hউভয়কেই অন্তর্ভুক্ত করা দরকার যেহেতু তারা বিচ্ছিন্নতায় কাজ করবে না: `` `//main.cpp # অন্তর্ভুক্ত" আহ "# অন্তর্ভুক্ত" বিএইচ "অন্তর্ভুক্ত মূল () {...}` `` বা তাদের মধ্যে একটি উদ্বোধনী প্রশ্নের মতো অন্যটিতে পুরোপুরি অন্তর্ভুক্ত করা দরকার। যেখানে b.hঅন্তর্ভুক্ত রয়েছে a.hএবং এর main.cppমধ্যে রয়েছেb.h
ফারওওয়ে

2
সবদিক থেকে গণনা করুন আমি দেখানো বিরক্ত করিনি main.cpp, তবে এটি আপনার মন্তব্যে কী থাকতে হবে তা নথিভুক্ত করেছেন তা চমৎকার। চিয়ার্স
টনি ডেলরয়

1
নীতি ও কুফলের কারণে কেন করণীয় ও করণীয় নয় তার চমৎকার বিশদ সহকারে আরও ভাল উত্তরের একটি ...
ফ্রান্সিস কিউলার

1
@ রেজাহাজিয়ানপুর: আপনি যে বিজ্ঞপ্তি, বিজ্ঞপ্তি বা অগ্রণী ঘোষণা করতে চান তা যে সকল শ্রেণীর জন্য অগ্রণী ঘোষণা শিরোনাম রয়েছে তা বোধগম্য হয়। এতে বলা হয়েছে, আপনি কেবল তখনই তাদের চাইবেন: ১) আসল ঘোষণাসহ ব্যয়বহুল (বা পরে হওয়ার প্রত্যাশা করা যেতে পারে) ব্যয়বহুল (যেমন এতে আপনার অনুবাদ ইউনিটকে অন্যথায় প্রয়োজন নাও হতে পারে এমন অনেকগুলি শিরোনাম অন্তর্ভুক্ত রয়েছে), এবং ২) ক্লায়েন্ট কোড পয়েন্টার বা অবজেক্টের রেফারেন্স ব্যবহার করতে সক্ষম হতে পারে। <iosfwd>এটি একটি সর্বোত্তম উদাহরণ: অনেকগুলি স্থান থেকে রেফারেন্সিত কয়েকটি স্ট্রিম অবজেক্ট থাকতে পারে এবং এর <iostream>মধ্যে অনেক কিছুই অন্তর্ভুক্ত।
টনি ডেলরয়

1
@ রেজাহাজিয়ানপুর: আমি মনে করি আপনার সঠিক ধারণা আছে তবে আপনার বক্তব্যটিতে একটি পরিভাষা আছে: "আমাদের কেবল প্রকারটি ঘোষণার দরকার " সঠিক হবে। প্রকার ঘোষিত হওয়ার অর্থ আগাম ঘোষণাটি দেখা গেছে; এটা হচ্ছে সংজ্ঞায়িত একবার পূর্ণ সংজ্ঞা পার্স করা হয়েছে (এবং যে জন্য আপনি পারে বেশি প্রয়োজন #includeগুলি)।
টনি ডেলরয়

20

মনে রাখার মতো ঘটনা:

  • সদস্য বা তদ্বিপরীত হিসাবে class Aকোনও অবজেক্ট থাকলে এটি কাজ করবে না class B
  • ফরোয়ার্ড ডিক্লেয়ারেশন হ'ল উপায়।
  • ঘোষণার বিষয়গুলির অর্ডার (এজন্য আপনি সংজ্ঞাগুলি সরিয়ে নিচ্ছেন)।
    • উভয় শ্রেণি যদি অন্যটির ফাংশনগুলিকে কল করে তবে আপনাকে সংজ্ঞাগুলি সরিয়ে নিতে হবে।

FAQ পড়ুন:


1
আপনি যে লিঙ্কগুলি সরবরাহ করেছেন সেগুলি আর কাজ করে না, আপনি কী নতুনটিকে উল্লেখ করবেন তা জানবেন?
রাম্যা রাও

11

আমি একবার শ্রেণীর সংজ্ঞার পরে সমস্ত ইনলাইন সরানো এবং শিরোনাম ফাইলটিতে ইনলাইনগুলির#include ঠিক আগে অন্যান্য ক্লাসগুলির জন্য রেখে এই ধরণের সমস্যাটি সমাধান করেছি । এইভাবে একটি নিশ্চিত করে নিন যে সমস্ত সংজ্ঞা + ইনলাইনগুলি ইনলাইনগুলি পার্স করার আগে সেট করা আছে।

এটি করার ফলে উভয় (বা একাধিক) হেডার ফাইলগুলিতে এখনও একগুচ্ছ ইনলাইন থাকা সম্ভব হয় possible তবে গার্ডদের অন্তর্ভুক্ত করা দরকার

এটার মত

// 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


কেন? আমি মনে করি এটি একটি জটিল সমস্যার এক মার্জিত সমাধান ... যখন কেউ ইনলাইন চায়। যদি কেউ ইনলাইনগুলি না চায় তবে কোডটি লেখা উচিত ছিল না যেমন এটি শুরু থেকেই লেখা ছিল ...
এপ্যাটেল

যদি কোনও ব্যবহারকারী B.hপ্রথমে অন্তর্ভুক্ত থাকে তবে কী হবে ?
মিঃ ফুজ

3
মনে রাখবেন যে আপনার শিরোনাম প্রহরী একটি সংরক্ষিত শনাক্তকারী ব্যবহার করছে, ডাবল সংলগ্ন আন্ডারস্কোর সহ যে কোনও কিছুই সংরক্ষিত।
লার্স ভিক্লুন্ড

6

আমি এই সম্পর্কে একবারে একটি পোস্ট লিখেছি: সি ++ তে বিজ্ঞপ্তি নির্ভরতাগুলি সমাধান করা

প্রাথমিক কৌশলটি ইন্টারফেস ব্যবহার করে ক্লাসগুলি ডিকুয়াল করা। সুতরাং আপনার ক্ষেত্রে:

//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.wikedia.org/wiki/Circular_d dependency# উদাহরণটিতে_পরিচালিকা_ নির্ভরতা_ইন_সিবিবি তে পড়তে পারেন )

ফাইল '' 'এইচ' '':

#ifndef A_H
#define A_H

class B;    //forward declaration

class A {
public:
    B* b;
};
#endif //A_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কোনও ক্রমে আহ এবং ভি অন্তর্ভুক্ত করতে পারেন

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.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"

এই কোডটি সংকলন করে না কারণ বি :: ড এর সম্পূর্ণ ধরণের এ দরকার যা পরে সংজ্ঞায়িত হয়।

এটি উত্স কোডটি সংকলন করে তা নিশ্চিত করার জন্য:

//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

এই নির্দেশের বর্তমান ফাইলটির প্রক্রিয়াকরণ স্থগিত করা উচিত এবং এতে অন্তর্ভুক্ত সমস্ত মুলতুবি রয়েছে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.