Forward Declaration?, Include guard?, or something else?

991 views Asked by At

I have a parent class called Token

I have two children classes, ErrorToken and EndToken. Each one of those classes needs to be able to create an object of the other, and return it through a function call. Each one has their own seperate header class.

So, ErrorToken needs to be able to create a new EndToken object and return it, and EndToken needs to be able to create a new ErrorToken object and return it.

What is going to be the best way to succeed at doing this? I would prefer if it was as cross compiler compatible as possible so I don't want to use pragma once. (but thats essentially what I'm looking for).

Ideally I'd like to be able to do something like this...

#ifndef ErrorToken
#include "ErrorToken.h"
#endif

But that never seems to work (and my guess is that that is wrong? can someone help me understand why?).

My understanding of forward declaration is that it only works on function signatures and pointers (is that correct?), so I don't think that will work for my situation since I need it to be able to run the constructor...or does the compiler just need to be aware that the constructor exits at that moment?

2

There are 2 answers

6
Kerrek SB On BEST ANSWER

Well, use forward declarations. As you said, there are millions of explanations out there, and now there are millions and one:

ErrorToken.h:

#ifndef H_ERROR_TOKEN
#define H_ERROR_TOKEN

#include "Token.h"

class EndToken;

class ErrorToken : public Token
{
public:
    EndToken makeEndToken();
};

#endif

EndToken.h:

#ifndef H_END_TOKEN
#define H_END_TOKEN

#include "Token.h"

class ErrorToken;

class EndToken : public Token
{
public:
    ErrorToken makeErrorToken();
};

#endif

In each implementation file, you can now happily include both headers:

#include "ErrorToken.h"
#include "EndToken.h"

ErrorToken EndToken::makeErrorToken()
{
    return ErrorToken();   // example
}

EndToken ErrorToken::makeEndToken()
{
    return EndToken();
}

As @James Kanze pointed out, you might be confused about how C++ works. The following code may be more in line with the kind of behaviour you expect from Java, and makes more sense in a polymorphic design way:

class Token { virtual ~Token()  {} };

class ErrorToken : public Token
{
    std::unique_ptr<Token> makeEndToken();
};

class EndToken : public Token
{
    std::unique_ptr<Token> makeErrorToken();
};

std::unique_ptr<Token> EndToken::makeErrorToken()
{
    return { new ErrorToken; }
}

std::unique_ptr<Token> ErrorToken::makeEndToken()
{
    return { new EndToken; }
}

Since you're only handling objects via base pointers, the headers don't need to know anything about other derived classes. (I leave it to you to subdivide the code into files; each block goes into a separate file.)

2
doctorlove On

Why is your

#ifndef ErrorToken

wrong? First, put it in the header file, second make sure something actually defines ErrorToken


Include guards and forward declarations might get you out of this hole:

//"ErrorToken.h"
#ifndef ERROR_TOKEN_INCLUDED
#define ERROR_TOKEN_INCLUDED

class EndToken;

class ErrorToken
{
public:
    EndToken DoSomething();
};
#endif

//"EndToken.h"
#ifndef END_TOKEN_INCLUDED
#define END_TOKEN_INCLUDED
class ErrorToken;

class EndToken
{
public:
    ErrorToken DoSomething();
};
#endif

There have been several previous discussions on circular dependencies: here, here and here
You can then do the bulk of the work in the cpp file, where you include the other header, so it knows the full class definition, rather than just the forward declaration, and can use it.
Step back first though and ask, "Do they really need to know about each other?"