Sign in to follow this  
stratusfer

Separate headers for declaration and definition

Recommended Posts

[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif][size=3][left][background=rgb(250, 251, 252)]Hi everybody,[/background][/left][/size][/font][/color]

[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif][size=3][left][background=rgb(250, 251, 252)]I want to discuss a topic discussed in a previous thread: [/background][/left][/size][/font][/color]http://www.gamedev.net/topic/510239-separate-header-files-for-declarations-and-definitions/ : [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif][size=3][left][background=rgb(250, 251, 252)] declaration and definition of classes in separate header files.[/background][/left][/size][/font][/color]

[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif][size=3][left][background=rgb(250, 251, 252)]I have been reading the Scott Meyers's Effective C++ book (3ed edition):[font=courier new,courier,monospace][/font][/background][/left][/size][/font][/color][font=courier new,courier,monospace][i]Item31: Minimize compilation dependencies between files [/i][/font][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif][size=3][left][background=rgb(250, 251, 252)]but there are some points that are not so clear..[/background][/left][/size][/font][/color]

[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif][size=3][left][background=rgb(250, 251, 252)]I copy and past the same question of the old thread because it's what I wanted to ask:[/background][/left][/size][/font][/color]

[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif][size=3][left][background=rgb(250, 251, 252)]"I often use forward declaration of classes in my header files in order to avoid including unnecessary class headers and thus increasing dependencies (and therefore compile times). E.g.[/background][/left][/size][/font][/color]
[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif][size=3][left][background=rgb(250, 251, 252)]
[color=blue][font=CourierNew]class[/font][/color] A; [color=gray][font=CourierNew]// Avoids having to include "A.h"[/font][/color]

[color=blue][font=CourierNew]class[/font][/color] B {
[color=blue][font=CourierNew]public[/font][/color]:
SomeFunction(A* parameter); [color=gray][font=CourierNew]// Compiler is happy, because A has been declared[/font][/color]
};

[/background][/left][/size][/font][/color]
[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif][size=3][left][background=rgb(250, 251, 252)]However, Scott Meyers [/background][/left][/size][/font][/color][i]Effective C++[/i][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif][size=3][left][background=rgb(250, 251, 252)] (3rd edition, page 144) suggests that it is best practise to actually put these forward declarations into a separate header file and include that instead of the forward declaration. For instance, in the above example class A would be split into 3 files: * A.h - The definitions * A.cpp - The implementations * A_fwd.h - The forward declarations (but no definitions or implementations) A_fwd.h would be included in the above code, rather than explicitly declaring class A. My query is though, is this really beneficial and do people really follow this practise? The justification for this best practise seems to be that if a class declaration changes then you only have to change it in one place (the declaration header). This doesn't see to make sense though for two reasons: * Why would my class declaration change? My definitions and implementations may but the forward declaration simply states that a class of that name exists. If I decided to rename a class then ok, but... * Even if I did change the declaration, I still have to visit all my class definitions & implementations and rename the class everywhere it's used! (This could be avoided if I'm using pimpl unless it's the interface class that's changing, but I don't think we need to get into that). Any thoughts? Cheers! :)"[/background][/left][/size][/font][/color]

Share this post


Link to post
Share on other sites
A type name may be more complex than simply the name of a class. For instance, it could be a typedef for a specific template instantiation (e.g. std::string is a typedef for std::basic_string<char>). Alternately, you could decide to change something from a separate class to a typedef to another class (e.g. changing from your homegrown string class to a typedef for an existing string class). Similarly you could have a forward declaration for a template class that you decide to add new template arguments to with default arguments.

Share this post


Link to post
Share on other sites
[quote name='stratusfer' timestamp='1341484528' post='4955921']
[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif][size=3][background=rgb(250, 251, 252)]My query is though, is this really beneficial and do people really follow this practise?[/background][/size][/font][/color]
[color=#282828][font=helvetica,arial,verdana,tahoma,sans-serif][size=3][background=rgb(250, 251, 252)][/quote][/background][/size][/font][/color]
I have always used forward declarations, then header for forward declarations for a component (say 5 or so related class). Now I'm starting to use per class headers for forward declaration, thanks to this thread.

[quote name='stratusfer' timestamp='1341484528' post='4955921']
[color=#282828][font=helvetica,arial,verdana,tahoma,sans-serif][size=3][background=rgb(250, 251, 252)]The justification for this best practise seems to be that if a class declaration changes then you only have to change it in one place (the declaration header)..."[/background][/size][/font][/color]
[/quote]
If you rename the class in the header file for forward declaration, and rename the x_fwd.h file itse'f, then compiler will give errors and force you to rename. If you have forward declared the class everywhere its name is used, compiler might not.

Additionally, this will make my code more clean. My source looks like this now:
[code]
#pragma once
#include "pt/box_fwd.h" // template<typename T, int dim> class box; typedef box<float,3> box3f;

namespace pt {
class parameter_collection;
}

namespace pt { namespace streams {
class file_input_stream;
}}

namespace pt { namespace d3d9 { namespace shaders {
class surface_shader_collection;
}}}

namespace pt { namespace d3d9 { namespace rendering {

class example
{
// refer all classes by reference.
};

}}} // namespace pt::d3d9::rendering

[/code]

And it will become to look like this:
[code]
#pragma once
#include "pt/box_fwd.h"
#include "pt/parameter_collection_fwd.h"
#include "pt/streams/file_input_stream_fwd.h"
#include "pt/d3d9/shaders/surface_shader_collection_fwd.h"

namespace pt { namespace d3d9 { namespace rendering {

class example
{
// refer all classes by reference.
};

}}} // namespace pt::d3d9::rendering


[/code]

edit: fooled with the editor Edited by Codarki

Share this post


Link to post
Share on other sites
Hi guys, thank you very much for the explanations.



[quote name='SiCrane' timestamp='1341488684' post='4955931']
A type name may be more complex than simply the name of a class. For instance, it could be a typedef for a specific template instantiation (e.g. std::string is a typedef for std::basic_string). Alternately, you could decide to change something from a separate class to a typedef to another class (e.g. changing from your homegrown string class to a typedef for an existing string class). Similarly you could have a forward declaration for a template class that you decide to add new template arguments to with default arguments.
[/quote]

Yes, that's true. I didn't think at these other cases..


[quote name='Codarki' timestamp='1341491732' post='4955952']
If you rename the class in the header file for forward declaration, and rename the x_fwd.h file itse'f, then compiler will give errors and force you to rename. If you have forward declared the class everywhere its name is used, compiler might not.
[/quote]

Uhm... I don't know if I have understood exactly your explanation here. Are you saying that it's better the first case because the compiler can give you compilation errors and so you know that there's something wrong or that is it better the second option?




[quote name='Codarki' timestamp='1341491732' post='4955952']
class example
{
// refer all classes by reference.
};
[/quote]

Here, the use of references is just your choice right?
I tried using forward declarations and creating objects on the stack and I got no problems.

By the way, this Effective C++ book is a bomb :)

Share this post


Link to post
Share on other sites
[quote name='stratusfer' timestamp='1341497053' post='4955980']
Here, the use of references is just your choice right? [/quote]

Nope, you can only declare pointers or references to forward-declared classes and you can't dereference them.

[quote name='stratusfer' timestamp='1341497053' post='4955980']
I tried using forward declarations and creating objects on the stack and I got no problems.[/quote]

Care to give an example? Compiler can't allocate an object when it doesn't know how big it is.

Share this post


Link to post
Share on other sites
I used to put all #includes in a header for a specific implementation file with its declarations. This is of course problematic if that header has to be used by other headers for other implementation files, causing them to include a whole lot of other headers. My solution which is a bit in the middle, is simply to take all the #includes that aren't mentioned in the declarations in the header file can be moved to the top of the implementation file.

For instance, a class uses std::string in its interface and uses std::stringstream internally. Putting only the #include <string> in the header and the #include <sstream> in the implementation file, since code referencing to this class need not know about what's used by the implementation.

It helped my compile times a lot.. but maybe I'm doing something now that every one is doing by common sense from the get go. =P

How many use the monster headers with all an implementation's includes put into them? (Of those who aren't using forward declaring headers)
Might just have been me...

Share this post


Link to post
Share on other sites
@Aardvajk

In my project I just tried something like this:

[CODE]
//HeaderA.h
class A
{
//stuff
};
//HeaderB.h
//no #include here
class A;
class B
{
public:
B()
{
A a;
}
};
[/CODE]

and it worked.. In the B constructor I am not using references..



[quote name='Zoomulator' timestamp='1341531272' post='4956148']
I used to put all #includes in a header for a specific implementation file with its declarations. This is of course problematic if that header has to be used by other headers for other implementation files, causing them to include a whole lot of other headers. My solution which is a bit in the middle, is simply to take all the #includes that aren't mentioned in the declarations in the header file can be moved to the top of the implementation file.

For instance, a class uses std::string in its interface and uses std::stringstream internally. Putting only the #include in the header and the #include in the implementation file, since code referencing to this class need not know about what's used by the implementation.

It helped my compile times a lot.. but maybe I'm doing something now that every one is doing by common sense from the get go. =P

How many use the monster headers with all an implementation's includes put into them? (Of those who aren't using forward declaring headers)
Might just have been me...
[/quote]

If I have understood well, you're saying that, for example, if a classA.h file requires less #include compared to the classA.cpp file, you just include only the stuff you need there and not all the #include required in the cpp file? if so, I usually do the same :)

Share this post


Link to post
Share on other sites
[quote name='stratusfer' timestamp='1341577539' post='4956298']
@Aardvajk

In my project I just tried something like this:

[CODE]
//HeaderA.h
class A
{
//stuff
};
//HeaderB.h
//no #include here
class A;
class B
{
public:
B()
{
A a;
}
};
[/CODE]

and it worked.. In the B constructor I am not using references..
[/quote]
If the source file you're compiling includes header A then header B, then header B has the full definition of the class despite not including it by itself. Try to include header B only in a source file and compile it.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this