0% found this document useful (0 votes)
14 views17 pages

Polymorphism 2

The document discusses the principles of inheritance and polymorphism in object-oriented programming, focusing on designing base classes, name hiding, and the implications of modifying member functions in derived classes. It explains the behavior of constructors, destructors, and static member functions regarding inheritance, as well as the concept of private inheritance and the use of protected members. Additionally, it covers upcasting and its safety, emphasizing the relationship between derived and base classes.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views17 pages

Polymorphism 2

The document discusses the principles of inheritance and polymorphism in object-oriented programming, focusing on designing base classes, name hiding, and the implications of modifying member functions in derived classes. It explains the behavior of constructors, destructors, and static member functions regarding inheritance, as well as the concept of private inheritance and the use of protected members. Additionally, it covers upcasting and its safety, emphasizing the relationship between derived and base classes.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 17

Inheritance

andPolymorphismm
Final Remarks
Designing A Base Class with Inheritance and
Polymorphism
in mind

For the base class


1. Identify the set of operations common to all the
children
2. Identify which operations are type-independent
(these become (pure) virtual to be overridden in
derived classes)
3. Identify the access level (public, private, protected)
of each operation

For a more complete discussion, see “Essential C++”, Stanley Lippman (section 5.4)
2
Name hiding
• If you inherit a class and provide a new definition for one of its
member functions, there are two possibilities.
1. The first is that you provide the exact signature and return type in the
derived class definition as in the base class definition.
This is called redefining for ordinary member functions and overriding
when the base class member function is a virtual function.
• But what happens if you change the member function argument list
or return type in the derived class? Here’s an example:
#include <iostream>
#include <string>
using namespace std;
class Base {
public:
int f() const {
cout << "Base::f()\n";
return 1;
}
int f(string) const { return 1; }
void g() {}
};
class Derived1 : public Base {
public: int main() {
void g() const {}
}; string s("hello");
class Derived2 : public Base {
public:
Derived1 d1;
// Redefinition: int x = d1.f();
int f() const {
cout << "Derived2::f()\n"; d1.f(s);
}
return 2; Derived2 d2;
}; x = d2.f();
class Derived3 : public Base {
public: //! d2.f(s); // string version hidden
// Change return type: Derived3 d3;
void f() const { cout << "Derived3::f()\n"; }
}; //! x = d3.f(); // return int version
class Derived4 : public Base {
public: hidden
// Change argument list: Derived4 d4;
int f(int) const {
cout << "Derived4::f()\n"; //! x = d4.f(); // f() version hidden
return 4;
}
x = d4.f(1);
}; }
Name hiding
In Base you see an overloaded function f( ), and Derived1 doesn’t make any
changes to f( ) but it does redefine g( ).
In main( ), you can see that both overloaded versions of f( ) are available in
Derived1. However, Derived2 redefines one overloaded version of f( ) but not
the other, and the result is that the second overloaded form is unavailable.
In Derived3, changing the return type hides both the base class versions, and
Derived4 shows that changing the argument list also hides both the base class
versions.
In general,
we can say that anytime you redefine an overloaded function name from the
base class, all the other versions are automatically hidden in the new class.
Name hiding
If you change the interface of the base class by modifying the
signature and/or return type of a member function from the base
class, then you’re using the class in a different way than inheritance
is normally intended to support.
It doesn’t necessarily wrong, it’s just that the ultimate goal of
inheritance is to support polymorphism, and if you change the function
signature or return type then you are actually changing the interface of
the base class.
Functions that don’t automatically inherit
Not all functions are automatically inherited from the base class into the derived class. Constructors
and destructors deal with the creation and destruction of an object, and they can know what to do
with the aspects of the object only for their particular class, so all the constructors and destructors
in the hierarchy below them must be called. Thus, constructors and destructors don’t inherit and
must be created specially for each derived class.

In addition, the operator= doesn’t inherit because it performs a constructor-like activity. That is, just
because you know how to assign all the members of an object on the left-hand side of the =
from an object on the right-hand side doesn’t mean that the assignment will still have the same
meaning after inheritance.
Inheritance and static member functions

static member functions act the same as non-static member


functions:
1. They inherit into the derived class.
2. If you redefine a static member, all the other overloaded functions in
the base class are hidden.
3. If you change the signature of a function in the base class, all the
base class versions with that function name are hidden
(this is really a variation of the previous point).
However, static member functions cannot be virtual
Private inheritance
You can inherit a base class privately by leaving off the public in
the base-class list, or by explicitly saying private. When you
inherit privately, you’re “implementing in terms of;” that is, you’re
creating a new class that has all of the data and functionality of the
base class, but that functionality is hidden, so it’s only part of the
underlying implementation.
The inherited class user has no access to the underlying functionality,
and an object cannot be treated as an instance of the base class
Private inheritance
You may wonder what the purpose of private inheritance is, because
the alternative of using composition to create a private object in the
new class seems more appropriate. private inheritance is included in
the language for completeness, but if for no other reason than to
reduce confusion, you’ll usually want to use composition rather than
private inheritance.
However, there may occasionally be situations where you want to
produce part of the same interface as the base class and disallow the
treatment of the object as if it were a base-class object. private
inheritance provides this ability.
Publicizing privately inherited
members
class Pet {
Publicizing privately inherited public:
char eat() const { return 'a'; }
members When you inherit int speak() const { return 2; }
privately, all the public float sleep() const { return 3.0; }
members of the base class float sleep(int) const { return 4.0; }
};
become private. If you want class Goldfish : Pet { // Private inheritance
any of them to be visible, just public:
say their names (no arguments Pet::eat; // Name publicizes member
Pet::sleep; // Both overloaded members exposed
or return values) in the public };
section of the derived class:
int main() {
Goldfish bob;
bob.eat();
Notice that giving the name of bob.sleep();
an overloaded function bob.sleep(1);
//! bob.speak();// Error: private member function
exposes all the versions of the }
overloaded function in the
base class.
protected
In an ideal world, private members would always be hard-and-fast
private, but in real projects there are times when you want to make
something hidden from the world at large and yet allow access for
members of derived classes.
The protected keyword reflects a practical compromise, allowing
derived classes to access certain members of the base class while still
maintaining a level of encapsulation.
it says, “This is private as far as the class user is concerned, but
available to anyone who inherits from this class.”
protected
The best approach is to leave #include <fstream>
using namespace std;
the data members private – class Base {
int i;
you protected:
int read() const { return i; }
should always preserve your void set(int ii) { i = ii; }
public:
right to change the underlying Base(int ii = 0) : i(ii) {}
int value(int m) const { return m * i; }

implementation. };
class Derived : public Base {
int j;
You can then allow controlled public:
Derived(int jj = 0) : j(jj) {}
access to inheritors of your void change(int x) { set(x); }

class through protected };


int main() {
member functions: Derived d;
d.change(10);
}
Upcasting
The most important aspect of inheritance is not that it provides member functions for the new
class, however. It’s the relationship expressed between the new class and the base class.
This relationship can be summarized by saying, “The new class is a type of the existing class.”

class Instrument {
public:
void play() const {}
};
// Wind objects are Instruments
// because they have the same interface:
class Wind : public Instrument {};

void tune(Instrument& i) {
// ...
i.play();
int main() {
Wind flute;
tune(flute); // Upcasting
}
Upcasting

Casting from derived to base moves up on the inheritance diagram,


so it’s commonly referred to as upcasting.
Upcasting is always safe because you’re going from a more specific type
to a more general type – the only thing that can occur to the class
interface is that it can lose member functions, not gain them.

This is why the compiler allows upcasting without any explicit casts or
other special notation.
Upcasting and the copy-
constructor
If you allow the compiler to synthesize a copy-constructor for a
derived class, it will automatically call the base-class copyconstructor,
and then the copy-constructors for all the member
objects (or perform a bitcopy on built-in types) so you’ll get the
right behavior:
Operator overloading & inheritance
Except for the assignment operator, operators are automatically
inherited into a derived class.

You might also like