Declaration vs. Definition

In this discussion on the specification of classes in C++, the term "definition" has been used regarding functions. When we "define" a function, we dictate the function's behavior through the code that exists within the curly braces. The "declaration" of a function, on the other hand, only specifies the interface of the function. This interface includes the function name, the return type, and the list of parameters and their types. The following listing shows both a declaration and definition of the function average.

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: #include <iostream>#include <cstdlib> using namespace std; // function declarationdouble average(int, int); int main(int argc, char* argv[]) { cout << average(10, 2) << endl; return EXIT_SUCCESS;} // function definitiondouble average(int total, int count) { return (total / count);}
Listing 6 Declaration vs. definition

The declaration and definition of a function differ in many aspects. In the above example, notice the declaration of function average is followed by a semi-colon and not by curly braces. This specifies that it is the declaration, not the definition. A function declaration does not need to include variable names for the parameters. The definition, on the other hand, looks just the same as any C++ function we have seen so far. It is interesting to note that the definition of the function comes after its actual use in function main. This is acceptable since we placed a declaration of the function prior to function main. This practice, known as a forward reference, is common in C++ programming. It is legal to use a function or a class that is not yet defined, as long as it has been declared. It is illegal, however, to use a function or a class that is not yet defined without including the forward reference.

The rules regarding class specification in C++ offer some flexibility in the placement of member function definitions. A C++ class can include all its member function definitions inside of the class definition. This is the approach taken in the examples we have seen so far. Alternatively, the definitions for one or more class member functions can appear outside the class definition, as long as we still include the declarations inside the class definition.

Programmers must fully qualify the names of class member functions that appear outside of a class definition. To qualify a name fully, the function name must be prepended with the class name followed by the scope resolution operator (::). Listing 7 defines class member functions outside the class definition.

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: class BankAccount { private: double sum; string name; public: BankAccount(string nm) : name(nm), sum(0) {} BankAccount(string nm, double bal) : name(nm), sum(bal) {} double balance(); void deposit(double); void withdraw(double); string getName();}; double BankAccount::balance() { return sum;}void BankAccount::deposit(double amount) { sum += amount;}void BankAccount::withdraw(double amount) { sum -= amount;}string BankAccount::getName() { return name;}
Listing 7 Defining class member functions outside the declaration

Placing class member function definitions outside the class definition has benefits. First, it reduces the size of the class definition. This enhances readability, since it becomes easier to view the entire class interface when the function definitions appear elsewhere.

Separating member function definitions from their declarations also allows the sharing of precompiled code. Listing 7 shows class member function definitions that appear outside their class definition. These definitions still appear in the same file as the class definition. Class member-function definitions can also appear in an altogether different file than the class definition. When a programmer specifies a class in this manner, the programmer can compile the file that contains the class member-function definitions. This file containing the class member-function definitions is known as an implementation file. The class definition file (or header file) then becomes the interface that shares the precompiled code.

Listing 8 shows the definition file bankaccount.h:

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: #include <string>#include <cstdlib>#include <iostream> #ifndef _BANKACCOUNT_H#define _BANKACCOUNT_H using namespace std; class BankAccount { private: double sum; string name; public: BankAccount(string nm) : name(nm), sum(0) {} BankAccount(string nm, double bal) : name(nm), sum(bal) {} double balance(); void deposit(double); void withdraw(double); string getName();}; #endif
Listing 8 bankaccount.h    

Listing 9 shows the implementation file bankaccount.cpp:

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: #include "bankaccount.h" double BankAccount::balance() { return sum;}void BankAccount::deposit(double amount) { sum += amount;}void BankAccount::withdraw(double amount) { sum -= amount;}string BankAccount::getName() { return name;}
Listing 9 bankaccount.cpp

The preprocessor directive #include replace the directive with the contents of the specified file. The directives #define, #ifndef and #endif are used to check if a definition file is not included more than once. We examine the uses of preprocessor directives in page 1.3.4 The Preprocessor.