Poor code layout?

Hey guys, I'm having an issue with what I think is circular dependency, or just poor code layout.

I take 2 headers. Player.h and Group.h and have the two to include each other. With or without header guards (#ifdef, or #pragma once) does not fix the issue.

I come from a background of C#, where doing things like this is super easy. There are no includes, everything is exposed via namespaces.

Can anyone knowledgeable on C++ clarify on some of these things?

If I forward declare a class, I can't call the members or variables of said class. I can however, use it as name to point to a reference of it. Is there no other alternative?

Basically, I want a Player class, and a Group class. The Group class holds an array of Player. While each player has a Group instance tracking each Player in the group.

After working for 3 days on this, I figured out today, something I can do is take both header files, forward declare the appropriate class, and use it in the .cpp file, but it seems very wrong.

Blu.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef BLU_H
#define BLU_H

#include "stdafx.h"
//#include "Red.h" .. Can't put this here.

class Red;

class Blu {
public:
	std::string Name;
	Red *red;
	
	Blu();

	void PrintEnemyName();
	void PrintMyName();
};

#endif 



Blu.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "stdafx.h"
#include "Blu.h"
#include "Red.h"

Blu::Blu()  {
	*red = Red();
}

void Blu::PrintEnemyName() {
	std::cout << "Blu's Enemy Name: " << Blu::red->Name << "\n";
}

void Blu::PrintMyName() {
	std::cout << "Blu's Name: " << Blu::Name << "\n";
}



Red.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef RED_H
#define RED_H

#include "stdafx.h"
//#include "Blu.h"

class Blu;

class Red {
public:
	std::string Name;
	Blu *blu;

	Red();

	void PrintEnemyName();
	void PrintMyName();
};

#endif 



Red.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "stdafx.h"
#include "Red.h"
#include "Blu.h"

Red::Red()  {
	*blu = Blu();
}

void Red::PrintEnemyName() {
	std::cout << "Red's Enemy Name: " << Red::blu->Name << "\n";
}

void Red::PrintMyName() {
	std::cout << "Red's Name: " << Red::Name << "\n";
}


main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "stdafx.h"
#include "Red.h"
#include "Blu.h"

int _tmain(int argc, _TCHAR* argv[])
{
	Red red = Red(); // Create an instance of each class.
	Blu blu = Blu();

	red.Name = "RED Object.";
	red.blu = &blu;					// Create instance of "blu" within red.
	red.blu->Name = "RED Object's enemy. BLU.";
	red.blu->PrintMyName();			// Seems methods are exposed with this class acting as a third party. I believe it is because this class does not declare Red or Blu, so it can include both Red and Blu.

	system("pause");

	
	return 0;
}


If anyone can clarify or maybe lead me on the right path, I'd be very appreciative. Thanks,

Mark.
Last edited on
What you are illustrating is how it's done. It's not hacky and useless, it's just that C++'s compilation is based on individual source files... treating each of them independently, whereas C# sees the entire project as one entity.


What you have to remember is that in C++ #include is basically a copy/paste operation. When you #include a header, it's like the compiler just takes the body of that header and dumps it into the source file.

So when you have two headers #including each other (even if guarded), it's impossible for both of them to get defined in time.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
// a.h
#ifndef A_H_INCLUDED
#define A_H_INCLUDED

#include "b.h"

class A
{
  B* b;
};

#endif 

1
2
3
4
5
6
7
8
9
10
11
12
// b.h
#ifndef B_H_INCLUDED
#define B_H_INCLUDED

#include "a.h"

class B
{
  A* a;
};

#endif 

1
2
3
4
// main.cpp
#include "a.h"

int main() { }


Compilation of this will fail with a "A not defined" error, even though B is clearly defined.

The reason for this is because when main.cpp is compiled, and the #includes are expanded, it will [conceptually] look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//main.cpp

  // > expanding #include "a.h"
  // a.h
    // > expanding #include b.h"
    // b.h
      // > expanding #include a.h"
      //  -- skipped because include guard detected that a.h has already been included
      // < done with a.h

    class B
    {
      A* a;  // <- ERROR, A hasn't been defined yet
    };



Forward declaring a class lets you say "okay, this identifier 'A' is a class name, but don't worry about the details of the class because it isn't fully defined yet." This allows you to break out of this loop:

1
2
3
4
5
6
class A;  // hey compiler:  A is a class, but it's not defined yet

class B
{
  A* a;  // this is okay now.. the compiler knows A is a class
};



More reading:
http://www.cplusplus.com/forum/articles/10627/
Last edited on
I understand the include is essentially copy/paste operation.

I'm glad someone can tell me this is how it is done.

I have one question though, how would one go about accessing the members of the declared but not-yet defined class?

Are we stuck with tying the 2 classes together in a third class?

Thanks for your help,

Mark.
I have one question though, how would one go about accessing the members of the declared but not-yet defined class?


You can't. If you have this situation it means you probably screwed something up in your design somewhere (at least in all places I've seen it).
I'm not sure how regular software is done. Does it seem too far out to have classes that call each other's members? In C# I would do this all the time.
That's a very normal thing to do in C++ as well. Just not in header files.

A source (cpp) file can #include both red and blue headers just fine. And therefore any members in either the red and blue classes can be accessed from that source file.

The only issue is one header file requiring another header file. (Which can be done but is a bit more complicated).
That's a very normal thing to do in C++ as well. Just not in header files.

A source (cpp) file can #include both red and blue headers just fine. And therefore any members in either the red and blue classes can be accessed from that source file.

The only issue is one header file requiring another header file. (Which can be done but is a bit more complicated).


Ok! I'm feeling a bit more comfortable with this whole thing now!

Quite honestly though, I'd like to do all my coding in .cpp files. I'm sure many other developers would frown on that... I try to be atleast somewhat standard.

How insane would it be to have a header with those classes declared and then have 2 seperate .CPP files including that single header? Does that sound too far out?

Thanks alot for your replies. :)

Mark.
How insane would it be to have a header with those classes declared and then have 2 seperate .CPP files including that single header?


It's not insane: sometimes complex libraries that consist of many source (and header) files expose just one header file to the users.

Moreover, you have a circular dependency (Red uses Blu, Blu uses Red) and it's exposed in the interface. Typically this means that your two classes are so tightly coupled that they should live in the same source file, not just the same header.

It's usually worth the effort to break all circular dependencies. For example, come up with a base class, and have them both hold a pointer to that base class.

Incidentally, while you're at it, you could drop stdafx.h (uncheck precompiled headers in Visual Studio's project settings) and the "_tmain" silliness, so that your program would compile on any OS with any compiler. Try http://ideone.com to see how it behaves in Linux.
Last edited on
It's not insane: sometimes complex libraries that consist of many source (and header) files expose just one header file to the users.

Moreover, you have a circular dependency (Red uses Blu, Blu uses Red) and it's exposed in the interface. Typically this means that your two classes are so tightly coupled that they should live in the same source file, not just the same header.

It's usually worth the effort to break all circular dependencies. For example, come up with a base class, and have them both hold a pointer to that base class.

Incidentally, while you're at it, you could drop stdafx.h (uncheck precompiled headers in Visual Studio's project settings) and the "_tmain" silliness, so that your program would compile on any OS with any compiler. Try http://ideone.com to see how it behaves in Linux.


I guess I may just have to do that. I always liked separating each class to a .h file, but if this is the alternative, I'll take it. :)

I really just wanted input from other C++ coders on good practice and what/what-not to do.

I'm glad you pointed out that there's an alternative to making this multi-platform. I'll do that! :D
Topic archived. No new replies allowed.