By default, derived classes inherit all of the behaviors defined in a base class. In this lesson, we’ll examine in more detail how member functions are selected, as well as how we can leverage this to change behaviors in a derived class.
Calling a base class function
When a member function is called with a derived class object, the compiler first looks to see if that member exists in the derived class. If not, it begins walking up the inheritance chain and checking whether the member has been defined in any of the parent classes. It uses the first one it finds.
Consequently, take a look at the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
class Base { protected: int m_value; public: Base(int value) : m_value(value) { } void identify() { std::cout << "I am a Base\n"; } }; class Derived: public Base { public: Derived(int value) : Base(value) { } }; int main() { Base base(5); base.identify(); Derived derived(7); derived.identify(); return 0; } |
This prints
I am a Base I am a Base
When derived.identify() is called, the compiler looks to see if function identify() has been defined in the Derived class. It hasn’t. Then it starts looking in the inherited classes (which in this case is Base). Base has defined an identify() function, so it uses that one. In other words, Base::identify() was used because Derived::identify() doesn’t exist.
This means that if the behavior provided by a base class is sufficient, we can simply use the base class behavior.
Redefining behaviors
However, if we had defined Derived::identify() in the Derived class, it would have been used instead.
This means that we can make functions work differently with our derived classes by redefining them in the derived class!
In our above example, it would be more accurate if derived.identify()
printed “I am a Derived”. Let’s modify function identify() in the Derived class so it returns the correct response when we call function identify() with a Derived object.
To modify the way a function defined in a base class works in the derived class, simply redefine the function in the derived class.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Derived: public Base { public: Derived(int value) : Base(value) { } int getValue() { return m_value; } // Here's our modified function void identify() { std::cout << "I am a Derived\n"; } }; |
Here’s the same example as above, using the new Derived::Identify() function:
1 2 3 4 5 6 7 8 9 10 |
int main() { Base base(5); base.identify(); Derived derived(7); derived.identify(); return 0; } |
I am a Base I am a Derived
Note that when you redefine a function in the derived class, the derived function does not inherit the access specifier of the function with the same name in the base class. It uses whatever access specifier it is defined under in the derived class. Therefore, a function that is defined as private in the base class can be redefined as public in the derived class, or vice-versa!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class Base { private: void print() { std::cout << "Base"; } }; class Derived : public Base { public: void print() { std::cout << "Derived "; } }; int main() { Derived derived; derived.print(); // calls derived::print(), which is public return 0; } |
Adding to existing functionality
Sometimes we don’t want to completely replace a base class function, but instead want to add additional functionality to it. In the above example, note that Derived::identify() completely hides Base::identify()! This may not be what we want. It is possible to have our derived function call the base version of the function of the same name (in order to reuse code) and then add additional functionality to it.
To have a derived function call a base function of the same name, simply do a normal function call, but prefix the function with the scope qualifier (the name of the base class and two colons). The following example redefines Derived::identify() so it first calls Base::identify() and then does its own additional stuff.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Derived: public Base { public: Derived(int value) : Base(value) { } int GetValue() { return m_value; } void identify() { Base::identify(); // call Base::identify() first std::cout << "I am a Derived\n"; // then identify ourselves } }; |
Now consider the following example:
1 2 3 4 5 6 7 8 9 10 |
int main() { Base base(5); base.identify(); Derived derived(7); derived.identify(); return 0; } |
I am a Base I am a Base I am a Derived
When derived.identify()
is executed, it resolves to Derived::identify(). However, the first thing Derived::identify() does is call Base::identify(), which prints “I am a Base”. When Base::identify() returns, Derived::identify() continues executing and prints “I am a Derived”.
This should be pretty straightforward. Why do we need to use the scope resolution operator (::)? If we had defined Derived::identify() like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Derived: public Base { public: Derived(int value) : Base(value) { } int GetValue() { return m_value; } void identify() { identify(); // Note: no scope resolution! cout << "I am a Derived"; } }; |
Calling function identify() without a scope resolution qualifier would default to the identify() in the current class, which would be Derived::identify(). This would cause Derived::identify() to call itself, which would lead to an infinite loop!
There’s one bit of trickiness that we can run into when trying to call friend functions in base classes, such as operator<<. Because friend functions of the base class aren’t actually part of the base class, using the scope resolution qualifier won’t work. Instead, we need a way to make our Derived class temporarily look like the Base class so that the right version of the function can be called.
Fortunately, that’s easy to do, using static_cast. Here’s an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include <iostream> class Base { private: int m_value{}; public: Base(int value) : m_value{ value } { } friend std::ostream& operator<< (std::ostream &out, const Base &b) { out << "In Base\n"; out << b.m_value << '\n'; return out; } }; class Derived : public Base { public: Derived(int value) : Base{ value } { } friend std::ostream& operator<< (std::ostream &out, const Derived &d) { out << "In Derived\n"; // static_cast Derived to a Base object, so we call the right version of operator<< out << static_cast<const Base&>(d); return out; } }; int main() { Derived derived{ 7 }; std::cout << derived << '\n'; return 0; } |
Because a Derived is-a Base, we can static_cast our Derived object into a Base, so that the appropriate version of operator<< that uses a Base is called.
This prints:
In derived In base 7
![]() |
![]() |
![]() |
does the static_cast example creates a copy of the class ?
or since i see like a reference & does it permanently cast d into a class Base ?
The cast returns a reference to the base portion of `d`. It creates a new reference, but it doesn't copy anything.
I’m curious, does the static cast for friend functionality just create a temporary base class object using the base values in derived as initializers, or is there some kind of special functionality that static casts have for derived classes?
The `static_cast` created a temporary `Base` object. If `Base` had a user-defined copy-constructor, that would have been used. Since `Base` doesn't have one, a member-wise copy is made.
The cast should have been `static_cast` to avoid copying anything. I've updated the lesson. This returned reference returns to the `Base` portion of the `Derived` without creating a new object.
>>This means that we can make functions work differently with our derived classes by redefining them in the derived class!
shouldn't with replaced with 'within'?
How do I use these techniques in separate files?
Define `Base` in one file. Define `Derived` in another file and include "base.h"
Thank you nascar driver, we did figure it out, but we were thinking of something different when me and my friend posted this.
Does calling member function of base class with scope resolution passes "this" pointer too through additional parameter? Because calling it with scope resolution seems similar to call to a static member function which doesn't have "this" pointer.
All non-static member functions have a this-pointer parameter. They can't be called them without a this-pointer.
fails by telling me that void B::no_op() doesn't exist. I thought it did, since it was inherited from A.
Just to be sure: do I need to expicitly declare B::no_op() in b.hpp to be able to define it in b.cpp? (side remark, real names of classes and member signatures differ). My guess:
is how I'm supposed to do it.
> is how I'm supposed to do it.
That's how you're supposed to do it. I can't tell you why the explicit declaration is required, there must be a reason.
Symbols need to be declared before they're used (eg in a definition). For class members this is only possible during the class definition.
The solution would be to have the declaration take place implicitly during the definition of derived classes. But if you then don't define B::no_op() you'll get a linker error instead of it using A::no_op().
That means that at the time of definition of B::no_op() we need to go back and change the B class definition.
I can see why they opted to not do that but it might come in handy, at least for pure virtual functions.
“The object of super class accesses the overridden member function of sub class”. Write a program to demonstrate this?
How do I make sure I'm not accidentally masking a method inside the parent of a class? Is making sure I don't the only way?
Also, what happenes if I make a derived class a friend class of the base class, while also having the derived class inherit from the base class?
Basically,
I tried it, doesn't cause compile errors, I can access private variables of base from the derived. That's it I'm guessing?
I don't think there's an automated way of detecting an accidental override, because a system can't know if you're doing it intentionally or not.
There's nothing special about befriending a child class.
Oh cool! No weird additional behaviors then!
Thank you!
If friend is not a member of the class, doesn't that mean the friend function shouldn't be able to access the class' private members?
`friend` gives the function access to the class even though it's not a member. That's the only purpose of `friend`.
Right. I wasn't thinking properly. Thanks
in the section: "Redefining behaviors"
i thought because we made it the opposite as private we can use the print from the base class as it can't find the print from the derived class
Hi Michael!
Functions are resolved before their access modifiers are taken into account. Your compiler sees that you want to call derived.print, it finds @Derived::print, it stops searching. Only after this has happened, the compiler checks if you actually have access to @Derived::print, which you don't, so it errors out.
I would like to ask you about polymorphism.
Redefining behaviors - it's means: Creating functions, which overide function in base class.
Could tell me if I create functions without virtual keyword, does it belong to polyformism?
I know, that base ptr or ref, call only base functions (not dervied).
I'd say no, as it means the derived class can't be properly substituted for the base class and still be functional as intended.
I have advanced a little more in the tutorial and now I think in the line
we are doing upcasting and we are using anonymous sliced object to be able to use the friend function in the base class.
Quote:
"To modify a function the way a function defined in a base class works in the derived class, simply redefine the function in the derived class."
I believe you meant:
"To modify the way a function defined in a base class works in the derived class, simply redefine the function in the derived class."
I did indeed. Thanks for pointing out the typo.
why we use static cast?
To convert a value from one type to another. See lesson 4.4a on explicit type conversion (casting).
Don't we then convert from bigger type to a smaller type? Isn't, there, data loss then?
It depends on the context. If you static cast a double value to a float value, then yes. If you static cast a derived class pointer or reference to a base class pointer or reference, then no.
Hi Alex, I don't understand why we are using the 'using' keyword when redefining the access specifier on inherited members.
why do we use 'using'. Also, why does c++ not include the braces when re-specifying access of a member function i.e.
Thanks for the help :).
In this context, the using keyword tells the code that we want to use the base class function at this new access level. C++ doesn't seem to like to create new keywords (that may cause naming conflicts with existing programs) so they often overload existing ones.
I'm guessing the parenthesis are not needed because you don't need to respecify the parameters. Having empty parenthesis implies no parameters, which isn't necessarily the case.
@Alex
In the "Redefining functionality" section, I think this line needs review:
"Therefore, a function that is defined as private in the base class can redefined as public in the derived class, or vice-versa!"
A function that is defined as private in base class cannot be accessed by the derived class let alone redefining it.
Hi Alex
Can I redefine a member variable in the derived class?
I'm not sure what you mean by "redefine" in this context. You can't have a derived class change the type of a variable in the base class, but you can declare a member variable with the same name. This member variable will shadow the variable in the base class with the same name.
What do you mean by 'shadow'?
It is written "Therefore, a function that is defined as private in the base class can redefined as public in the derived class, or vice-versa!"
But the derived class can not access private members/functions of base class . so how is it possible ?
If a function is private in the base class and reimplemented as public in the derived class, the derived version will be callable externally, but it won't be able to access private members from the base class (after all, they are private). There no rule-breaking here.
Does that mean void print() in the base class and derived class are not the same functions?
They are not the same. They just happen to share a name and the latter is considered an override of the former.
Since derived functions like void print() are overrides of their base class counterparts, then why can't the override keyword be used with them? It looks like C++ compilers only allow the keyword to be used with virtual functions, yet it seems to me that derived override functions like void print() would equally benefit from it.