C++ Functions, Parameters, and Return Values
Design focused functions with explicit inputs, outputs, const-correct parameters, and contracts that can be tested independently of console code.
Before this lesson
Separate calculation from console input and output
Choose value or const-reference parameters
State and test a function contract
The short answer
A function names one operation. Parameters carry required input, the return value carries the result, and side effects should be visible. Prefer small interfaces that make invalid calls difficult and testing straightforward.
A function owns one responsibility
Functions reduce duplication, but their deeper value is naming a rule. calculate_shipping(subtotal) lets a caller use the rule without knowing its branch details. A function that parses input, changes global state, calculates, saves a file, and prints has several reasons to change and is difficult to test.
Start from the contract: required input, promised output, valid range, and possible failure. Then choose a name and signature that expose those decisions.
#include <iostream>
int shipping_cents(int subtotal_cents)
{
return subtotal_cents >= 5000 ? 0 : 599;
}
int main()
{
std::cout << shipping_cents(4999) << '\n';
std::cout << shipping_cents(5000) << '\n';
}Expected output
599 0
Declarations and definitions must agree
A declaration tells the compiler a function's name, parameter types, and return type. A definition supplies the body. In one small file, define a function before it is called. In multi-file programs, declarations usually live in headers and matching definitions in source files.
A mismatched declaration and definition can compile separate files but fail during linking because callers request a symbol that was never defined. Treat the function signature as one contract shared by both sides.
Parameters communicate ownership and mutation
Pass small values such as int, double, and bool by value. The function receives its own copy. Pass a larger existing object by const& when the function only observes it and copying is unnecessary. Use a non-const reference only when mutation is a visible part of the function contract.
Do not return a reference or pointer to a local variable; the local object dies when the function returns. Lifetime is part of interface design even before pointers are studied in depth.
| Parameter form | Meaning | Typical use |
|---|---|---|
| int value | Independent copy | Small input |
| const std::string& text | Borrowed read-only access | Inspect existing text |
| std::vector<int>& values | Borrowed mutable access | Intentional in-place change |
| std::vector<int> values | Owned local copy | Transform or consume a copy |
Return results instead of hiding them
A return value makes data flow visible and composes with other operations. A pure calculation with no hidden state is easy to test using examples around its boundaries. void is appropriate when the operation itself is the purpose, such as writing to a supplied stream, but it should not hide a result that callers need.
Keep input and output near the application boundary. Parse in main, call domain functions with typed values, and format the returned result. This separation becomes essential when the same rules later serve a GUI, web API, or automated test.
Quick knowledge check
Answer before you reveal.
01Why return a calculation instead of printing inside it?
The caller can test, format, store, or combine the result without redirecting console output.
02When is const std::string& a useful parameter?
For read-only access to an existing string when copying it would be unnecessary and the caller guarantees it remains alive during the call.
Exercise
Practice challenge
Extract a grade_letter function that accepts one validated score and returns a char. Call it for boundary values and keep all printing in main.
Requirements
- The function has no std::cin or std::cout calls
- Every valid score returns the expected grade
- The caller remains responsible for invalid input
Optional extension: Decide whether invalid scores should return an optional value in lesson 17.
Open in C++ compilerLesson checkpoint
One small step locks it in
Mark this lesson complete, then keep the momentum going.
Clear up the details
Frequently asked questions
Must every function be short?
No fixed line count defines quality. A function should have one coherent responsibility and an interface simpler than its implementation.
What is function overloading?
Several functions may share a name when their parameter lists differ and the calls remain unambiguous.
Should output parameters be avoided?
Prefer a return value for one result. Multiple related results can use a small struct; output references are useful only when their mutation is clear.