Jump to content
  • Advertisement
Sign in to follow this  
averisk

Why not forward-declare everything? (c++)

This topic is 4720 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've found that it's useful to forward declare the types I'm using in my class header files (or at least the ones that aren't allocated on the stack ). This means that I can just put the include in the CPP file and will allow for a quicker compile Also, clients of the class that don't depend on the forward-declared type are not forced to include it. I do this a lot... and lately I've been thinking I should have a file for each package that forward declares everything in said package Is this a good or bad idea?

Share this post


Link to post
Share on other sites
Advertisement
Just to ensure we're on the same page... you're talking about doing:
class foo;
class bar {
foo * baz;
};

Instead of:
#include <foo.h>
class bar {
foo * baz;
};

?

There's pros and cons to both.

For the first, you have:
Pro: Faster compiles, dosn't lead to cyclilic dependancies
Con: Dosn't include the class body in case you want to call them from an inline function, or have said class a member (as in, not a pointer).
Possibly Pro or Con, depending on opinion: Bugs won't be marked in the header if you rename foo to pie - they'll only appear in code directly using that class (e.g. class implementation).

For the second, you have:
Pro: can include multiple types, allows one to specify the class as a member, and otherwise freely use the type(s).
Con: Increases compile time.


Personally, I prefer the later unless I'm:

1) Breaking a cyclilic dependancy
2) Intentionally hiding the definition (for when I have classes like so:)

class foo {
class implementation; //forward decleration!!!
boost::shared_ptr< implementation > impl;
public:
//...public methods...
};


That way, if implementation has to deal with voodoo magic (unpleasant namespace/definespace pollution) or is OS-specific (e.g. the network lib I last used this pattern in has an implementation of "implementation" for winsock (for windows, obviously) and another for BSD sockets (for linux)) it dosn't end up screwing over the using code.

If something's taking too long to compile, I break it up and find out which bits are taking so long (e.g. large boost::spirit grammars) and toss them into a source file where they belong. Since I use automatic dependancy-based building, this means I only take the hit when I need to recompile that file.

Share this post


Link to post
Share on other sites
Thanks for the response.

Quote:
Original post by MaulingMonkey
Just to ensure we're on the same page... you're talking about doing:
class foo;
class bar {
foo * baz;
};

Instead of:
#include <foo.h>
class bar {
foo * baz;
};

?


Yes, except more along the lines of this:

foo.h

#include <math.h>
class foo
{
vector* vec;
matrix3* matrix;
};


math.h

class vector;
class matrix3;
class matrix4;
class quaternion;
class vector2;
// etc..


Quote:

There's pros and cons to both.

For the first, you have:
Pro: Faster compiles, dosn't lead to cyclilic dependancies
Con: Dosn't include the class body in case you want to call them from an inline function, or have said class a member (as in, not a pointer).


I'm not too worried about this. I can always inline functions later in development, when I need the speed. Plus, if the function ever changes I don't have to re-compile, just re-link.

Quote:

Possibly Pro or Con, depending on opinion: Bugs won't be marked in the header if you rename foo to pie - they'll only appear in code directly using that class (e.g. class implementation).


I don't really consider this a big deal. If I forward declare always, whenever it is possible, then these errors become pretty obvious after the first few.

Quote:

For the second, you have:
Pro: can include multiple types, allows one to specify the class as a member, and otherwise freely use the type(s).
Con: Increases compile time.


When you say include multiple types, do you mean through just a single header? Yeah that is a valid argument.

I think I'm going to try using a common header that forward-declares everything and see how it goes.
Another con that comes to mind is that adding a new class to this file will result in a full recompile. But I'm assuming I won't be adding new classes very often..

Share this post


Link to post
Share on other sites
I use forward declarations all the time. The time savings in compilation far outweigh any of the cons listed above. They don't really even bother me that much...

There is an annoyance of sometimes being forced to change it to an include if I want to inline a function like he mentioned but it's just a judgment call whether to inline it or keep it in the source file.

For me it's a little more work than just including everything in the header but it just feels more correct.

Share this post


Link to post
Share on other sites
Having only declarations in headers force you to use pointers instead of value types. This may (Bridge pattern) or may not (dynamic allocations everywhere) be desirable. You've got to judge on a case by case basis.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Be aware that you will also not be able to use most smart pointers to hold forward declared classes as class members. (If you do, your destructor will not be called, but rather a compiler default-constructed one will.) This can be a hard memory leak to find, as well, because it is so easy to think such great things about smart pointers. Fortunately boost::shared_ptr will probably give you at least a warning. std::auto_ptr probably won't, though.

David

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
(your destructor for the forward declared class, that is)

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Be aware that you will also not be able to use most smart pointers to hold forward declared classes as class members. (If you do, your destructor will not be called, but rather a compiler default-constructed one will.) This can be a hard memory leak to find, as well, because it is so easy to think such great things about smart pointers. Fortunately boost::shared_ptr will probably give you at least a warning. std::auto_ptr probably won't, though.

David


Eh, are you sure about that?

I've got a forward declared class held in a smart pointer in my app, and the destructor is most definitely being called. Is this another case of Visual Studio 2002 deviating from the standard then?

Actually, from checking the boost::shared_ptr documentation, it says that the type doesn't have to be a complete type when you declare a shared_ptr, surely this wouldn't be the case if something as dangerous as the destructor not being called was happening?

Share this post


Link to post
Share on other sites
Oh, I thought you meant like this:


// Doing forward declaration

void foo;

int main() {
foo();
}

void foo() {}

// vs. not doing forward declaration

void foo() {}

int main() {
foo;
}



In *that* case I definitely prefer to avoid the forward declarations as much as possible, because it's extra typing you have to maintain. Within the one source file, you should only put them where they're needed due to cyclic dependency - because it then serves as documentation of said dependency.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!