UPI Study › Courses › C++ Programming › Free lessons › Lesson 8.8 Free with a free account
Breaking Tasks Into Functions
C++ Programming · Chapter 8: FunctionsSection 8.8 · lesson 58 of 84video coming soon
← Recursive CallsNext lesson: Bundling Related Fields With Structs →
In this lesson
An applied challenge in decomposition.
- Find the functions hidden in a specification
- Test each for a single responsibility
- Choose signatures; split across header and source files
- Dispatch from a menu, and test functions on their own
Transcript
Everything said in the video, section by section (1,778 words). Read along, or search it.
Lesson Overview
Judgment Over New Syntax
No new syntax appears in this lesson. Everything needed has already been covered. What hasn't been covered is the judgment that turns one paragraph of requirements into a set of functions. Functions that each do one thing, can each be tested alone, and fit together, without knowing much about each other. That judgment is the whole subject here, and it's learned by working an example all the way through.
The Specification
A Marks Analyzer Brief
Here's the specification, exactly as it might arrive. A marks analyzer. Accept up to 50 marks, one at a time, finishing early when the user enters minus 1. Reject any entry outside 0 to 100, explain why, and ask again for the same position. Report the count, the average, the highest and the lowest. Grade each mark: A for 90 and above, B for 80, C for 70, D for 60, and F below that. Show how many marks fall in each band, with a bar. Let the user search for a mark. And present all of this as menu options, usable in any order, until the user quits. Notice how little it says about how anything should be organized.
Finding Functions
Underline Every Action in the Spec
The temptation is to start typing at requirement one, and stop at requirement seven. You'd get one enormous main, in which nothing can be tested separately. A better first move costs five minutes, and no typing. Underline every action in the specification. Each one is a candidate function, because a function is precisely a named action. Accept marks becomes read marks. Reject out-of-range values becomes is valid mark. Report the average becomes average of. Highest and lowest become two functions. Assign a letter becomes grade for. Count each band becomes tally grades. And search becomes position of. The menu isn't an action on data. It becomes the structure that calls the others.
One Responsibility
The One-Sentence Test Without And
Here's the test for whether a function is doing too much. Try to describe it in one sentence, without using and. Works out the average of a set of marks passes. Works out the average, and prints it, and warns if it's below 50, fails. That's three functions wearing one name. Two decisions are worth defending. Highest and lowest are two functions, not one. A single function would need somewhere to put two answers. And counting the bands doesn't repeat the grading rules. Tally grades calls grade for, so the grade boundaries exist in exactly one place.
Design Rule
Return Values or Print Output
Each function needs a decision that shapes everything downstream. Does it return a value, or produce output? The tempting answer, do both, is almost always wrong. A function that computes and returns can be used anywhere: printed, stored, compared, passed on, or tested. A function that computes and prints can only ever be used to print. It can't be tested without capturing its output, and the moment somebody needs the same number elsewhere, the calculation gets written a second time. So the calculating functions return, and say nothing. Print summary and print distribution display, and return nothing. No function appears in both groups. Read marks and read menu choice sit between the two, because obtaining input is inherently both. Keeping them few and small limits the awkwardness.
Signatures
Building the Header From Types
The remaining questions for each signature are mechanical. What must the function be given, and what type is its answer? Working through the list produces the header of the finished program. Two shared constants: the maximum number of marks, and the number of grade bands. Then a declaration for every function: what it's called, and how to call it. Average of returns a double, because an average of whole numbers is rarely whole. Grade for returns a char, because the answer genuinely is one letter. Position of returns an int, using minus 1 for absent, a value that can never be a real answer. The functions that only look at the marks take a const array, so they can't alter them. Every array is followed by a count, because an array carries no length into a function. And tally grades returns nothing: it has 5 answers, so it fills a caller-supplied array.
Call Graph
Drawing Which Function Calls Which
Before writing anything, draw which function calls which. Main calls the six functions in the middle column. Read marks calls is valid mark. Print summary calls average of, highest of and lowest of. And tally grades calls grade for. The five on the right call nothing, and depend on nothing, which is exactly what makes them easy to test. Arrows only ever point one way. No function needs to know about the one that called it. A picture with few arrows is a good design. One where everything points at everything is a warning.
File Layout
Splitting Into Three Files
Eleven functions plus main is more than belongs in one file. The conventional split uses three. Marks dot h holds the declarations and shared constants, with no bodies. It answers, what can I call, and how? Marks dot cpp holds the body of every declared function. It answers, how does each one work? That's of no interest to the caller. Main dot cpp holds main, the menu loop, and the data. It answers, what does this program do? Both source files include the header in quotation marks, which means look beside my file first. Each compiles alone, and the linker joins them. With a build tool, changing one function body recompiles only marks dot cpp.
Include Guards
Preventing Duplicate Header Definitions
A header pasted into a file twice defines everything in it twice, and duplicate definitions are an error. That happens more easily than it sounds: a file includes two headers, and one of them already includes the other. Here's the real message from including limits dot h twice. Clang names the redefinition, points at both include sites, and calls the header unguarded. An include guard prevents it in three lines. If not defined marks H, define marks H, then the declarations, then end if. The first time the header is pasted in, the marker doesn't exist yet, so it's defined, and the body is included. Every later time, the marker already exists, and the whole body is skipped. The marker must be unique across the whole program. The file's own name in capitals is the usual choice.
Main Program
A Short Menu Dispatch Loop
With every action written and tested, main becomes short enough to read at a glance. Each menu option does one thing: call a function. Choice 1 reads marks. Choice 2 prints the summary, unless no marks have been entered yet. Choice 3 tallies and prints the distribution. 4 searches, and 5 quits. Three things are worth copying into your own menus. The default case catches anything unexpected, instead of ignoring it. Quitting works by setting the loop's own condition, so there's exactly one way the loop ends. And the guards against an empty list live here, in the dispatch, rather than inside every calculation. That keeps those functions single-purpose.
Sample Run
Rejecting 120 and Searching Marks
Here's a real run. The user chooses 1 and enters marks, and at position 4 types 120. It's rejected, and the prompt returns to mark 4, rather than moving on. That works because read marks advances the count only when a value passes is valid mark. One line, working because validation is a separate function with one job. The summary reports 6 marks, an average of 75, a highest of 91, and a lowest of 55. The distribution shows a bar for each band. Searching for 79 finds position 3, counting from zero, and 99 is absent: minus 1.
Testing
A Second Main Runs Seven Checks
Here's the payoff for every decision so far. Because the calculating functions return values and print nothing, they can be checked without running the menu, without typing input, and without looking at any output by eye. Write a second main, in a file of its own, built instead of main dot cpp. It calls each function with known values, and compares the answer with what it should be. 100 is a valid mark. 101 is rejected. 90 is an A, and 89 is a B. 99 is absent from the sample. Seven of seven checks pass, and the exit code is zero.
Test Design
Choosing Values at the Edges
Choose test values at the boundaries. Testing that 75 gives a C proves almost nothing. Every plausible implementation gets the middle of a band right. What finds faults is the edges: 90 and 89, 60 and 59, 0 and 100, a list with one element, and a list with none. The pair grade for 90, and grade for 89, is precisely the off-by-one a wrong comparison operator would produce. And the test program returns a non-zero exit code when anything fails. That lets an automated system run it, and know whether the program is healthy, without a human reading the output.
Graded Exercise
Fuel Log Analyzer Assignment
Now the graded exercise: a fuel log analyzer. Apply the same process: decompose first, decide signatures, split across files, then write and test. Accept up to 100 entries, each the liters filled and the distance since the last fill, ending on a sentinel. Reject zero or negative values, and re-ask the same entry. Work out each entry's efficiency in kilometers per liter, and report the totals and the overall efficiency. Classify each entry as good, at 18 or more, fair, from 14 up to 18, or poor, below 14, with a bar for each band. And find the best and worst entries, from a menu. Submit three files with a correct include guard, a test file with at least 10 checks, four of them at boundaries, and a one-page note stating each function's responsibility.
Common Mistakes
Three Places Marks Get Lost
Marks are most often lost in three places. A function that calculates and prints. It can't be tested, and it costs marks in two criteria at once. Classification rules written twice, once when labeling an entry, and again when counting the bands. Call the labeling function from the counting one, as tally grades does. And tests that only use comfortable values. The interesting ones are 14 and 13.9, 18 and 17.9, a log with one entry, and a log with none.
Quick Check
Which Test Catches Greater-Than?
Quick check. Suppose gradeFor's A rule says mark > 90, instead of mark >= 90. Which test catches it: 75 is a C, 89 is a B, or 90 is an A? Pause and think. The check that 90 is an A. With greater-than, 90 drops to a B, so that check fails, while 75 and 89 still pass. That's why the tests sit on both sides of every boundary.
Key Takeaways
From Specification to Tested Functions
To summarize. Underline the actions in a specification. Each is a candidate function. Give each one a single responsibility, describable in one sentence without and. Calculations return values, and display functions print. Put declarations in a header with an include guard, and bodies in a source file. And test at the boundaries, with a non-zero exit code on failure.
About C++ Programming at UPI Study
C++ Programming (Computer Science 242) is a self-paced online course of 84 video lessons in 11 chapters, from your first program to decisions, loops, arrays, functions, classes, files, pointers and inheritance. It carries a NCCRS credit recommendation for 3 semester credits.
Every lesson transcript is free on pages like this one, and the videos are free with a free UPI Study account. Enrolling adds a practice quiz after each lesson, the graded work and final exam that earn the credit, lifetime access, and an official transcript on completion. UPI Study has helped over 130,000 students with college credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
Enroll now → Course details Download the syllabus Every course with PRO →
Why take it here
Why students take C++ Programming with UPI Study
Where the credit goes
Universities that work with NCCRS
C++ Programming carries a NCCRS credit recommendation. These universities work with NCCRS; some are UPI Study's signed partners. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
Thomas Edison State UniversitySigned partner
University of the PeopleSigned partner
Charter Oak State CollegeSigned partner
Strayer UniversitySigned partner
Columbia Southern UniversitySigned partner
Indiana TechSigned partner
Penn StateWorks with NCCRS
Southern New Hampshire UniversityWorks with NCCRS
SUNYWorks with NCCRS
+many more cooperating universitiesFind my college →Find my college → Not listed? Ask Athena in the corner of this page, or book a 15-minute call.
FAQ
Questions students ask us
About Breaking Tasks Into Functions
How do I find which functions to write from a program specification in C++?
How do I know if a C++ function is doing too much?
What goes in the header file and what goes in the source file when splitting a C++ program?
How do I test C++ functions on their own, and which values should I test?
About the C++ Programming course
How many college credits is C++ Programming?
How much does C++ Programming cost and what is included?
Which universities accept UPI Study credits?
How long does C++ Programming take?
How are the exams done?
How do my credits reach my college?
Ready for the full course? Earn 3 credits in C++ Programming.
84 video lessons, a quiz after each one, and the graded work that earns the credit. Recommended for credit by NCCRS.
3 credits · NCCRSEnroll →