Forward Declarations

Understand what function prototypes are, and learn how we can use them to let us order our code any way we want.
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, Unlimited Access
3D art showing a technician character
Ryan McCombe
Ryan McCombe
Updated

So far, we've been working under the restrictions that functions must be declared before they are used. For example, we've been unable to create code like shown below:

int main() {
  // Add is not defined yet
  Add(1, 2);
}

int Add(int x, int y) {
  return x + y;
}
Error: 'Add': identifier not found

Because the compiler reads our files top-to-bottom, when it reaches line 3, it encounters a call to a function that it’s not aware of.

The easy solution we've been using has been to move Add above main. However, we may not want to do this for stylistic reasons.

Worse, not all problems in this category can be solved by just moving functions around.

Circular Dependencies

Circular dependencies occur when two or more parts of our code reference each other in a cycle.

With functions, it might look something like this:

void HandlePositive(int x) {
  if (x < 0) HandleNegative(x);
}

void HandleNegative(int x){
  if (x >= 0) HandlePositive(x);
}

Here, HandlePositive calls HandleNegative and vice versa, creating a circular dependency.

This dependency cannot be resolved by merely reordering the functions, as each function requires the other to be declared beforehand.

The primary method C++ provides to let us solve circular dependencies is the ability to separate the declaration and definition of our function. This means we can declare a function in one region of code, but define it elsewhere.

We can think of a declaration as a minimalist description of our function, whilst the definition is the complete implementation.

For code to use our function, it only needs to be declared at the point of use - not fully defined. The full definition can come later in the file or, in larger projects, in a different file entirely.

Taking advantage of this mechanism is referred to as forward declaring the function.

Preview: The Compiler and the Linker

In C++, the compilation process is broken down into two processes - first our files are compiled, and then they are linked.

The compiler reads each of our source files from top to bottom. Each file generates an intermediate output, sometimes referred to as a translation unit.

The linker then takes these translation units and links them together to create a single cohesive package

A forward declaration is a promise to the compiler that the thing we're trying to use is going to be defined somewhere else. We’re telling the compiler that the linker will take care of it.

We'll discuss how our code gets built in more detail later in the course.

Function Prototypes

To forward declare a function, we first need to understand what a function prototype is. This is sometimes also called the signature.

We can think of the prototype as just the heading of the function, without its body. For example, if we have the following Add function:

int Add(int x, int y) {
  return x + y;
}

Its prototype would be this:

int Add(int x, int y);

So, the prototype is the function's return type, the function name, and the parameter types.

In this example, the return type is int, the name is Add, and the parameter types are int, int

Within the function prototype, the names of our parameters are optional. They are typically worth including anyway for the benefit of anyone reading our code.

If the names are descriptive, including them helps anyone reading our prototype get a better understanding of how our function works.

But technically, only the types are required, so the following is also a valid prototype for our function:

int Add(int, int);
Test your Knowledge

Function Prototype

What is a function prototype?

What is a valid prototype for this function?

float Calculate(int x, float y) {
  return x * y;
}

Interfaces and APIs

Function prototypes relate closely to the concept of an interface in software design. An interface is sometimes also referred to as the API - the application programming interface.

The interface represents how our code is used by consumers.

In the context of a function, the consumer is any code that calls our function. So the interface is everything that the caller needs to know to use our function.

This is exactly the same information that is in the prototype:

  • the function’s name
  • the type of data they need to provide as arguments
  • the type of data the function will return to them

The function body is not part of the interface because the callers don’t interact with the body at all.

Using Forward Declaration

Now, we no longer have to define our function before we use it - we just have to declare it, using its prototype.

Forward declarations are typically put near the top of the files that use them, but as long as they're provided before the functions are used, our code will work as expected:

int Add(int x, int y);

int main() {
  Add(1, 2);
}

int Add(int x, int y) {
  return x + y;
}
Test your Knowledge

Forward Declarations

After running the code below, what is the value of x inside the main function?

int main() {
  int x { Calculate() };
}

int Calculate() {
  return 10 * 10;
}

After running the code below, what is the value of x inside the main function?

int Calculate();

int main() {
  int x { Calculate() };
}

int Calculate() {
  return 10 * 10;
}

Terminology Review: Definitions, Declarations, and Prototypes

It’s worth highlighting a relationship between declarations and definitions that may not be immediately obvious:

  • Function definitions are also function declarations
  • Function declarations may or may not be function definitions
  • A function prototype is a declaration without a definition

The code below is a function declaration and definition, but not a prototype:

int Calculate() {
  return 10 * 10;
}

Whilst this code is a declaration and prototype, but not a definition:

int Calculate();

This separation between declaring a function and defining it is likely to feel quite strange, especially at this stage.

However, this is a very important concept. Most C++ code is created this way. This is because the separation can have some major workflow benefits for larger projects that span many files. We'll examples of this soon.

Summary

In this lesson, we've explored several key concepts regarding forward declarations in C++:

  • The necessity of forward declarations when functions are used before their definitions.
  • Understanding circular dependencies and their resolution using forward declarations.
  • The distinction between declaring and defining functions.
  • How function prototypes work, including their syntax and purpose.
  • Practical application of forward declarations in code, demonstrating how they enable flexible ordering of function definitions.

This knowledge not only helps in writing more organized and maintainable code but also lays the groundwork for understanding more complex patterns we’ll see later in the course.

Preview of Next Lesson: Loops

In the upcoming lesson, we will dive into one of the most fundamental constructs of programming: loops. Here's a sneak peek at what we'll cover:

  • Introduction to loops, allowing us to repeat blocks of code whilst some condition is true.
  • Exploration of different types of loops: for loops, while loops, and do-while loops.
  • Understanding loop control statements, including break and continue.
  • Practical examples and exercises to illustrate the usage of loops in various scenarios.
  • Tips on avoiding common pitfalls such as infinite loops.

Was this lesson useful?

Next Lesson

Loops

Learn how we can use loops to iterate over a block of code, executing it as many times as needed.
3D art showing a dragon
Ryan McCombe
Ryan McCombe
Updated
Lesson Contents

Forward Declarations

Understand what function prototypes are, and learn how we can use them to let us order our code any way we want.

3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, Unlimited Access
Functions, Conditionals and Loops
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access

This course includes:

  • 56 Lessons
  • Over 200 Quiz Questions
  • 95% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Loops

Learn how we can use loops to iterate over a block of code, executing it as many times as needed.
3D art showing a dragon
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved