Separate headers for declaration and definition

Started by
8 comments, last by stratusfer 11 years, 8 months ago
[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[background=rgb(250, 251, 252)]Hi everybody,[/background]

[/font]

[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[background=rgb(250, 251, 252)]I want to discuss a topic discussed in a previous thread: [/background]

[/font]http://www.gamedev.net/topic/510239-separate-header-files-for-declarations-and-definitions/ : [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[background=rgb(250, 251, 252)] declaration and definition of classes in separate header files.[/background]

[/font]

[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[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]

[/font][font=courier new,courier,monospace]Item31: Minimize compilation dependencies between files [/font][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[background=rgb(250, 251, 252)]but there are some points that are not so clear..[/background]

[/font]

[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[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]

[/font]

[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[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]

[/font]
[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[background=rgb(250, 251, 252)]
[color=blue][font=CourierNew]class[/font] A; [color=gray][font=CourierNew]// Avoids having to include "A.h"[/font]

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

[/background]

[/font]
[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[background=rgb(250, 251, 252)]However, Scott Meyers [/background]

[/font]Effective C++[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[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]

[/font]

Advertisement
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.

[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[background=rgb(250, 251, 252)]My query is though, is this really beneficial and do people really follow this practise?[/background][/font]
[color=#282828][font=helvetica,arial,verdana,tahoma,sans-serif]

[background=rgb(250, 251, 252)]

[/background][/font]
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.


[color=#282828][font=helvetica,arial,verdana,tahoma,sans-serif]

[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][/font]


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:

#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



And it will become to look like this:

#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




edit: fooled with the editor
Hi guys, thank you very much for the explanations.




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.


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



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.


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?





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


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 :)

Here, the use of references is just your choice right?


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


I tried using forward declarations and creating objects on the stack and I got no problems.


Care to give an example? Compiler can't allocate an object when it doesn't know how big it is.
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...
@Aardvajk

In my project I just tried something like this:


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


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




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


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 :)
UP!

@Aardvajk

In my project I just tried something like this:


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


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

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.
Sorry for the delay.. You're right :)


Thank you!

This topic is closed to new replies.

Advertisement