Forward declaration in C++ not working

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • ducttape
    New Member
    • May 2009
    • 15

    Forward declaration in C++ not working

    myProgram.cpp:

    Code:
    class childClass
    {
              parentClass parent;  //reference to class that created this child
              childClass(parentClass p)
              {
                        parent = p;
              }
    };
    
    class parentClass
    {
              childClass child(this);
    };


    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:


    Code:
    [B]class parentClass;[/B]
    
    class childClass
    {
              parentClass parent;  //reference to class that created this child
              childClass(parentClass p)
              {
                        parent = p;
              }
    };
    
    class parentClass
    {
              childClass child(this);
    };
    But this code then gives the error: "error C2079: 'myProgram::par ent' 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.
  • Banfa
    Recognized Expert Expert
    • Feb 2006
    • 9067

    #2
    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
    Code:
              parentClass parent;  //reference to class that created this child
    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
    Code:
              parentClass &parent;  // reference to class that created this child
              parentClass *parent;  // pointer to class that created this child
    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)

    Code:
    parentClass::parentClass() :
              childClass child(this)
    {
    }
    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?

    Comment

    • scruggsy
      New Member
      • Mar 2007
      • 147

      #3
      Code:
               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:
      Code:
      class Mystery;
      
      class Me {
        Mystery * mysteryPtr;   // this is fine
        void Do(Mystery& mystery);   // fine
        Mystery mystery;    // not fine
        Mystery::InnerClass inner;  // not fine
      };
      
      class Mystery {
        class InnerClass {
          //
        }
      };
      It seems to me if you just want ChildClass to remember its parent you would make parent a parentclass* or parentclass&.

      Comment

      • ducttape
        New Member
        • May 2009
        • 15

        #4
        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:

        Code:
        class childClass
        {
                  [B]parentClass* parent;[/B]  //reference to class that created this child
                  childClass(parentClass p)
                  {
                            parent = p;
                  }
        };
        
        class parentClass
        {
                  childClass child(this);
        };
        am I getting closer to the right idea? I will start reading those links you posted, thanks very much.

        Comment

        • Banfa
          Recognized Expert Expert
          • Feb 2006
          • 9067

          #5
          Close but you need a * in

          childClass(pare ntClass *p)

          Comment

          • ducttape
            New Member
            • May 2009
            • 15

            #6
            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..)

            Code:
            #include "stdafx.h"
            #include <iostream>
            
            using namespace std;
            
            class parentClass; //forward declaration of parentClass
            
            class childClass
            {
            public:
            	parentClass * parent;
            	int someVar;
            
            	childClass(){} //default constructor
            
            	void setParent(parentClass *p)
            	{
            		parent = p;
            	}
            
            	void displayParentVar()
            	{
            		someVar = (*parent).parentVar;
            		cout << someVar;
            	}
            
            };
            
            class parentClass
            {
            public:
            	childClass child;
            	int parentVar;
            
            	parentClass()
            	{
            		parentVar = 10;
            		child = childClass();
            		child.setParent(this);
            	}
            };
            
            int _tmain(int argc, _TCHAR* argv[])
            {
            	parentClass myClass();
            	return 0;
            }
            can you see what's wrong here?

            Comment

            • Banfa
              Recognized Expert Expert
              • Feb 2006
              • 9067

              #7
              The problem is you are still trying to use parentClass before it has been defined in childClass::dis playParentVar

              Code:
              class childClass
              {
              public:
              //HERE 
                  void displayParentVar()
                  {
                      someVar = (*parent).parentVar;
                      cout << someVar;
                  }
               
              };
              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

              Code:
              // Forward declare parentClass
              
              class childClass
              {
              // ... correct stuff snipped
              public:
                  void displayParentVar();
              };
              
              // Declare parentClass
              
              // definition of child class member functions
              void childClass::displayParentVar()
              {
                  someVar = (*parent).parentVar;
                  cout << someVar;
              }
              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).paren tVar;

              is

              someVar = parent->parentVar;

              Oh and that declaring someVar as a class variable is superfluous as it is only used locally to displayParentVa r so could be declared locally to that function.

              Comment

              • ducttape
                New Member
                • May 2009
                • 15

                #8
                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.

                Comment

                • JosAH
                  Recognized Expert MVP
                  • Mar 2007
                  • 11453

                  #9
                  Use a guard that protects the preprocessor from going all the way down:

                  Code:
                  // parent.h:
                  #ifndef PARENT_H
                  #define PARENT_H
                  #include <child.h>
                  
                  // your parent.h stuff here
                  
                  #endif
                  
                  // child.h:
                  #ifndef CHILD_H
                  #define CHILD_H
                  #include <parent.h>
                  
                  // your child.h stuff here
                  
                  #endif
                  kind regards,

                  Jos

                  Comment

                  • ducttape
                    New Member
                    • May 2009
                    • 15

                    #10
                    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.

                    Code:
                    //parentClass.h
                    #ifndef PARENTCLASS_H
                    #define PARENTCLASS_H
                    
                    #include <string>
                    #include "childClass.h" //if I leave this out it works
                    
                    using namespace std;
                    class parentClass
                    {      
                      public:
                          //constructor   s
                          parentClass(string newName);
                          //variables
                          string name;
                          //methods
                          int someNumber();
                          void setName(string newName);
                    };
                    
                    #endif
                    Code:
                    //childClass.h
                    #ifndef CHILDCLASS_H
                    #define CHILDCLASS_H
                    
                    #include "parentClass.h" 
                    
                    #include <string>
                    using namespace std;
                    class childClass
                    {
                      private:
                          parentClass* parent;  //reference to class that created this child
                      public:
                          childClass(parentClass* p);
                          string whoIsMyDaddy();
                    };
                    
                    #endif
                    Code:
                    //childClass.cpp
                    #include "stdafx.h"
                    #include "childClass.h"
                    
                    childClass::childClass(parentClass* p)
                    {
                      parent = p;
                    }
                    
                    string childClass::whoIsMyDaddy()
                    {
                      return parent->name;
                    }
                    Code:
                    // parentClass.cpp
                    #include "stdafx.h"
                    #include "parentClass.h"
                    
                    parentClass::parentClass(string newName)
                    {
                      name = newName;
                    }
                                                    
                    int parentClass::someNumber() 
                    {
                      return 4;
                    }
                    
                    void parentClass::setName(string newName)
                    {
                      name = newName;
                    }
                    Code:
                    //main.cpp
                    #include "stdafx.h"
                    
                    #include <iostream>
                    #include <string>
                    #include "childClass.h"
                    
                    using namespace std;
                    
                    int main(int argc, char *argv[])
                    {
                        cout << "creating new parent class with name 'alex':\n";
                        parentClass* p = new parentClass("alex");
                        cout << "name from parent: " << p->name << "\n";
                        cout << "creating new child class for parent:\n";
                        childClass* c = new childClass(p);
                        cout << "name from child:  " << c->whoIsMyDaddy()<< "\n\n";
                        
                        //change name and see what happens..
                        cout << "changing parent name to 'Ivan':\n";
                        p->setName("Ivan");
                        
                        cout << "name from parent: " << p->name << "\n";
                        cout << "name from child:  " << c->whoIsMyDaddy()<< "\n";
                        
                    	while(true);
                    
                    	return 0;
                    }

                    Comment

                    • JosAH
                      Recognized Expert MVP
                      • Mar 2007
                      • 11453

                      #11
                      Originally posted by ducttape
                      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.
                      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

                      Comment

                      • ducttape
                        New Member
                        • May 2009
                        • 15

                        #12
                        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

                        Comment

                        • ducttape
                          New Member
                          • May 2009
                          • 15

                          #13
                          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:

                          informationClas s
                          int someVar;

                          childClass(info rmationClass i) //constructor
                          informationClas s * info = i;

                          parentClass
                          childClass children[1000]
                          informationClas s info;

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

                          I'll let you know how it goes.

                          Comment

                          • JosAH
                            Recognized Expert MVP
                            • Mar 2007
                            • 11453

                            #14
                            Originally posted by ducttape
                            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.
                            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

                            Comment

                            • Banfa
                              Recognized Expert Expert
                              • Feb 2006
                              • 9067

                              #15
                              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 "parentClas s.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.

                              Comment

                              Working...