7

According to my teacher, it's bad practice to write user-defined functions like this:

int DoubleNumber(int Number)
{
    return Number * 2;
}

int main()
{
    cout << DoubleNumber(8);
}

Instead, he says to always use forward declarations, even if the functions don't need any knowledge of each other:

int DoubleNumber(int Number); // Forward declaration.

int main()
{
    cout << DoubleNumber(8);
}

int DoubleNumber(int Number) // Implementation.
{
    return Number * 2;
}

I find this especially strange since he made a point of telling us how important it is that the forward declaration and implementation are exactly the same or you'll get errors. If it's such a big deal, why not just put it all above main()?

So, is it really bad practice to declare and implement at the same time? Does it even matter?

2
  • 3
    The problem with teachers they rarely sufficiently practice the subject they teach, and programming is one of those fields where if you don't practice it, you lose it. Commented Dec 8, 2010 at 20:03
  • If your functions are to be used outside your compilation unit, you need to declare them in your *.h file anyway. Likewise, nontrivial class methods will be declared separately in the class definition. IME, internal-only non-inline functions are rare compared to the above usages, so in the broad view it doesn't matter much. Commented Dec 8, 2010 at 20:19

9 Answers 9

15

If you don't declare the forward declarations ("prototypes"), then you need to make sure that all your functions occur before any functions that depend on them, i.e. in the reverse order of the call graph. That's fine for a simple example as above, but is a complete pain for anything more realistic (and in some cases impossible, if there are any loops in the call graph).

Sign up to request clarification or add additional context in comments.

4 Comments

I find reordering the functions to be less onerous than keeping declarations and definitions in sync. It also helps illustrate the hierarchical structure of the design - helpers at the top, then more important stuff, then main() at the bottom. It seems like some people prefer exactly the reverse order, though.
It seems like ordering the functions would be more painful in hardcore "clean code" (i.e., put nearly everything in a function), especially in large projects. Then again, keeping the prototypes and implementations synchronized also seems like it would be annoying for a large project.
@Karl: If your functions are exposed via a header file, you would have to do the "keeping in sync" regardless. Regarding personal preference about order, mine is that helpers should go at the bottom, because they're the least interesting! (at least from an "understanding the structure" point of view)
of course you need to keep certain things in sync anyway. That isn't a reason to do it more than necessary. :)
9

I think your teacher is an old C programmer.

If you wrote a C program without forward declarations and one function called another function declared later in the file (or in a different compilation unit), the compiler would not complain but silently pretend to know what the prototype should be.

Debugging is horrible, if you don't know if your compiler is passing the arguments correctly. Therefore it was a good defensive policy to always declare all functions; at least the compiler could raise an error if the declaration did not match the implementation.

C compilers and tool have gotten better (I hope). It is still not an error to call an unknown function, but GCC for example is kind enough to warn by default.

But in C++ you can't call a function that hasn't been declared or defined. Consequently, C++ programmers don't worry much about forward declarations.

Comments

7

Your teacher's policy is horrible IMHO. Use forward declarations only when they're really needed. That way, their presence demonstrates their necessity, which gives the reader useful documentation (i.e., there may be mutual recursion between the functions). Of course you do need forward declarations in header files; that's what they're for.

1 Comment

The only thing that's horrible is not explaining why. There rest is about conventions. In my experience, fighting over "horrible" conventions is often a greater waste of time than complying.
4

In my first programming class, the teacher also emphasized this point. I'm not exactly sure there is a benefit to such a simple case in actual software.

However, it does prepare you for using header files if you haven't covered that yet. In a typical case, you will have a header file custom-math.h and source file custom-math.cpp where custom-math.h contains the forward declaration and custom-math.cpp the implementation. Doing so may increase compilation time significantly when doing modifications to function implementations only in large projects. It is also a convenient way to split your program into "logical" groups of functions and/or classes.

If you are going to put other functions in the same file as main(), then what you do probably depends on your personal preference. Some people prefer to have main() close to the top to get to the program logic right away. In this case, forward declare your functions.

Comments

3

Karl Knecthel writes "Use forward declarations only when they're really needed. That way, their presence demonstrates their necessity, which gives the reader useful documentation (i.e., there may be mutual recursion between the functions)." and IMHO that's sound advice.

Oli Charlesworth talks about "complete pain" for ordering functions so that they can be called without forward declarations. That's not my experience, and I cannot imagine how that pain/problem is accomplished. I suspect a PEBCAK problem there.

A practice of using forward declarations for all functions will not save you from PEBCAK problems, but they do introduce needless maintainance work and needless more code to relate to, and they do make it more unclear which functions really need forward declarations.

If you get to the point where forward declarations could help to see function signatures at a glance, when forced to some very simple editor, then there are two actions that should be taken: (1) refactoring of the code, and (2) switching to a better editor.

Cheers & hth.,

2 Comments

Regarding the pain of ordering functions, I think it may be a symptom of having overly long source files. I find that 200 lines is starting to feel significant, and 500 lines is clearly too long.
Sometimes forward decls are needed. And then you have some forward decls only. That may become difficult to maintain. I find it especially useful to put forward declarations as a point where I can document functions, and as a quick overview what functions are implemented in the file.
2

Ask your teacher about why he recommends this ;) Anyway, it's not bad practice in my opinion, and in most cases it doesn't even matter. The biggest advantage of declaring all functions upfront is that you have a high-level overview of what the code does.

4 Comments

If you do this, you might want to wait until after the lecture is over. Some teachers really don't like being questioned like that in front of the class.
I did ask him after class, and his response (paraphrased) was just "it's bad practice."
Ah, your teacher seems to be a member of the cargo cult programming sect. (In other words, he most probably doesn't understand the issue himself -- he's just blindly repeating something that he heard some place else.)
@André: Those are the teachers that usually get asked more questions :)
1

Traditionally you'll be sticking all your prototypes in a header file, so they can be used by other source files - or at least, you'll put the ones you want to expose in the .h file.

As far as code where it's not necessary, there's something to be said for putting all your file-level declarations at the top (variables and functions) because it means you can move functions around at-will and not have to worry about it. Not to mention, I can see every function in a file right away. But something like:

void Func1() { ... }
...
void Func2() { ... }
...
void Func3() { ... }
...
int main() { Func1(); Func2(); Func3(); return 0; }

That - that is to say, a number of disjointed functions all called by main() - is a very common file, and it's perfectly reasonable to forgo the declaration.

Comments

1

Blanket rules are rarely correct. The public api you would normally expose through the prototypes in the header file. The rest of the functions will likely to be in an anonymous namespace in the cpp file. If those are called multiple times in the implementation it make sense to provide prototypes at the top, otherwise every function using them would have to provide prototypes before calling functions. At the same time if some function is used multiple times in the cpp file it might be an indication that it's universal enough to be moved to a common api. If the functions are not used all over the place, it's better to provide as limited exposure to them as possible, i.e. declaring and defining them close to the place they are called from.

Comments

0

Personally, I like to only forward declare anything that client code needs to know about (i.e. in a class header). Anything that's local to a class implementation (such as helper functions), should be defined prior to usage.

Of course at the end of the day, for the most part, it boils down to either personal preference or coding standard that your project is following.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.