I'm having trouble understanding when it is more appropriate to put an include in the .cpp file versus the .h file
If an include is needed for all users of the header file, in particular for the header file to be valid code, then include from the header file, otherwise include from the source file. Here's some more specific examples.
For enums, if you're using the name of the enum only, such as for parameter type declarations of functions, you can just forward declare the enum. If you use the named values of the enum, such as setting a default parameter value, you much include the full enum.
enum EnumName;
void SomeFunction(EnumName parameter); // Doesn't need to see full enum. Actual enum values are not a concern here
void SomeFunction(EnumName parameter = enumValueName); // Full include needed, otherwise enumValueName won't be seen
For structs and classes, if you only need the type name to declare pointers to that type, you can just forward declare. If you declare actual instances, you need to include the full struct or class. As a side note, if you declare an instance, the size of that instance needs to be known, which means the full definition needs to be known to calculate that size. Side-side note, private fields also need to be seen to calculate a class or struct size, which kind of makes them not entirely private.
struct SomeStruct {
int field1;
int field2;
};
void SomeFunction(SomeStruct* structParameter); // Forward declare fine, it's just a pointer to some struct, the details of which are not important here
void SomeFunction(SomeStruct& structParameter); // Forward declare fine, same reason
SomeStruct structInstance; // Full include needed, as space must be allocated for this struct, which can't be calculated without knowing all the fields
class SomeClass {
void MemberFunction();
};
void SomeMethod(SomeClass* pointerToClass) // Forward declare fine for this
{
// ...
pointerToClass->MemberFunction(); // Full include needed for this, otherwise compiler won't know MemberFunction exists for this class type
}
For functions, you can forward declare any function which is not inline. If you just need access to one function from another file, you can forward declare it, rather than declare it in a header file and include the header. Doing this you can also avoid creating a header file if you're only exporting one function. Function declarations tend to be long though, so unless it's a single function with a short signature, used from only one external file, I don't recommend forward declaring functions.
void CreatePreGameDialogue(DialgueData* data); // Just one function, no header file
// ...
{
// ...
CreatePreGameDialogue(&data); // Access external function here
}
For global variables, you can forward declare and mark as extern (to prevent redefinition) any variable that is defined in another source file. This is similar to the above concerning functions, in that you'd normally only do this to access a single variable, otherwise it's easier to include a header containing a collection of variables or functions. (I used this technique to import data from BaseData.cpp, making a single declare for the outermost data structure, and avoided using any BaseData.h). This may not work so well if the compiler determines an array size based on declared data, and that size is needed at the point of use.
// Declare the StartLocation array in BaseData.cpp. (So it is available in this file).
extern StartLocation startLocation[];
Export int InitProc()
{
// Randomize starting locations
RandomizeList(4, startLocation); // Randomize (first) 4 starting locations (Note: 4 is hardcoded here)
//RandomizeList(AutoSize(startLocation)); // Need compile time array size info for this to work
}