5 Functions

A practical guide to designing, declaring, calling, and organizing C++ functions, including parameters, return values, scope, pass-by-value, overloading, and decomposition.

The role of functions

Functions package a focused operation behind a name. A typical C++ has a , a name, a list, and a body. For example, a named square could accept an integer , multiply that value by itself, and return the result.

A call transfers control to the . The executes its body and then returns control to the caller, optionally carrying a result. A meaningful name, explicit inputs, and a clear result or side effect make a easier to understand and use.

Takeaway: Treat each as a small operation with a clear purpose, clear inputs, and a clear outcome.

Declaring and defining functions

A tells the compiler that a exists. It gives the 's , name, and types, but it does not provide the implementation. A declaration is also called a prototype. For example, int square(int number); declares a without defining its body.

A includes the body and describes what the does. For example, a definition of square can return number * number. The declaration and definition must describe the same : their and types must agree.

The declaration must be visible before a call that uses the . Therefore, a program can place a declaration before main and the definition later, or it can place the complete definition before the call. In a multi-file program, declarations commonly belong in a header file while definitions belong in a source file. This separates the interface, or how a is used, from the implementation, or how it works.

Takeaway: Declare a before code calls it, and keep its declaration and definition consistent.

Parameters and arguments

A is a variable listed in a or definition. An is the value supplied at the call site. In add(4, 7), the values 4 and 7 are arguments; they initialize the 's corresponding parameters.

The list determines how many arguments a accepts and what types those arguments should have. An empty list represents a that takes no arguments. In a declaration, a name may be omitted when only the type is needed, as in int square(int);.

For example, a that adds two integers can use parameters named first and second, while a call supplies two integer arguments. The names of parameters are local to the and help explain the role of each input.

Takeaway: Parameters describe a 's inputs; arguments provide the values for those inputs.

Return values and void functions

The appears before the name. A non-void must return a suitable value on every path that reaches the end of the . A void performs an operation without returning a value.

A return statement transfers control back to the caller. When it includes an expression, that expression provides the result of the call. A void can use return; to leave early without producing a value.

For example, a that computes half of an integer can use the double and return a floating-point result. A that prints a message can use void. Simply evaluating an expression is not enough for a non-void ; the result must be returned explicitly.

Takeaway: Match the return statement to the , and make sure every required execution path produces a result.

and local data

is the region in which a name can be used. parameters and local variables are available inside their , while a variable declared inside a nested block is available only within that block and its nested regions.

For example, parameters named a and b and a local variable named result inside difference cannot normally be used in main or in another . A variable declared inside an if block is no longer in after that block ends.

An inner declaration can hide a name from an enclosing . Although this is allowed, distinct names are usually clearer. Passing data through parameters is generally preferable to relying heavily on global variables because it makes a 's inputs explicit and reduces unintended interactions.

Takeaway: Keep data near the code that uses it, and make communication between functions explicit through parameters and return values.

means that a receives its own object initialized from the . If the assigns a new value to that , the caller's original variable is unchanged.

For example, if try_to_change receives an integer and assigns 99 to its , the integer variable supplied by the caller keeps its original value. This behavior is useful when the should work with an independent copy or when the value is small, such as an int, double, or char.

Passing a class object by value also gives the a separate object and may involve copying or moving. When a later design needs to avoid copying a large object without modifying it, a reference-to-const can be used; that is different from because it refers to the caller's object.

Takeaway: Changing a by-value changes only the 's local copy.

lets multiple functions in the same use one name when their lists differ. The differences may involve types or the number of parameters. For example, maximum can have one version for two int arguments and another for two double arguments.

When a call is made, the compiler considers the available candidates and chooses the best match based on count, types, and permitted conversions. If no single best match exists, the call is ambiguous and the program is ill-formed.

Return types alone cannot distinguish overloads. Two functions with identical lists cannot be overloaded merely because one returns int and the other returns double. Overloads are clearest when they represent the same conceptual operation.

Takeaway: Use overloads for related operations, and distinguish them through lists rather than return types.

and design

divides a larger task into smaller functions with focused responsibilities. A rectangle-area program, for example, can use one to read a length, another to calculate the area, and another to print the result, while main coordinates the sequence.

This organization reduces complexity because each can be understood independently. It also encourages reuse, makes individual operations easier to test with known inputs, localizes changes, and clarifies how components communicate through interfaces.

A well-designed usually has a meaningful name, a small and coherent responsibility, explicit inputs, and a clear result or side effect. Avoid unnecessary dependence on global state so that the remains predictable and reusable.

Takeaway: Separate input, computation, output, and coordination when doing so gives each one clear responsibility.

Common mistakes and a review checklist

Several mistakes recur when writing functions:

  • Calling a before a declaration is visible. Place a declaration before the call or move the definition earlier.

  • Mismatching a declaration and definition. Check that the and every type agree.

  • Computing an expression without returning it from a non-void . Use an explicit return statement.

  • Expecting to modify the caller's variable. A by-value is a separate object.

  • Trying to overload functions using only their return types. Overloads require different lists.

  • Giving one too many unrelated responsibilities. Split the work when separate operations can be named and tested independently.

A useful review sequence is to check visibility first, then compare the declaration with the definition, then verify and types, return behavior, , and the 's responsibility.

Takeaway: Most errors become easier to find when interfaces, data flow, and responsibilities are checked separately.