Problems with classes

Hi guys. I have a program using QT and I have just solved some painfully annoying problem but I don't understand why the solution solved it, so I am coming to you to see if you can explain it to me :)

Basically I have a class called "mainWindow" which encapsulates my programs main GUI window. This class then creates a class object called "secondWindow" when a button is pressed. This "secondWindow" class encapsulates a new GUI window.

The code I used to do this was as follows:

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

#include "mainWindow.h"

...

public:
void setMainWindowHandle(mainWindow* handle){mainWindowHandle = handle;}

private:
mainWindow *mainWindowHandle;

...
// mainWindow.h

#include "secondWindow.h"

...

void maindWindow::openSecondWindowButtonPressed(){
secondWindow *d = new secondWindow();
d->setMainWindowHandle((mainWindow*)this);
}

...


This worked perfectly and I had no troubles.

However when I used to following code to extend the program by a third dialog window, created by "secondWindow" I got stress inducing errors saying things like "ISO C++ forbids declaration of 'secondWindow' with no type".

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

#include "secondWindow.h"

...

public:
void setSecondWindowHandle(secondWindow* handle){secondWindowHandle = handle;}

private:
secondWindow *secondWindowHandle;

...
// secondWindow.h

#include "thirdWindow.h"

...

void maindWindow::openThirdWindowButtonPressed(){
thirdWindow *d = new thirdWindow();
d->setSecondWindowHandle((secondWindow*)this);
}

...


This was solved by putting "class secondWindow;" just after the include files at the top of "thirdWindow.h" by why did this fix the problem? In fact why was this even a problem in the first place when it did not occur the first time?!?!?!?

Thanks in advance, aaron.
Last edited on
After the preprocessor includes and expands everything in an implementation file (cpp), what you get is one big pile of C++ junk called translation unit. This is what the compiler actually chews on. It is a sequence of definitions and declarations.

Now, thirdWindow.h and secondWindow.h include each other, and eventually... get included somewhere in some implementation file. Then the preprocessor spits out the translation unit for this implementation file, and inside it you have the definitions for the secondWindow and thirdWindow types. Both refer to each other. Consequently, the compiler can not digest either, without digesting the other one first. But the compiler works sequentially. Bummer.

However, when you refer to some type only to make pointer to its objects, and you don't call methods on this pointers and you don't dereference those pointers, then you don't need the type definition. You only need to show that it is some class type, so that the compiler knows that this is not syntactical error (typo). What you added finally, with "class secondWindow" is called forward declaration and does exactly that. It announces (declares) a type, without defining it.

So, my guess is this - in the translation unit (whichever it was) thirdWindow came before secondWindow, which actually means that the preprocessor had to include secondWindow.h first, which included thirdWindow.h, which included secondWindow.h again, but the guarding condition of the header file kicked in this time. In other words, there is no secondWindow definition when thirdWindow tries to declare pointer member to secondWindow object. So, it doesn't work without the forward declaration that you added later.

As a rule of thumb, it is advisable to use forward declarations instead of including entire headers whenever possible. This is rarely done for course projects, but it leads to smaller compilation times for bigger projects.

Additionally, note this. If the implementation of the methods of some class A refer to some type B in a way that requires the complete type definition (and not just forward declaration), you can still introduce A before B, and define the methods after both A and B are defined. The only case where this is a problem is when the type A is generic or the methods of A are inline (, because in both of those cases the method definitions are also in the header file next to the definition of A itself.) If A is generic, forward declaration of B can still save your methods, because they are not compiled before they are instantiated, and the type B will be hopefully introduced before the point of instantiation. If A is not generic, but the methods that require B are inline, then bummer. I personally don't know what you can do :(

Regards
Hi aatwo,
I'm a QT learner too and I want help you if you can show you code fully.
My msn and email as bellow:
aibyfaku@hotmail.com
leafaku@gmail.com
I'm a little late, but this article might help:

http://cplusplus.com/forum/articles/10627/ <- specifically, see sections 4,5


Basically you need to forward declare your windows instead of #include the other headers (as simeonz said).
Thanks to all of you!

@Disch, thanks for that article gem! I found it to be a very interesting read and it definitely taught me a lot :D

@ aiby, all of the relevant code is in my first post;) If you make three classes in QT and organise them in a similar manner to my description you will have all of the relevant code to recreate the problem I was having should you wish to see for your self :)

@ Simeonz, thank you Simeonz for your kind and detailed reply. It actually seems like an obvious concept after your enlightening response and it makes a lot of sense to me now, kind of like why you must forward declare any functions that you write below your main function...

1
2
3
4
5
6
7
8
9
void myFunction();

int main(){
	return myFunction();
}

void myFunction(){
	return 1;	
}


... instead of ...

1
2
3
4
5
6
7
int main(){
	return myFunction();
}

void myFunction(){
	return 1;	
}


I have started to forward declare all my classes now and I haven't seen the issue since so thanks again! You are a true legend! :)
Than I managed to hide my confusion successfully. :) Glad to be of help though.
Topic archived. No new replies allowed.