user2138149
user2138149

Reputation: 17170

C++ | How can I break this inter-class dependency issue?

I provide to you a MWE:

#include <iostream>


class C;


class A
{
    public:

    A(C &cc)
        : c(cc)
    {
    }

    int functionA()
    {
        return 0;
    }

    C &c;
};


class B
{
    public:

    B(C &cc)
        : c(cc)
    {
    }

    int functionB()
    {
        return c.a.functionA();
    }

    C &c;
};


class C
{
    public:

    C()
        : a(*this)
        , b(*this)
    {
    }

    int functionC()
    {
        return b.functionB();
    }

    A a;
    B b;
};


int main()
{
    C c;
    std::cout << c.functionC() << std::endl;
}

And associated compiler error:

main.cpp: In member function ‘int B::functionB()’:
main.cpp:37:16: error: invalid use of incomplete type ‘class C’
   37 |         return c.a.functionA();
      |                ^
main.cpp:5:7: note: forward declaration of ‘class C’
    5 | class C;
      |       ^

Further explanation is probably not required, however, the class C is not fully defined by the time we reach line return c.a.functionA().

What is the most appropriate way to break this interdependence problem?

If it helps to guide, then consider the following context

C = Host
A = CPU
B = RAM

and the actual code where this problem occurs in my project is

void CPU::MemFetchByte(byte &ret, const addr_t addr)
{
    HardwareDevice::host.memory.GetByte(ret, addr);
}

perhaps this is useful additional info, perhaps it is not.

Additionally, I tried to invert the problem as follows

#include <iostream>


//class C;
class A;
class B;



class C
{
    public:

    C()
        : a(new A(*this))
        , b(new B(*this))
    {
    }

    ~C()
    {
        delete b;
        delete a;
    }

    int functionC()
    {
        return b->functionB();
    }

    A *a;
    B *b;
};


class A
{
    public:

    A(C &cc)
        : c(cc)
    {
    }

    int functionA()
    {
        return 0;
    }

    C &c;
};


class B
{
    public:

    B(C &cc)
        : c(cc)
    {
    }

    int functionB()
    {
        return c.a->functionA();
    }

    C &c;
};


int main()
{
    C c;
    std::cout << c.functionC() << std::endl;
}

however this (as expected) makes things worse not better:

main.cpp:16:24: error: invalid use of incomplete type ‘class A’
   16 |         : a(new A(*this))
      |                        ^
main.cpp:6:7: note: forward declaration of ‘class A’
    6 | class A;
      |       ^
main.cpp:17:24: error: invalid use of incomplete type ‘class B’
   17 |         , b(new B(*this))
      |                        ^
main.cpp:7:7: note: forward declaration of ‘class B’
    7 | class B;
      |       ^

Upvotes: 1

Views: 213

Answers (3)

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136425

It looks like classes A and B are parts of system C, so that another option is to compose C from A and B using derivation. This allows A and B to get to C with a simple downcast, no C& c member is required:

struct C;

struct A {
    void fa();
};

struct B {
    void fb();
};

struct C : A, B {
    void fc();
};

void A::fa() {
    static_cast<C&>(*this).fc();
}

void B::fb() {
    static_cast<C&>(*this).fc();
}

Upvotes: 1

HolyBlackCat
HolyBlackCat

Reputation: 96579

int functionB()
{
    return c.a.functionA();
}

This function needs to be defined outside of the class body, after class C is defined.


You can apply the same idea to your second attempt (the definitions for C(), ~C(), and functionC() will need to be moved). But this is worse than your first attempt, because you're using heap allocation for no good reason (and class C doesn't follow the rule of three).

Upvotes: 1

Eljay
Eljay

Reputation: 5321

Work with complete types by moving the constructors and member function implementations out of the class declarations.

#include <iostream>

class A;
class B;
class C;

class A { 
public:
    A(C &cc);
    int functionA();
    C &c; 
};
class B { 
public:
    B(C &cc);
    int functionB();
    C &c; 
};
class C { 
public:
    C();
    int functionC();
    A a;
    B b;
};

int main()
{
    C c;
    std::cout << c.functionC() << std::endl;
}

A::A(C &cc) : c(cc)
{ }

int A::functionA() {
    return 0;
}

B::B(C &cc) : c(cc)
{ }

int B::functionB() {
    return c.a.functionA();
}

C::C() : a(*this), b(*this)
{ }

int C::functionC() {
    return b.functionB();
}

Upvotes: 1

Related Questions