Free Hosting

Forward declarations

Forward declarations in c++ script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-36724546-1']); _gaq.push(['_setDomainName', 'blogspot.com']); _gaq.push(['_setAllowLinker', true]); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'stats.g.doubleclick.net/dc.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();

Take a look at this seemingly innocent sample program called add.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
 
int main()
{
    using namespace std;
    cout << "The sum of 3 and 4 is: " << add(3, 4) << endl;
    return 0;
}
 
int add(int x, int y)
{
    return x + y;
}
You would expect this program to produce the result:
The sum of 3 and 4 is: 7
But in fact, it doesn’t compile at all! Visual Studio 2005 Express produces the following compile errors:
add.cpp(10) : error C3861: 'add': identifier not found
add.cpp(15) : error C2365: 'add' : redefinition; previous definition was 'formerly unknown identifier'
The reason this program doesn’t compile is because the compiler reads files sequentially. When it reaches the function call to add() inside of main(), it doesn’t know what add is, because we haven’t defined add() until later! That produces the error on line 10. Then when it gets to the actual declaration of add(), it complains about add being redefined (which seems slightly misleading, given that it wasn’t ever defined in the first place). Often times, a single error in your code will end up producing multiple warnings.
Rule: When addressing compile errors in your programs, always resolve the first error produced first.
In this case, that means we need to address the fact that the compiler doesn’t know what add is. There are three ways to fix this problem.
The first way is to reorder our function calls so add is defined before main:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
 
int add(int x, int y)
{
    return x + y;
}
 
int main()
{
    using namespace std;
    cout << "The sum of 3 and 4 is: " << add(3, 4) << endl;
    return 0;
}
That way, by the time main() calls add(), it will already know what add is. Because this is such a simple program, this change is relatively easy to do. However, in a large program, it would be extremely tedious trying to decipher which functions called which other functions so they could be declared in the correct order.
Furthermore, this option is not always available. Let’s say we’re writing a program that has two functions A and B. If function A calls function B, and function B calls function A, then there’s no way to order the functions in a way that they will both be happy. If you define A first, the compiler will complain it doesn’t know what B is. If you define B first, the compiler will complain that it doesn’t know what A is.
Consequently, a better solution is to use a forward declaration. In a forward declaration, we declare (but do not define) our function in advance of where we use it, typically at the top of the file. This way, the compiler will understand what our function looks like when it encounters a call to it later. We do this by writing a declaration statement known as a function prototype. Afunction prototype is a declaration of a function that includes the function’s name, parameters, and return type, but does not implement the function.
Here’s our original program with a forward declaration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
 
int add(int x, int y); // forward declaration of add() using a function prototype
 
int main()
{
    using namespace std;
    cout << "The sum of 3 and 4 is: " << add(3, 4) << endl;
    return 0;
}
 
int add(int x, int y)
{
    return x + y;
}
Now when the compiler reaches add() in main, it will know what add looks like (a function that takes two integer parameters and returns an integer), and it won’t complain.
It is worth noting that function prototypes do not need to specify the names of the parameters. In the above code, you could also forward declare your function like this:
1
int add(int, int);
However, we prefer the method where the parameters are named because it’s more descriptive to a human reader.
One question many new programmers have is: what happens if we forward declare a function but do not define it?
The answer is: it depends. If a forward declaration is made, but the function is never called, the program will compile and run fine. However, if a forward declaration is made, the function is called, but the program never defines the function, the program will compile okay, but the linker will complain that it can’t resolve the function call. Consider the following program:
1
2
3
4
5
6
7
8
9
10
11
#include "stdafx.h"
#include <iostream>
 
int add(int x, int y);
 
int main()
{
    using namespace std;
    cout << "The sum of 3 and 4 is: " << add(3, 4) << endl;
    return 0;
}
In this program, we forward declare add(), and we call add(), but we never define add(). When we try and compile this program, Visual Studio 2005 Express produces the following message:
Compiling...
add.cpp
Linking...
add.obj : error LNK2001: unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z)
add.exe : fatal error LNK1120: 1 unresolved externals
As you can see, the program compiled okay, but it failed at the link stage because int add(int, int) was never defined.
The third solution is to use a header file, which we will discuss shortly.
Quiz
1) What’s the difference between a function prototype and a forward declaration?
2) Write the function prototype for this function:
1
2
3
4
int DoMath(int first, int second, int third, int fourth)
{
     return first + second * third / fourth;
}
For each of the following programs, state whether they fail to compile, fail to link, or compile and link. If you are not sure, try compiling them!
3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
int add(int x, int y);
 
int main()
{
    using namespace std;
    cout << "3 + 4 + 5 = " << add(3, 4, 5) << endl;
    return 0;
}
 
int add(int x, int y)
{
    return x + y;
}
4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
int add(int x, int y);
 
int main()
{
    using namespace std;
    cout << "3 + 4 + 5 = " << add(3, 4, 5) << endl;
    return 0;
}
 
int add(int x, int y, int z)
{
    return x + y + z;
}
5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
int add(int x, int y);
 
int main()
{
    using namespace std;
    cout << "3 + 4 + 5 = " << add(3, 4) << endl;
    return 0;
}
 
int add(int x, int y, int z)
{
    return x + y + z;
}
6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
int add(int x, int y, int z);
 
int main()
{
    using namespace std;
    cout << "3 + 4 + 5 = " << add(3, 4, 5) << endl;
    return 0;
}
 
int add(int x, int y, int z)
{
    return x + y + z;
}

0 comments:

Blogger Template by Clairvo