UPI Study › Courses › C++ Programming › Free lessons › Lesson 11.3 Free with a free account
Overriding Inherited Behavior
C++ Programming · Chapter 11: Inheritance and PolymorphismSection 11.3 · lesson 80 of 84video coming soon
← Public, Private, and ProtectedNext lesson: Why Encapsulation Strengthens Design →
In this lesson
Rewriting what a derived class inherits.
- Redefine an inherited function; predict which version runs
- Explain name hiding, and bring hidden versions back
- Extend a base version; use override to check replacements
- Describe static binding, and the problem it creates
Transcript
Everything said in the video, section by section (1,356 words). Read along, or search it.
Opening Problem
When Inherited Behavior Is Wrong
A derived type inherits everything the base offers, and some of it will be wrong for the specialized case. Declaring a function with the same name in the derived type replaces it, but only in ways worth knowing exactly. The replacement is decided when the program is built, from the type you wrote, rather than the object you have. That produces a result most people find surprising, and it's the problem section 11.5 exists to solve.
Redefinition
Savings Redefines Describe and Fee
A derived type may declare a member function with the same name as one it inherited. Here, account describes itself and charges a fee of fifty. Savings redefines both, describing itself as a savings account, with a fee of twenty. Calls on a savings object now find the derived version. S dot describe prints savings account. And the base version is still there. Writing account, colon colon, describe, on the same object, prints account. Run it.
Name Lookup
The Search Stops at the Name
Here's how the compiler resolves the name. For s dot describe, it looks in savings first. It finds describe there, and the search stops. Account is only searched if the name wasn't found above. Writing account, colon colon, goes straight to the base version, bypassing the search. Stops as soon as it finds the name is the whole rule. And it's the cause of what comes next.
Name Hiding
One Derived Log Hides Them All
The search stops at the name, not at a matching function. So declaring any function called log in the derived type hides every base function called log, including ones taking different arguments. Here, base has two versions of log, one taking an int code. Derived redefines only the one without arguments. Now calling log with four hundred four on a derived object fails to build. The compiler counts too many arguments. And the error message doesn't mention hiding at all. It just points at the derived version, which expects no arguments.
The Fix
Restoring Hidden Versions With Using
The natural conclusion is that the base version has been deleted. It hasn't. Naming base explicitly still works: d dot base colon colon log, with four hundred four. It's simply not considered, because the search found the name in derived, and never looked further. To bring the hidden versions back, write using base, colon colon, log, inside the derived type. Now the call with an int finds the base version again. Run it. This isn't overloading. Within one type, same-named functions are considered together. Across an inheritance boundary, the derived declarations are searched first, and alone. Whenever a call that used to work stops compiling after someone adds a derived function, this is the reason.
Extending Base
Call the Base, Then Add More
Often the derived version should do what the base did, and then something more. So call the base version explicitly, and add to it. Here, savings describe first calls account's describe, then prints its own addition. Run it: both lines print. Don't copy the base body into the derived one. A copy won't follow the base when it changes.
Common Mistake
Unqualified Call Recurses Forever
And the qualification matters. Plain describe, inside savings describe, finds itself, and the function calls itself forever. The compiler even says so. It warns that all paths through the function call itself. So I won't run it.
Exact Match
What Replacement Really Requires
For a derived function to replace a base one, rather than merely hide it, the declarations must agree exactly. Same name, same parameter list, and the same const marking. Against a base describe marked const, only an identical declaration replaces it. Leave off the const, and you get a separate function that hides the base one, unusable on a read-only object. Add a parameter, and it's a new function that also hides. Change the capitalization, and the base stays reachable, so nothing seems wrong until the wrong one runs. A different result type is rejected, once the base is marked replaceable. A near miss isn't an error. It quietly produces a second, unrelated function.
Safety Check
Override Turns Near Misses Into Errors
Every one of those near misses compiles. Writing override after the derived declaration turns each into a build error. It states an intention the compiler can verify: this is meant to replace something in the base. A wrong name, a missing const, or an extra parameter are each caught at once. Only the exact match is accepted. Override applies where the base declaration is marked as replaceable, which section 11.5 covers. It never changes what the code does. Its entire job is to reject a signature that doesn't line up. Once you meet that marking, write override on every replacement, without exception.
Surprising Result
Base Handles Give the Base Answer
Here's the part that surprises people. A derived object may be referred to through a base reference, or a base pointer. That's one of the main reasons to arrange types this way. Here, one savings object is reached directly, through an account reference, and through an account pointer. Called directly, it says savings account. But through the reference and the pointer, it says account. Even though the object really is a savings.
Explanation
One Object, Two Answers
One object, two answers. The savings object has both versions inside it. Which one runs depends on the handle. Through a savings variable, the declared type and the object agree, so the savings version runs. Through an account reference or pointer, the version is chosen from the declared type, so the account version runs. And asking by name for account describe runs the base version on purpose. It's decided by the label on the handle, not by what's on the end of it.
Static Binding
Why Collections of Base Pointers Fail
This behavior is called static binding. The version is bound to the call when the program is built, and nothing at run time can change it. For a call on a plain object, it gives exactly what you expect. Through a base handle, it doesn't, and that's a real problem. Hierarchies usually exist to treat kinds uniformly. Keep a collection of base pointers to every kind of account, walk it once, and ask each to describe itself. Under static binding, every call reaches the base version. Run it: three savings accounts, and the collection prints account three times. With fifty accounts it would print account fifty times, and the specialized versions never run. Making the call consult the actual object is the subject of section 11.5.
Troubleshooting
Matching Symptoms to Their Causes
Here are the faults worth recognizing. A base overload that stops compiling after a derived function is added is name hiding. Add a using declaration. The base version running through a base handle is static binding, and its remedy comes in section 11.5. A derived version that never runs, even on a derived object, has a mismatched signature. Compare the declarations, and add override. A program that hangs on the call has a derived version calling itself. Qualify the base call. And when a copied base body drifts out of date, it should have been a call to the base version instead.
Key Terms
Vocabulary for Overriding Behavior
Name hiding is a derived declaration concealing every base function of that name, whatever their parameters. A qualified call names the scope explicitly, to bypass the ordinary search. A using declaration brings hidden base versions back. A signature is the name, parameter list and const marking that must agree for a replacement. Override states that a derived function is meant to replace a base one, so the compiler can check it. And static binding chooses which version a call reaches when the program is built, from the declared type.
Quick Check
Missing Const on the Derived Version
Quick check. The base declares describe, marked const, and the derived declares describe without const. What has the programmer created, and what will they observe? Pause and predict.
Answer
A Hiding Function, Not a Replacement
A separate function that hides the base one, not a replacement. Called on a plain derived object, the derived version runs. Through a base reference, the base version runs. And on a read-only derived object, the call won't even compile, because the hidden base version isn't considered. Remove the comment marks and see.
Key Takeaways
Lookup, Hiding, Signatures, and Binding
To summarize. Name lookup stops at the first scope holding the name. Redefining one function hides all the base versions of that name, and a using declaration brings them back. Extend a base version by calling it with its qualified name. Only an exact signature replaces a base function, and override checks that for you. And through a base handle, static binding runs the base version.
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 Overriding Inherited Behavior
Why did a base class function stop compiling after I added a same-named function to the derived class?
How do I call the base class version of a function from inside the derived version?
What does override do in C++, and which mistakes does it catch?
Why does a base class reference run the base version of a function on a derived object?
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 →