Reputation: 17170
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
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
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
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