A number of students are aware that the following code works:
#include <iostream>
using namespace std;
int main() {
cout << "Hello world!" << endl;
}
In fact, even this works:
#include <iostream>
#include <cmath>
using namespace std;
double W( double x ) {
return abs( 3.0 - abs( 3.0*x ) );
}
int main() {
double value;
cout << "Enter a value: ";
cin >> value;
cout << "W(" << value << ") = " << W(value) << endl;
}
Thus, you may be wondering why we:
- require you to use function declarations,
- do not allow you to use the using keyword, and
- have main() return 0.
Ultimately, we want you to be competent developers, not just
fly-by-night programmers who can throw something together in 30
seconds and then have it fail the next day, month or year.
Function (and class) declarations
C++ does not always require function declarations, but function
declarations are useful for the following reasons:
- Anyone reading your code can find all functions
in one easy-to-read list of function declarations
without having to read through the entire file.
- In the example above,
because main() calls W(...), it is
absolutely necessary that W(...) be
declared or defined before main() is
defined. Otherwise, if the compiler is not aware
of W(...), its parameters and return
type, it cannot deal with it in main().
Thus, without a list of function declarations,
the programmer is always needing to ensure
that all function definitions come in the
appropriate order so that no function is
ever called in any other function definition
before that function is defined.
- In some cases, it is impossible to not declare functions,
such as, if function M(...) calls
function F(...), and vice versa.
- Sometimes, it makes no sense to have certain functions at
the top of files, just because they are used by
other functions. Small helper functions are better
defined at the end of the file, while more critical
functions are defined near the top.
- Also, trying to teach students about the correct "order"
and exceptions to this is actually much more complicated.
"All functions must be declared first." is a simple
rule that parallels "All variables must be declared
first." This simpler rule is always guaranteed
to work, no matter what.
- In any library, functions are declared separately
from where they are defined. The header file only contains
function declarations. It is in the corresponding .cpp
file that the functions are defined.
- A more subtle reason has to do with version control. Suppose
a file contained two functions, f(...) and
g(...), and suppose initially, neither function
called the other, and thus, either function could be
defined first. Suppose that f(...) was defined
first. Revision control keeps track of all changes
so that changes made to a source file can be tracked, just
in case, for example, a new change introduces a new bug.
Suppose you were making a change where f(...) now
had to call g(...). If f(...) was defined
first, the order of the two functions would have to be
switched (alternatively, you could just add a function
declaration for g(...), but if you didn't do this
initially, you may not think of it here). If the change was
to only one line of code, the apparent change would be
very significant, and any subsequent developer trying to
find what was actually changed may become rather
frustrated: two large functions were moved around, but
all their statements appear to be essentially the same,
except for exactly one line. If the functions were all
declared first, this change would be recorded as having
modified exactly one line, and thus any developer can
subsequently determine exactly what was modified.
- Finally, if you are showing off your source code to others
(think interviewers) who are more experienced programmers,
they will appreciate your style, rather than ask you
why you are (in your current opinion) wasting both time
and space.
The same goes for class declarations versus class definitions.
using
Next, you may be wondering why we do not allow you to use the statement
using namespace std;
in which case, you don't have to prefix cout and endl
with std::. Again, for the same reasons that we make you declare
all your functions:
- You would almost never use using in any developed
code base. The entire point of namespaces is to prevent
collisions of names. By using the using keyword,
you are destroying that protection.
- Thus, once again, if you are showing your source code to
someone else, they will appreciate that reason, as opposed
to asking you why you are (in your current opinion)
wasting time and space.
The purpose of this course is not teach you solely how to write simple
and easy functions that solve trivial problems. We're trying to teach you
programming habits that will benefit you when you begin to work in
industry in larger programming environments with source trees
that have source files numbering in the hundreds or thousands
with cumulatively tens of thousands or even millions of lines of code.
If you would like to read a further discussion on not
using the using operator, see stackexchange.com.
An acceptable use of using
What is acceptable is explicitly stating which items in a
particular namespace you would like to use without the, in this
case, std:: prefix.
#include <iostream>
// This ensures that only 'cout' and 'endl' may be used
// with the 'std::' prefix. For any other object, class, etc.,
// in the standard library, you must still prefix the name
// by 'std::'
using std::cout;
using std::endl;
int main();
int main() {
cout << "Hello world!" << endl;
return 0;
}
Returning 0 from main()
As for returning zero from main(), yes, C++ and a more recent
version of the C standard does not require main() to return anything.
Instead, the compiler will automatically
include a return 0; statement if none is stated. However:
- Does it really make sense to teach an exception to a
rule when that exception applies to exactly one
function (i.e., main()) and that
exception isn't even necessary?
- If you ever go back to C programming, especially if you
are working in embedded systems where you may not be
using the most recent compiler, you will need to
forget that exception anyway.
Impressing your interviewer
Why does the following program not compile?
#include <iostream>
#include <cmath>
int main() {
double value;
std::cout << "Enter a value: ";
std::cin >> value;
std::cout << "W(" << value << ") = " << W(value) << std::endl;
}
double W( double x ) {
return std::abs( 3.0 - std::abs( 3.0*x ) );
}
When the compiler gets to the line with W(value), it
needs to know what is being printed: is it an int, an
unsigned int, a double or an instance of a completely
different class. With neither a function declaration, and with the function
definition not yet reached, the compiler is simply unaware of what
to do at this point.
A compiler for a different programming language may make multiple
passes through the code: first to find all functions that are defined,
and only then to make a second pass where it processes the balance of
the code. C++ does not do this: it must be aware of the properties
of all functions, classes, etc. before they are ever called.
If W(...) was
either declared at the top of this function, or the function definition
was moved to in front of main(), then the compiler would be
immediately aware that it should be calling the appropriate function
to print a double.
Question: Did you get this far, and if so, did this help or not, and
if you still think our approach is bad, please let Douglas know.
Acknowledgements: J.B.