


Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Single inheritance is where one class is derived from another class. Objects are defined as extensions previously defined objects. The extension inherits all ...
Typology: Schemes and Mind Maps
1 / 4
This page cannot be seen from the preview
Don't miss anything!



We’ve seen templates as one method for code reuse. Another important technique is inheritance. One of the important benefits of inheritance is that it allows us to extend a class without modifying (or breaking) the original class. Single inheritance is where one class is derived from another class. Objects are defined as extensions previously defined objects. The extension inherits all data members and member functions.
Abstractly, inheritance represents an is‐a relationship. A rectangle is a shape. A square is a rectangle. We can build hierarchies using this concept. For shapes (the arrow represents an is‐a relationship):
The square is an example of multiple inheritance. (The structure is not necessarily a tree.) Note that we draw the arrow from the “child class” or derived class to the “parent class” or base class. Other terminology is superclass and subclass. Every member of the derived class is also a member of the base class, but not vice versa. Attributes (like private data) that every shape has are inherited by all the descendent classes. This doesn’t mean that they each have the same value, but
that they have some value for these attributes. For our general shapes, there might not be any attributes that all of the derived classes share. Members of the derived classes can have additional attributes. Quadrilaterals have a center of mass, an area, and a circumference.
Other commonly seen first examples show Mammal as the parent class with child classes such as Human, Dog, etc. Here is a simple example of inheritance in C++. We can first define the base class, which will be Person.
class Person { public: string getName() const; int getAge() const; bool getGender() const; void display() const; private: string name; bool gender; int birthdate; // Could be days elapsed since Jan. 1, 1800. };
Shape
Quadrilateral
Parallelogram
Trapezoid
Circle
RectangleSquare Rhombus
Conic section
Square
Concave quadrilateral
Parabola (^) Ellipse Hyperbola
Now we can define derived classes that inherit from Person. For example, every student is a person, so inheriting the
attributes (members) from the Person class is reasonable.
class Student : public Person { float getGPA() const; void display() const; private: float gpa; };
The “public Person” after the class name indicates that the Student class inherits from the Person class in “public” way. All public operations in the Person class remain public in the Student class. We could instead use private inheritance in which the public members of Person become private in Student. This is unusual, but there are cases where you don’t want people to be able to use the interface to the base class. The default is private inheritance, so it is important to remember the “public.”
In the Student class, we have added members that can be used for a student and overridden the display operation. (You
override an operation if the signature is the same and overload it if the signature is different.) If the display operation is used for an object known to be a Student, then the Student’s display is used instead of the Person’s display. Can it be the case that we don’t know whether an object is a student or not? Consider the following code: Person *p; if (someBool) p = new Person; else p = new Student; p->display(); This is legal code, using a concept called polymorphism. Which display routine is used? In this case, it is the Person’s display routine, even if p is a Student. However, there are ways to change this. We’ll return to this concept in some detail
later. Note that the following is illegal code. Student *p = new Person; // ILLEGAL A Student is a Person, but a Person is not necessarily a Student.
We can use “partial overriding” by calling the base class method in the derived class method. void Student::display() const { Person::display(); cout << “ “ << gpa; }
Note that data that is private in the base class, cannot be accessed by the derived class. Of course, sometimes we would like to be able to access this data. There are two ways to do this. The first (and safest) way is to use accessor or mutators in the base class. The second is to change the visibility of the data. However, we certainly don’t want to change the visibility
to “public.” There is another visibility that allows derived classes to access the data, but not other classes. This visibility is “protected.” So, if we want the Student class to be able to access the birthdate attribute (but not the others), use:
protected: int birthdate; private: string name; bool gender;
Note that the default constructor for Student will call the constructor for Person and then perform the default initialization on new data members, such as gpa. If we implement the constructor for Student explicitly, we can still call the constructor for Person if want:
Student::Student() : Person() { gpa = 0.0; }
(This can be done in the initialization list or in the method code.)
Note that to override a function, you need to use the exact same prototype for the function (with one exception). You cannot substitute a derived class for a base class parameter (or vice versa). For example, you can’t override: bool Person::operator<(const Person &p) const
with bool Student::operator<(const Student &p) const The parameter is different, so it is considered to be overloaded, not overridden.
We can implicitly cast from the derived class to the base class, but not vice versa. Let’s say that we want to implement a virtual operator< for people. If they are both students, we can order them by GPA. bool Student::operator<(const Person &p) const { const Student &s = static_cast<const Student &>(p); return gpa < s.gpa; }
We have to override the base class using a Person parameter to match the prototype for the virtual function and make the class non‐abstract. Inside the function, we have to cast to a Student, since only they have a GPA. What if the parameter is not a Student?
There is also a dynamic casting operation that does run‐time checking: const Student *s = dynamic_cast<const Student *>(p); If p is not a Student, then the dynamic cast will return NULL. This can be used to prevent casting pointers to other objects
into pointers to Students. Note that you must turn on RTTI (run‐time type information) to use dynamic_cast in Visual C++ (Properties – C/C++ – Language – Enable Run‐Time Type Info). This works with references, too. However, C++ doesn’t have NULL references, so dynamic_cast throws an exception if you try to call it with an incorrect parameter.
There is one exception to the rule about having the same prototype. You are allowed to return a pointer or reference to a derived class object instead of the base class. This facilitates operations such as clone.
We saw above that we can inherit from multiple base classes. This can be tricky and should be used only when necessary.
class rhombus; class rectangle;
class square: public rhombus, public rectangle {…};
One issue that can arise is that both base classes may define a particular method. You need to be able to specify which one to use. For example, if both the rhombus and rectangle class had a method foo, then we could use: mySquare->rhombus::foo();
Inheritance is a useful tool for extending classes, allowing code reuse, and writing general containers (your BSTree is
general to any class that inherits from Object, with some minor modifications). Polymorphism allows objects to have different behaviors depending on the run‐time binding of a variable. However, it is important to use virtual functions to ensure polymorphic behavior.