Declaring interdependent classes

hello everyone,
I have found similar questions in the forum but really cant understand the answers.
I am declaring two classes, each one of these classes needs to know about the other class. One of the classes will contain a reference to an object of the other class's type. The other class has a member function which takes an argument of the other class's type. How can I declare these classes?
The way I have it now doesn't work because the first class doesn't know about the second class. Doing forward declaration still gives me an error "field has incomplete PflPowerMax".
I know this is basic, but I have struggled with this for days.
How can I declare these interdependent classes?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct PointElementMax
{
	PointElementMax(PflPowerMax& parentPfl)
	{
            _parentPflMax = parentPfl;
	}

	int nominalpower;
        PflPowerMax _parentPflMax;
	bool _powReserved = false;
};

struct PflPowerMax
{
	int _powAvailable;

	bool canReservePower(const PointElementMax& pointMax) const
	{
		if (pointMax.nominalpower <= _powAvailable)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
};


thanks for your help.
Last edited on
It's not clear why you're keeping a reference to PflPowerMax in PointElementMax. I assume there is code being left out where you are "dereferencing" that reference? Because that matters for how we do this.

For your code as it is right now, you need to do three things:
(1) Before line 1, add struct PflPowerMax;

(2) line 9 should be a reference.
PflPowerMax& _parentPflMax;

(3) In the constructor, the reference needs to be initialized with the initialization list part of the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Example program

struct PflPowerMax;

struct PointElementMax
{
	PointElementMax(PflPowerMax& parentPfl)
	: _parentPflMax(parentPfl)
	{

	}

	int nominalpower;
	PflPowerMax& _parentPflMax;
	bool _powReserved = false;
};

struct PflPowerMax
{
	int _powAvailable;

	bool canReservePower(const PointElementMax& pointMax) const
	{
		if (pointMax.nominalpower <= _powAvailable)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
};

int main()
{

}


PS: Your second function can just be
1
2
3
4
	bool canReservePower(const PointElementMax& pointMax) const
	{
		return (pointMax.nominalpower <= _powAvailable);
	}
Last edited on
Is this a circular dependency and thus should be avoided?
Thanks so much for the super quick response.
The PointElementMax class will have a member function that will call a member function in _parentPflMax (which is of PflPowerMax type), it will pass a reference of itself (the PointElementMax instance) as an argument of that function call.
So something like this,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
struct PflPowerMax;

struct PointElementMax
{
	PointElementMax(PflPowerMax& parentPfl)
	: _parentPflMax(parentPfl)
	{

	}

	int nominalpower;
	PflPowerMax& _parentPflMax;
	bool _powReserved = false;
};

struct PflPowerMax
{
	int _powAvailable;

	bool canReservePower(const PointElementMax& pointMax) const
	{
		if (pointMax.nominalpower <= _powAvailable)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
};

        void cando()
        {
             _parentPflMax.canReservePower(this); // not sure how to pass this argument
        }
};
int main()
{
        PointElementMax pointmax;
        pointmax.cando();
}


I am passing the full PflPowerMax object to the function and not just a member variable because, in the future, canReservePower will make changes to the referenced (argument) PointElementMax.

The idea is to have a parent child tree. Where PointElementMax is a child of PflPowerMax, which is a child of AnotherClassMax. PflPowerMax will also call a member function of a AnotherClassMax object passing itself as an argument. That's the idea, but I'm just a beginner, so any help is appreciated.

To your other comments.
1. I knew this.
2,3 That's interesting, I was actually not declaring it as a reference because I was getting a clang error in the constructor initialization of _parentPflMax (I think about the type). Why does it need to be in the initializer list? I have done both things (2,3) and there are no warnings.

thanks so much for your help.

I saw your PS. thanks for that too.




Why does it need to be in the initializer list?
Because once you're in the body of the constructor, it's too late. A reference cannot be uninitialized, so it needs to actually be initialized in the "member initalizer list", and not "assigned to" after the fact.
Last edited on
Thanks for the clarification on member initialization.
I am still having issues with this.
On the "cando" function I get a compiler error: invalid use of incomplete type 'struct PflPowerMax"
The previous code was incorrect, below is correct code of what I am trying to do:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
struct PflPowerMax;

struct PointElementMax
{
	PointElementMax(PflPowerMax& parentPfl)
	: _parentPflMax(parentPfl)
	{

	}

	int nominalpower;
	PflPowerMax& _parentPflMax;
	bool _powReserved = false;

        void cando()
        {
             _parentPflMax.canReservePower(this); // not sure how to pass this argument
        }
};

struct PflPowerMax
{
	int _powAvailable;

	bool canReservePower(const PointElementMax& pointMax) const
	{
		if (pointMax.nominalpower <= _powAvailable)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
};

int main()
{
        PointElementMax pointmax;
        pointmax.cando();
}

What is the play then?
Don't define the function inline with the class definition. You can only use a reference (i.e. call one of its members) after the definition of the class is known.

Change lines 15-18 to just:
void cando();

Then add the following below line 36:
1
2
3
4
void PointElementMax::cando()
{
    _parentPflMax.canReservePower(*this);
}


Also,
'this' is a pointer (arbitrary language design choice that we all have to live with)
'*this' dereferences the pointer and gets you a reference.
Last edited on
Thanks a lot for this Mr Ganado. That works.
I have a few questions about this still though:
1. When you say "You can only use a reference .." . Why do you use the word "reference" here, you are not talking about an actual C++ reference, you are simply talking about using a user defined type (class) in the declaration of another class right?
2. This situation makes me wonder, what is the best/common practice when declaring multiple classes that might depend on each other. Is it just best to have a header file and a corresponding cpp file for each individual class? When is inline definition useful or appropriate?
3. If I use forward declaration like I have done, and try to use a copy (new memory?) of an object, I again get the compiler error saying "incomplete type", so if in the above code, I had this code instead:
1
2
3
4
5
6
7
8
9
10
11
12
struct PointElementMax
{
	PointElementMax(PflPowerMax parentPfl)
	: _parentPflMax(parentPfl)
	{

	}

	int nominalpower;
	PflPowerMax _parentPflMax;
	bool _powReserved = false;
};


Why does it make a difference whether I am using a reference to a PflPowerMax object or just a (copy) PflPowerMax object?

Its funny how "derefencing" 'this' actually gets me a reference. Is this what usually happens? When you dereference a pointer you get a reference to the object lying in the memory address that pointer points to?

thanks so much for your help
The "Where?" is partly answered by "What do I need to know at this point?"
Look at https://herbsutter.com/2013/08/19/gotw-7a-solution-minimizing-compile-time-dependencies-part-1/
afduggirala wrote:
1. When you say "You can only use a reference .." . Why do you use the word "reference" here, you are not talking about an actual C++ reference, you are simply talking about using a user defined type (class) in the declaration of another class right?

The important word here is use. Yes, it's talking about actual C++ references. You can store and pass a reference using just a forward declaration. But if you actually want to use it, then the code that uses it will need to know the complete definition of the type.

This makes sense when you think about it - the compiler wouldn't be able to understand any code that tries to actually use a reference to something, without knowing the definition of its type, any more than it could understand code that uses any other kind of entity.

2. This situation makes me wonder, what is the best/common practice when declaring multiple classes that might depend on each other. Is it just best to have a header file and a corresponding cpp file for each individual class?

Generally, yes. It helps keep your compilation times down, and helps keep your header files easier for users to read if they're not cluttered with loads of implementation detail.

When is inline definition useful or appropriate?

That's a bit of a can of worms, and different people will have different opinions. One place where it's definitely a good idea is in class templates. In order to generate code from a template, the compiler will need access to the full definition of the template, so it should all be in the header.

(There are tricks to get around this, but honestly it's just easier to put it all in there.)

If I use forward declaration like I have done, and try to use a copy (new memory?) of an object, I again get the compiler error saying "incomplete type"

Yes. This falls under the category of "using a reference", as discussed above. To create an object, the compiler needs to know the full definition of its type - otherwise how could it know what to create?

Its funny how "derefencing" 'this' actually gets me a reference.

If, when you dereferenced a pointer, you got a copy of the object being pointed to, rather than simply a reference to it, it would be a lot less useful.

Yes, it's unfortunate that the terminology is a bit clumsy, in that the word "reference" can mean both a general concept, and a specific C++ syntactic construct. That's what happens when one language (C++) evolves from an older one (C).
Last edited on
If you forward declare a struct/class, the actions allowed on that forward declared struct/class are very limited. Basically only pointers and references are allowed until the struct/class is fully defined.

See https://en.cppreference.com/w/cpp/language/class

What can be done is:
Declare pointers to that class.
Declare references to that class.
Declare methods that accept or return objects both by reference and by value.
Define methods that accept or return references or pointers to the class.
Thanks so much for your thorough response Mikey,
You have clarified this situation. But I don't think I will become proficient with pointers until I use them many times. However, I understand what you're saying about pointer dereferencing not being "useful" if you get a new object instead of a reference.
Thanks so much,

Thanks a lot @seeplus for the reference and info, I will learn to consult cppreference and cplusplus better.
Forward declaration was proposed as a way to declare interdependent classes. I don't know at this point if there is another way to achieve what I want.

thanks again.
No problem - glad I could help!
Topic archived. No new replies allowed.