0

I have 2 header files and a source file,

food.h:

#ifndef _FOOD_H
#define _FOOD_H

struct food {
    char name[10];
};

#endif

dog.h:

#ifndef _DOG_H
#define _DOG_H

class food;  //forward declaration

class Dog {
public:
    Dog(food f);

private:
    food _f;
};

#endif

And here is the source file of class dog,

//dog.cpp
#include "dog.h"
#include "food.h"

Dog::Dog(food f) : _f(f)
{}

The problem is I can compile the above code, I got an error saying _f has incomplete type.

I thought I can put a forward declaration in dog.h and include food.h in dog.cpp, but it doesn't work, why? And I shouldn't put user defined header file in .h files, right? It's deprecated, isn't it?

Eric Finn
  • 8,629
  • 3
  • 33
  • 42
Alcott
  • 17,905
  • 32
  • 116
  • 173
  • 2
    Put `#include "food.h"` in your `dog.h`. It's necessary, and not deprecated at all. – Kerrek SB Aug 02 '12 at 15:28
  • 5
    A side note: you should not use leading underscores in your include guardians (`#ifndef _FOOD_H ...`) since it's reserved in some contexts, just use `#ifndef FOOD_H ...` – Desmond Hume Aug 02 '12 at 15:32
  • @LokiAstari even if there's a `food` data member? – juanchopanza Aug 02 '12 at 15:35
  • @KerrekSB, but I was told that include user defined header in other headers may increase compile time, if one user defined file is changed. – Alcott Aug 02 '12 at 15:38
  • If you pass food by reference (and the member is a reference) then the compiler does not need to know the size and thus forward declarations will work. Otherwise it needs to know the size and thus you need to include the header file before use. – Martin York Aug 02 '12 at 15:39
  • @Alcott, that is true, but necessary in this case. Curious though as to your design, why is `food` a member of `Dog`? Wouldn't the canonical representation be that you `Feed()` the `Dog` some `food`, after which that `food` is consumed an no longer present? – Chad Aug 02 '12 at 15:39
  • @Alcott: It is indeed a good idea to avoid unnecessary includes; but you can't avoid necessary includes like this one. – Mike Seymour Aug 02 '12 at 15:39
  • @Chad, I just improvised a case, that's all – Alcott Aug 02 '12 at 15:43
  • @Alcott: Writing source code increases compile time. Yes, that's the nature of programming. If you write nothing at all, it'll compile very fast. But you can't avoid having to compile code that you actually need. – Kerrek SB Aug 02 '12 at 15:56

2 Answers2

3

The forward declaration doesn't work in this case because your Dog class has an instance of food. The compiler needs the full declaration of food in order to declare Dog.

As for the question regarding deprecation of includes, unless I misunderstand something, it is not true at all. Including food.h here would solve your problem:

//dog.h
#ifndef DOG_H
#define DOG_H

#include "food.h";

class Dog {
public:
    Dog(food f);

private:
    food f_;
};
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • Yes, including `food.h` will solve the problem, but including user defined headers might include compile time if the included file is altered, right? – Alcott Aug 02 '12 at 15:40
  • @Alcott indeed. If you change something in `food.h`, then all code that depends on it will have to be recompiled. It is good practice to minimize includes, and use forward declarations where possible, but in this case you can't (unless you change `Dog` such that it doesn't contain a `food` instance). – juanchopanza Aug 02 '12 at 15:42
  • To be honest, I don't quite understand how the compiler works when it declare the class `Dog`. I thought it'll first unfold `dog.h` into `dog.cpp` and then starts to interpret the class `Dog`'s definition, if that way, it should know what `food` looks like, because I include `food.h` in `dog.cpp`. – Alcott Aug 02 '12 at 15:47
  • @mikeseymour, is there a problem with trailing underscores in defines? I thought only leading underscores were reserved for the implementaiton. – juanchopanza Aug 02 '12 at 15:48
  • @Alcott the header provides a bunch of information required by the clients of the code at *compile time*. This includes function and class declarations. The full declaration of classes contained within classes is one of these pieces of information that is required. – juanchopanza Aug 02 '12 at 16:00
  • @juanchopanza: "Each name that **contains a double underscore** __ or begins with an underscore followed by an uppercase letter is reserved to the implementation for any use." You can get the gory details [here](http://stackoverflow.com/questions/228783), or to be on the safe side you could just avoid decorating your names with weird squiggles. – Mike Seymour Aug 02 '12 at 16:10
  • @MikeSeymour thanks for the clarification. I must have switched from one trailing underscore to two at some point without realising. – juanchopanza Aug 02 '12 at 16:14
2

The compiler needs to know the size of the object _f which cannot be accessed just using a forward declaration. If you were using pointers to class food this would work but as your member is a full class instance, you would need to include the header so that the compiler can know the size of class food.

Similarly, forward declaration would not work if you were inheriting from class food - you would specifically need to include the header.

Just remove the forward declaration and #include "food.h" and your code will compile.

mathematician1975
  • 21,161
  • 6
  • 59
  • 101