473,714 Members | 2,050 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Forward declaration in C++ not working

15 New Member
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::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.
May 8 '09 #1
26 14719
Banfa
9,065 Recognized Expert Moderator Expert
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 New Member
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
ducttape
15 New Member
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,065 Recognized Expert Moderator Expert
Close but you need a * in

childClass(pare ntClass *p)
May 8 '09 #5
ducttape
15 New Member
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,065 Recognized Expert Moderator Expert
The problem is you are still trying to use parentClass before it has been defined in childClass::dis playParentVar

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).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.
May 8 '09 #7
ducttape
15 New Member
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 Recognized Expert MVP
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
ducttape
15 New Member
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

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

Similar topics

2
4512
by: Plok Plokowitsch | last post by:
After over a decade of programming in C++ I seem to have missed some substantial point (mental note: am I getting too old for this?). A little bit of help would be *very* appreciated. I'm trying to gather various different classes into a common namespace using typedefs: class QWidget {}; class MyListview {}; namespace gui
3
20604
by: Michael Sgier | last post by:
Hello with the original code below I get the error: "forward declaration of `struct CPlayer'" class CPlayer; // what does this do? Instantiate the class CPlayer? // what's a forward declaration? class CEnemy : public CEntity { public:
3
2241
by: Eckart Haug | last post by:
I'm working with C# objects from managed C++ using the gcroot template. There'a a C++ header containing the definition of a C++ class: #using <mscorlib.dll> #include <vcclr.h> #using <CSharpModule.dll> class CPlusPlusClass
0
913
by: Eckart Haug | last post by:
Thanx for the quick reply. Your suggestion would mean that I'd have to add a C# reference to any C++ module that includes the C++ header. There's lots of them and I'd (for the moment) like to have the C++ <-> C# interface encapsulated in one C++ module. Vladimir Nesterovsky wrote: > You can add your C# assembly in a project's references thus you may not import it explicitly. > --
1
5707
by: HappyHippy | last post by:
Hi, I have a question about forward declaration of classes. First of all, a simple working example, which compiles and works with no problems. ///file class_a.h:/ #ifndef __CLASS_A_H__ #define __CLASS_A_H__
1
5413
by: toton | last post by:
Hi, I have two namespace contains class InkFrame and PrefDialog respectively. PrefDialog needs InkFrame to show the dialog over the frame. It also stores a pointer to InkFrame inside it. Now I want InkFrame to be forward declared in the PrefDialog header file rather than to be included. I want to include it in the cpp file instead. There is no harm including it in PrefDialog header, and it works. But I want to save some compilation time....
6
8616
by: Hunk | last post by:
Hi I have a question on usage of forward declarations of templates. While searching through this group , people suggested using forward declarations and typedefs for templates as // in myfile.h template<typename T,typename R> class some_class;
4
5371
by: Steve | last post by:
Hi, I always though to return an instance of a class by value, it had to be defined - i.e. forward declaration isn't good enough? Consider the following code snippet: class RGBA; class Colour
7
5081
by: RedLars | last post by:
Need some help with a couple of questions. Previously I have seen this declaration; struct Student { int age; char name; }; and this definition of an object; struct Student stud;
0
8715
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
9322
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
1
9092
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
7963
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
6641
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5964
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4472
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
4734
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
2
2531
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.