469,271 Members | 1,343 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,271 developers. It's quick & easy.

Forward declaration in C++ not working

myProgram.cpp:

Expand|Select|Wrap|Line Numbers
  1. class childClass
  2. {
  3.           parentClass parent;  //reference to class that created this child
  4.           childClass(parentClass p)
  5.           {
  6.                     parent = p;
  7.           }
  8. };
  9.  
  10. class parentClass
  11. {
  12.           childClass child(this);
  13. };


This would work fine in Java, which I'm more accustomed to, but I need to do it in C++. The problem being that C++ compilers read classes sequentially and this code gives the error "error C2061: syntax error : identifier 'parentClass'" because it does not know what parentClass is yet. All the advice on the internet says to create a forward declaration for parentClass BEFORE the definition for childClass, and then keeping the definition for parentClass after childClass, such:


Expand|Select|Wrap|Line Numbers
  1. class parentClass;
  2.  
  3. class childClass
  4. {
  5.           parentClass parent;  //reference to class that created this child
  6.           childClass(parentClass p)
  7.           {
  8.                     parent = p;
  9.           }
  10. };
  11.  
  12. class parentClass
  13. {
  14.           childClass child(this);
  15. };
But this code then gives the error: "error C2079: 'myProgram::parent' uses undefined class 'parentClass'"

I've tried swapping around the classes, too, so that parentClass comes before childClass with a forward declaration for childClass. This gives the same error. I've also tried putting the classes in seperate *.cpp files. I must admit I don't really know how to do this, but the way I did it resulted in the same error (...uses undefined class...).

What do you think? I'm a decent Java and C# programmer but C++ is just taking the piss.
May 8 '09 #1
26 13434
Banfa
9,064 Expert Mod 8TB
When you forward declare a class you are just telling the compiler that a class with that name is going to exist. You can declare pointers and references from a forward declaration but you can not declare an instance of the class because the type is incomplete, the compiler only knows its name not its structure.

You can only declare an instance of the class once the class has been fully declared.

Where you declare this
Expand|Select|Wrap|Line Numbers
  1.           parentClass parent;  //reference to class that created this child
  2.  
you are trying to declare a instance of the parentClass, the compiler can't do this because it does not know the structure of parentClass yet. Also this is not a reference. If you want a reference or a pointer you need to declare them like this
Expand|Select|Wrap|Line Numbers
  1.           parentClass &parent;  // reference to class that created this child
  2.           parentClass *parent;  // pointer to class that created this child
  3.  
My extremely limited and rusty Java is telling me that all objects are held as references in Java so you don't need to make this distinction between object, reference and pointer. Both of these declarations would work with a forward declaration.

Again the constructor you have defined for childClass takes a parentClass object as a parameter. Something that is not recomend in C++ (or C), passing whole objects of indeterminate size as parameters. Where you try to initialise it in parentClass you pass this, a pointer to the class.

I suggest you read up about C++ References and C pointers

I also note you appear to be trying to initialise child in your parentClass. The correct way to do this would be by attaching the initialisation to the definition of the constructor of the parentClass (the syntax you have used is being introduced in C++0x I believe)

Expand|Select|Wrap|Line Numbers
  1. parentClass::parentClass() :
  2.           childClass child(this)
  3. {
  4. }
  5.  
However you need to be careful when using the this pointer from the constructor as the class is not fully constructed yet so you can get odd side effects if you try to access members of the class.

Try reading Should you use the this pointer in the constructor?
May 8 '09 #2
scruggsy
147 100+
Expand|Select|Wrap|Line Numbers
  1.          parentClass parent;  //reference to class that created this child
In Java I assume this would be a reference to some object. In C++ it is not. parent here is an object embedded into a child object. C++ needs to know the class layout of parent in order to determine the class layout of child.

Forward declarations simply tell the compiler a class exists:
Expand|Select|Wrap|Line Numbers
  1. class Mystery;
  2.  
  3. class Me {
  4.   Mystery * mysteryPtr;   // this is fine
  5.   void Do(Mystery& mystery);   // fine
  6.   Mystery mystery;    // not fine
  7.   Mystery::InnerClass inner;  // not fine
  8. };
  9.  
  10. class Mystery {
  11.   class InnerClass {
  12.     //
  13.   }
  14. };
It seems to me if you just want ChildClass to remember its parent you would make parent a parentclass* or parentclass&.
May 8 '09 #3
OK!! That's really helpful, I was assuming I was passing through a pointer just as in Java. As you may have guessed the code I posted was just a conceptual version, in the actual version the parent (a particle system) contains an array of children (particles) - I just want each child to have a pointer to its parent so it can ask it what it needs to look like when it respawns.

So, if I do something like this:

Expand|Select|Wrap|Line Numbers
  1. class childClass
  2. {
  3.           parentClass* parent;  //reference to class that created this child
  4.           childClass(parentClass p)
  5.           {
  6.                     parent = p;
  7.           }
  8. };
  9.  
  10. class parentClass
  11. {
  12.           childClass child(this);
  13. };
  14.  
am I getting closer to the right idea? I will start reading those links you posted, thanks very much.
May 8 '09 #4
Banfa
9,064 Expert Mod 8TB
Close but you need a * in

childClass(parentClass *p)
May 8 '09 #5
Hi Banfa, I'm getting really close, I can feel it! I actually went and implemented that conceptual code in Visual Studio 2008 and I even got it to compile. So I added a variable to pass through to test it, and drew a blank. By the way, that article convinced me using "this" in the constructor was a bad idea. Here's the code, which results in the same error (..uses undefined class..)

Expand|Select|Wrap|Line Numbers
  1. #include "stdafx.h"
  2. #include <iostream>
  3.  
  4. using namespace std;
  5.  
  6. class parentClass; //forward declaration of parentClass
  7.  
  8. class childClass
  9. {
  10. public:
  11.     parentClass * parent;
  12.     int someVar;
  13.  
  14.     childClass(){} //default constructor
  15.  
  16.     void setParent(parentClass *p)
  17.     {
  18.         parent = p;
  19.     }
  20.  
  21.     void displayParentVar()
  22.     {
  23.         someVar = (*parent).parentVar;
  24.         cout << someVar;
  25.     }
  26.  
  27. };
  28.  
  29. class parentClass
  30. {
  31. public:
  32.     childClass child;
  33.     int parentVar;
  34.  
  35.     parentClass()
  36.     {
  37.         parentVar = 10;
  38.         child = childClass();
  39.         child.setParent(this);
  40.     }
  41. };
  42.  
  43. int _tmain(int argc, _TCHAR* argv[])
  44. {
  45.     parentClass myClass();
  46.     return 0;
  47. }
  48.  
can you see what's wrong here?
May 8 '09 #6
Banfa
9,064 Expert Mod 8TB
The problem is you are still trying to use parentClass before it has been defined in childClass::displayParentVar

Expand|Select|Wrap|Line Numbers
  1. class childClass
  2. {
  3. public:
  4. //HERE 
  5.     void displayParentVar()
  6.     {
  7.         someVar = (*parent).parentVar;
  8.         cout << someVar;
  9.     }
  10.  
  11. };
  12.  
All other references to parentClass in childClass just use a pointer to it but here you are trying to access a member of parentClass which is not defined yet (only forward declared) so you get an error.

Your sequence has to be
  1. Forward Declare parentClass
  2. Declare childClass (only using pointers/references to parentClass)
  3. Declare parentClass
  4. Define childClass
  5. Define parentClass

That is why we use separate headers(h) and code (cpp) files. We put the declaration of each class into 2 separate headers and the definition of each class into 2 separate code files. The 2 code files then both include both headers (in the same order normally) so that both classes are fully declared and then each file defines the member functions their own class in full view of those declarations.

Both headers are then included into the source file containing main so that you can create an instance of parentClass.

Your code needs this structure

Expand|Select|Wrap|Line Numbers
  1. // Forward declare parentClass
  2.  
  3. class childClass
  4. {
  5. // ... correct stuff snipped
  6. public:
  7.     void displayParentVar();
  8. };
  9.  
  10. // Declare parentClass
  11.  
  12. // definition of child class member functions
  13. void childClass::displayParentVar()
  14. {
  15.     someVar = (*parent).parentVar;
  16.     cout << someVar;
  17. }
  18.  
You can define class functions within the class declaration but those functions are automatically inlined so you should only do this for functions with a few lines of code (and that don't use a forward declared type).

Also note that the shorthand for

someVar = (*parent).parentVar;

is

someVar = parent->parentVar;

Oh and that declaring someVar as a class variable is superfluous as it is only used locally to displayParentVar so could be declared locally to that function.
May 8 '09 #7
I think I understand how headers work now. I can see the advantages and I'm going to split all my classes into seperate files from now on. So, I've split up the parent and the child class, made headers for them with their variables, and seperate .cpp files for their methods. The files all now #include each other.

The problem I'm getting now is error C1014: too many include files : depth = 1024. I figure its because class 1 includes class 2, which includes class 1, which includes class 2... I've tried the includes in many orders and left them out but because both classes need to know about each other I have a chicken and egg situation.

I can't see any way to resolve this because as soon as I make one class I need to have the other already made. I may have to go back to having it all in one file and use the order you suggested, but I'd really like to make it work with seperate files and headers.
May 10 '09 #8
JosAH
11,448 Expert 8TB
Use a guard that protects the preprocessor from going all the way down:

Expand|Select|Wrap|Line Numbers
  1. // parent.h:
  2. #ifndef PARENT_H
  3. #define PARENT_H
  4. #include <child.h>
  5.  
  6. // your parent.h stuff here
  7.  
  8. #endif
  9.  
  10. // child.h:
  11. #ifndef CHILD_H
  12. #define CHILD_H
  13. #include <parent.h>
  14.  
  15. // your child.h stuff here
  16.  
  17. #endif
  18.  
kind regards,

Jos
May 10 '09 #9
Thanks Jos, what do you mean, protect the preprocessor from going all the way down?

I added your code, it works if I leave out the #include in one of the classes, but if I put it in (which I obviously need to) it doesn't recognise it.

I include the code here in case I'm neglecting to tell you something.

Expand|Select|Wrap|Line Numbers
  1. //parentClass.h
  2. #ifndef PARENTCLASS_H
  3. #define PARENTCLASS_H
  4.  
  5. #include <string>
  6. #include "childClass.h" //if I leave this out it works
  7.  
  8. using namespace std;
  9. class parentClass
  10. {      
  11.   public:
  12.       //constructor   s
  13.       parentClass(string newName);
  14.       //variables
  15.       string name;
  16.       //methods
  17.       int someNumber();
  18.       void setName(string newName);
  19. };
  20.  
  21. #endif
  22.  
Expand|Select|Wrap|Line Numbers
  1. //childClass.h
  2. #ifndef CHILDCLASS_H
  3. #define CHILDCLASS_H
  4.  
  5. #include "parentClass.h" 
  6.  
  7. #include <string>
  8. using namespace std;
  9. class childClass
  10. {
  11.   private:
  12.       parentClass* parent;  //reference to class that created this child
  13.   public:
  14.       childClass(parentClass* p);
  15.       string whoIsMyDaddy();
  16. };
  17.  
  18. #endif
  19.  
Expand|Select|Wrap|Line Numbers
  1. //childClass.cpp
  2. #include "stdafx.h"
  3. #include "childClass.h"
  4.  
  5. childClass::childClass(parentClass* p)
  6. {
  7.   parent = p;
  8. }
  9.  
  10. string childClass::whoIsMyDaddy()
  11. {
  12.   return parent->name;
  13. }
  14.  
Expand|Select|Wrap|Line Numbers
  1. // parentClass.cpp
  2. #include "stdafx.h"
  3. #include "parentClass.h"
  4.  
  5. parentClass::parentClass(string newName)
  6. {
  7.   name = newName;
  8. }
  9.  
  10. int parentClass::someNumber() 
  11. {
  12.   return 4;
  13. }
  14.  
  15. void parentClass::setName(string newName)
  16. {
  17.   name = newName;
  18. }
  19.  
Expand|Select|Wrap|Line Numbers
  1. //main.cpp
  2. #include "stdafx.h"
  3.  
  4. #include <iostream>
  5. #include <string>
  6. #include "childClass.h"
  7.  
  8. using namespace std;
  9.  
  10. int main(int argc, char *argv[])
  11. {
  12.     cout << "creating new parent class with name 'alex':\n";
  13.     parentClass* p = new parentClass("alex");
  14.     cout << "name from parent: " << p->name << "\n";
  15.     cout << "creating new child class for parent:\n";
  16.     childClass* c = new childClass(p);
  17.     cout << "name from child:  " << c->whoIsMyDaddy()<< "\n\n";
  18.  
  19.     //change name and see what happens..
  20.     cout << "changing parent name to 'Ivan':\n";
  21.     p->setName("Ivan");
  22.  
  23.     cout << "name from parent: " << p->name << "\n";
  24.     cout << "name from child:  " << c->whoIsMyDaddy()<< "\n";
  25.  
  26.     while(true);
  27.  
  28.     return 0;
  29. }
  30.  
May 10 '09 #10
JosAH
11,448 Expert 8TB
@ducttape
Something else is wrong then (incorrect file name?) because the guard mechanism is a well know idiom in C and C++ programming; you see it all over the world; it prevents the preprocessor from going down including A from B from A from B from A etc.

kind regards,

Jos
May 10 '09 #11
Ok, that sounds like exactly what I need, I'll read up about the "guard mechanism" - is that what it's called? Perhaps you only need to put that guard around one of the classes to stop the preprocessor from going all the way down. I'll experiment and post my findings here. Thanks again
May 10 '09 #12
I read up about include guards, I found this link helpful: http://www.efnetcpp.org/wiki/Include_guards

Unfortunately it seems like it's not what I need after all becuase it doesn't solve the original problem that A needs to have B defined and B needs to have A defined.

This is really messing with my head I feel like I'm going a bit crazy thinking about it. I'm kind of coming to the conclusion that you can't have two different classes with references to each other, ie. no instance of a class can know the instance that created it.

I'm going to try a different route to solve my problem. I need to pass information between the "parent" and the "child", specifically, once the parent has created an array of children, the children need to periodically as the parent what they need to look like. So if I create a third class which has variables to store all that information, thus:

informationClass
int someVar;

childClass(informationClass i) //constructor
informationClass * info = i;

parentClass
childClass children[1000]
informationClass info;

hopefully both child and parent will be able to access someVar.

I'll let you know how it goes.
May 10 '09 #13
JosAH
11,448 Expert 8TB
@ducttape
You may not realize it (yet) but those guards are exactly what you want; especially when those mutually dependant classes show up. It surprises me that you got an error when you included that file again. What exactly was the error that got reported by the compiler? It must've been something else ...

kind regards,

Jos
May 10 '09 #14
Banfa
9,064 Expert Mod 8TB
It's not the files names. It's a basic problem with including headers mutually including other headers.

With your files as posted when the first file you include is parentClass.h it goes wrong. This is what happens.
  1. Include parentClass.h set inclusion protection
  2. parentClass.h: #include "childClass.h"
  3. Include childClass.h set inclusion protection
  4. childClass.h: #include "parentClass.h" (because childClass uses parentClass)
  5. Include parentClass.h, protection inclusion is set so none of the code of the file is actually processed. Note at this point although we have include parentClass.h twice we have not yet processed class parentClass at all.
  6. Back in childClass.h having finished processing parentClass.h from 5
  7. Process class childClass. Oh dear child class uses parentClass and that is not yet declared.

This works OK when childClass.h is included first because parentClass does not use childClass in this example. If it did you would be in trouble.

It is almost always an error for A.H to include B.H and B.H to include A.H Avoid it if at all possible. In fact that is what a forward declaration is for. childClass.h should forward declare parentClass rather than including parentClass.h

You can still include childClass.h in parentClass.h if you wish (and if parentClass.h actually uses childClass but that depends on your projects header inclusion policy. The 4 common ones are
[list=1]Headers mustn't include other headers[*]Project headers can include system headers but not other project headers[*]Project headers should include all the headers so that if they are included no other include statements need to be added to the place that they are include into.[*]Do whatever the hell you like and leave the mess for someone else to tidy up.[list]

My personal preference is for 2. I have seen plenty of commercial attempt 3 and end up with 4.
May 10 '09 #15
Banfa
9,064 Expert Mod 8TB
FYI Jos these are the errors

childClass.h:13: error: ISO C++ forbids declaration of ‘parentClass’ with no type
childClass.h:13: error: expected ‘;’ before ‘*’ token
childClass.h:15: error: expected `)' before ‘*’ token

when compiling parentClass.cpp
May 10 '09 #16
JosAH
11,448 Expert 8TB
@Banfa
Ah, I get it: in child.h the parentClass should be declared and in parent.h the childClass should be declared (that's what this entire discussion started).

The two next lines of error messages are just bogus given the first error.

kind regards,

Jos

edit: @OP: don't mix up 'definition' with 'declaration'; i.e. simply declare the existense of the other class in your .h files
May 10 '09 #17
It's actually quite an interesting discussion, I always did wonder it was bad programming form on my part but I do it all the time in Java and C#: for the class that instantiates another class to pass itself through as a parameter so that the second class can refer to it as its parent. It's a really neat trick which saves bucketloads of code and I'd imagine saves bucketloads of memory. But if C++ doesn't allow it easily perhaps that's indicative of poor form.

I'm going to try my third inbetween class method and see how that works. I'll hopefully have it done by tomorrow and I'll report back. Till then, take care guys thanks for all your help.
May 10 '09 #18
JosAH
11,448 Expert 8TB
@ducttape
You don't need an inbetween class; define your classes in a .h file and only make the class refer to the other class as a reference or a pointer. Declare the existence of the other class before you define the class that belongs in your .h header file.

Also guard your two header files to prevent the preprocessor to dive in its recursion when two header files include eachother.

In your .cpp files you are free to do what you want after including the two header files. The classes are defined in the header files already.

Don't make a simple mistake of yours lead you to a complicated (artificial) solution. It's just the compiler that is too stupid to understand that an arbitrary word is supposed to be the name of a class. C++ is not Java.

kind regards,

Jos

ps. I cooked up a little skeleton:

Expand|Select|Wrap|Line Numbers
  1. #ifndef CHILD_H
  2. #define CHILD_H
  3.  
  4. // tell the compiler this class exists:
  5. class parentClass;
  6.  
  7. // define your class here:
  8. class childClass {
  9.    // only refer to the parent class as
  10.    // parentClass* aPointerToParent; or
  11.    // parentClass& aParentReference
  12. }
  13.  
  14. #endif
  15.  
Do the same for your parentClass (childClass <--> parentClass) and program away in your parent.cpp and child.cpp files where both classes are defined (because of the inclusion of the .h files).
May 10 '09 #19
Banfa
9,064 Expert Mod 8TB
@JosAH
Are you saying that Java does treat any arbitrary keyword as a class (or class name)?

More seriously does Java void this problem because it does >1 pass compilation and therefore has visibility of all class names after the first pass?
May 10 '09 #20
JosAH
11,448 Expert 8TB
@Banfa
Nope, not saying that but the Java compiler is able to peek in compiled code; just enough to judge the code to be compiled for validity. That's where Java's 'classpath' comes in and that's why a lot of beginners are having trouble with it: they expect a certain level of psychic abilities from the compiler ;-)

kind regards,

Jos
May 10 '09 #21
It's working with the forward declaration of childClass instead of the #include childClass. Thanks so much for your patience. In retrospect I'm embarassed that I too expected psychic abilities from the compiler. It just seemed so natural.

One of the starkest differences is that Java's variables are pointers by default. To create a new instance you actually have to use the "new" keyword. It seems to me to be a much more efficient way of doing things.

For anyone else reading this thread, if you change the code in the first block in post 10, line 6, from

#include "childClass.h"

to

class childClass;

it works. I still wonder about the quality of my code using a trick like that but it seems to work fine. Thanks again Banfa and JosAH you've been most kind.
May 11 '09 #22
JosAH
11,448 Expert 8TB
@ducttape
C++ can do that too but C++ carries a C heritage where objects can have value semantics, i.e. you don't just have a pointer (reference) to an object, as in Java, but you can plough your way through an object itself, anyway you want. That heritage is a mixed blessing.

kind regards,

Jos

ps. remember: everything needs to be declared before you can even mention it in other code. A definition counts as a declaration.
May 11 '09 #23
I spoke too soon. I forgot my code example doesn't actually have a child belonging to the parent. If I try to add one, even if I forward declare childClass, I get error C2079: 'parentClass::c' uses undefined class 'childClass'. This is from simply adding a childClass variable to parentClass, not even from instantiating it.
May 11 '09 #24
JosAH
11,448 Expert 8TB
@ducttape
Here we go again: declare everything before you use it; if you just declare a class you can't use any of its members (because the compiler doesn't know about them), nor can you use the size of an object of the class. The guard mechanism as well as the forward declaration of the class(es) are enough to solve your problem.

kind regards,

Jos
May 11 '09 #25
Thanks for your patience. Everything was declared and I was not using anything. I have guard mechanisms around all of my classes. I forward declared childClass and then added a childClass variable to parentClass. In childClass, I forward declared parentClass and added a parentClass variable to childClass. So far I have not defined anything or tried to use any of its members. If simply adding a variable requires you to define the class then logically the problem is unsolvable because declaring the variables is part of each classes definition.

I suspect you are right though, which means there must be some other error in my code that is causing the problem. I will try to figure it out when I have more time, unfortunately I need to get on with this project so I'm using my horrible artificial solution wth a third class. At least this way I never have to use "this" references or anything.

If I ever get it working I'll post my findings here. Till then, then.
May 11 '09 #26
Banfa
9,064 Expert Mod 8TB
This is what we have been through in posts 1-6 of this thread.

The forward declaration does not allow you to declare a instance of the class but does allow you to declare a pointer to reference to the class.

You can then instantiate this pointer/reference using new in the class constructor.

You can do it where you define the constructor but not where you declare the class because the cpp file holding the member function definitions can have visibility of the declarations of both classes but the class declarations in the 2 headers can not have mutual visibility of each other.

That is if the childClass declaration is visible to the parentClass then the parentClass declaration can not be visible to the childClass. But the childClass can have visibility of a forward declaration of the parentClass name allowing it to declare pointers and references.
May 11 '09 #27

Post your reply

Sign in to post your reply or Sign up for a free account.

Similar topics

2 posts views Thread by Plok Plokowitsch | last post: by
3 posts views Thread by Michael Sgier | last post: by
reply views Thread by Eckart Haug | last post: by
1 post views Thread by HappyHippy | last post: by
4 posts views Thread by Steve | last post: by
7 posts views Thread by RedLars | last post: by
reply views Thread by zhoujie | last post: by
reply views Thread by suresh191 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.