Reputation: 63
For example there are classes A and B. A uses B's methods and vice versa. So, what is usually done is passing a pointer to an object of the other class in the constructor.
But what if they use each other – how should I pass B to A if B is not yet constructed? Ok, I wrote setter for A and call it after both are created. Now I don't use any B's code in A constructor, but it may change. What should I do then?
class A;
class B;
// A uses B
class A {
B* b;
public:
A() {
// potentially calls b's methods
}
void setB(B* b) { this->b = b; }
}
// B uses A
class B {
A* a;
public:
B(A* a) : a(a) {
// potentially calls a's methods
}
}
int main() {
A a;
B b(&a);
a.setB(&b); // ugly, but no alternative?
// Other classes use a and b
return 0;
}
Upvotes: 4
Views: 884
Reputation: 6637
Here's one way you can do it that :
By creating a third class AB that creates an A and a B in it:
#include <iostream>
struct B;
struct A
{
B* b;
void print() { std::cout << "A"; }
};
struct B
{
A* a;
void print() { std::cout << "B"; }
};
struct AB
{
A a;
B b;
AB() : a{&b} , b{&a} {}
};
Then you can use structure binding to extract the A and B from AB:
int main()
{
auto [a, b] = AB{};
a.print();
b.print();
}
Similarly, you can have a function that returns a std::pair<A, B>
:
auto create_AB()
{
A a;
B b{&a};
a.b = &b;
return std::make_pair(a, b);
}
And in main:
auto [a, b] = create_AB();
Upvotes: 0
Reputation: 51825
You can address part of your problem by defining the constructors (or any other methods that use members or methods of the other class) "out of body" – that is, put just the declaration(s) in the body of the class definitions, then define those actual methods after both classes have been fully specified. But that won't address the issue you have of both constructors needing pointers to the other.
Here's a possible code arrangement:
class A;
class B;
// A uses B
class A {
B* b;
public:
A(B* arg_b = nullptr); // Declaration only - defined later!
void setB(B* arg_b) {
b = arg_b;
}
void foo() { } // Dummy test function
};
// B uses A
class B {
A* a;
public:
B(A* arg_a = nullptr); // Declaration only - defined later!
void bar() { } // Dummy test function
};
// We provide the actual DEFINITIONS of the constructors after both classes have been specified...
A::A(B* arg_b) : b(arg_b)
{
if (b != nullptr) b->bar(); // Potentially uses one of B's methods
}
B::B(A* arg_a) : a(arg_a)
{
if (a != nullptr) a->foo(); // Potentially uses one of A's methods
}
int main()
{
A a; // This will initialize "a" with a default (null) pointer to its contained "B"
B b(&a);
a.setB(&b); // Can't really get round this - chickens, eggs and all that!
// ...
return 0;
}
You could also add/copy the relevant code from the constructor into the setB
function, and (similarly) place that definition after both classes:
void A::setB(B* arg_b)
{
b = arg_b;
if (b != nullptr) b->bar();
}
Upvotes: 2
Reputation: 118340
Either the chicken, or the egg, must come first. One of these objects must be created before the other one.
There are only two ways to cheat:
Declare both objects in global scope and use forward declarations:
extern A a;
extern B b;
A a{&b};
B b{&a};
Note that a
still gets constructed before b
, so invoking b
's methods in a
's constructor will be undefined behavior. But merely saving a pointer to the object is fine.
A variation of this is to construct both objects as member of a third object, and basically do the same thing in the object's constructor:
struct C {
A a;
B b;
C() : a{&b}, b{&a} {}
};
Now, instantiating C
in automatic or dynamic scope will instantiate both objects, passing each other's address to their respective constructors. But you still can't avoid the fundamental C++ property that only one object gets constructed at a time (in the same execution thread).
Upvotes: 3