Search

Sponsored Links

Meta

Categories

Archives

Recent Posts

RSS Feeds

06
Dec

C++ Basics Tutorial - Lesson 8

Related Blog Items

Please refer lesson 7..

8. Inheritance
8.1 Method Overriding
8.2 Initialization of the Base-class Part of the Object
8.3 Conversion between base class and derived class
8.4 is-a , has-a , “Use-A” and “Know-A” Relationship
8.5 Public, Protected and Private Inheritance
8.6 Shrinking Inheritance
8.7 Methods That Are Not Inherited
8.8 Software Engineering with Inheritance
8.9 Partial Assignment
8.10 Sequence of Constructor Call in Inheritance
8.11 Default Constructor in Inheritance

8. Inheritance

8.1 Method Overriding

When a method of the derived class has the same signature as that of the base class, it is said that the method of the derived class overrides the method of the base class. The base-class version of method is only overridden from external point of view they are still accessible from the derived class internally. The derived-class method often needs to call its overridden base-class method to perform part of the job related to the base-class data members.

In this case, if you forget to use the scope resolution operator in front of the invoked overridden base-class method, it will actually call itself and thus create a infinite recursion until the memory is run out.

One interesting point: normally all overloaded methods can be accessed through different signatures, but if you overload a base-class method in the derived class with a different signature, the base-class method is actually overridden instead of overloaded:

 
#include <iostream>
 
class Base 
{
public:
   int print(int i)
   {
      cout << "Base-class version, int a = " << i << "\n";
      return i;
   }
};
 
class Derived : public Base 
{
public:
   char print(char c)
   {
      cout << "Derived-class version, char c = " << c << "\n";
      return c;
   }
};
 
int main()
{
  Derived d1;
  char c = 'a';
  int i = 1234;
  d1.print(i);
  d1.print(c);
  cin >> i;
}
</iostream>

Output will be:
Derived-class version, char c = p
Derived-class version, char c = a

Only the derived-class method will be called. This rule looks a bit wierd. In Java, a base-class method can be overloaded in the derived-class and both methods can be accessed by clients (if they are both public).

8.2 Initialization of the Base-class Part of the Object

To initialize the base-class part of data members of the derived class, member initializer must be used.

If a base-class constructor is not explicitly invoked, the compiler will implicitly call the base-class default constructor. If no base-class default constructor is provided, the compiler will issue a syntax error.

 
class Derived : public Base 
{
public:
  Derived(const int = 0, const int = 0);
  Derived(const Derived &);
  const Derived & operator=(const Derived &);
private:
  int member;
};
 
Derived::Derived(const int i1, const int i2) : Base(i1), member(i2)
{}
 
Derived::Derived(const Derived & d) : Base(d), member(d.member)
{}
 
const Derived & Derived::operator=(const Derived &rv)
{
      member = rv.member;
      Base::operator=(rv);
      return *this;
}

When a derived-class object is created, the base-class constructor will be called first, then the derived-class constructor. When it is to be deleted, the derived-class destructor is called first, then the base-class destructor.

In a multi-level inheritance, the constructor of a certain level is only responsible to call the constructor of the next-level class.

8.3 Conversion between base class and derived class

Objects of a derived class may be used as an object of the base class. The compiler will make an implicit conversion. But objects of the base class can not be used as objects of derived class. The derived class is more specific and thus contains more info. To cast a less specific class to a more specific class, the extra info needed to construct the later one is missing. It may cause serious run-time errors.

Suppose:

Base is a base class, b1 is a base-class object, basePtr1 is a base-class pointer;
Derived is a derived class, d1 is a derived-class object, derivedPtr1 is a derived-class pointer
Both the base and the derived class has a method “print( )”

Using object name:

b1.print(); // base-class version “print()” called
d1.print(); // derived-class version “print()” called

Using Pointer:

basePtr1 = &b1;
derivedPtr1 = &d1;
basePtr1->print(); // Base-class version “print()” called
derivedPtr1->print(); // Derived-class version “print()” called
derivedPtr1 = &b1; // Try to convert b1 to Derived-class object. Illegal!
basePtr1 = &d1; // Implicitly convert d1 to Base-class object.
basePtr1->print(); // Still the base-class version “print()” called

However, when you use

derivedPtr1 = static_cast(&b1);

you are telling the compiler that you know the danger and you deliberately want to take that risk. So compiler will allow that operation. But the derived-class part of data will remain undefined.

8.4 is-a , has-a , “Use-A” and “Know-A” Relationship

is a relationship is inheritance. In an is-a relationship, an object of a derived class can also be treated as an object of the base class, just like a 4WD can be treated as a vehicle.

has a relationship is composition. In a has-a relationship, an object has one or more objects of other classes as members. Just like a 4WD has an engine.

A person is not a car and do not contain a car, but he uses a car. A method uses an object simply by issuing a method call to a method of that object.
An object can know another object by containing a pointer to it. This is called “know a” relationship. Sometimes it is called “association”.

8.5 Public, Protected and Private Inheritance

Public inheritance inherits base class’s public and protected members as its own public and protected members, and base class’s private members are hidden.

Therefore, if you do not want derived classes to access a member, you should declare it private. If you do not allow clients but would allow derived classes to access it, you should declare it protected.

Protected data breaks encapsulation a change to protected members of a base class may require modifications of all derived classes. Therefore, always try to declare data members private, and use protected as a final resort.

=>Public inheritance

From base-class to derived-class:
public => public
protected => protected
private => hidden

=>Protected inheritance

public => protected
protected => protected
private => hidden

=>Private inheritance

public => private
protected => private
private => hidden

8.6 Shrinking Inheritance

Through private inheritance, all the members of the base class are made hidden or private in the derived class, thus inaccessible to the clients. This is useful for shrinking inheritance: you only want to inherit part of the methods from a class. You override the base-class methods with a simple call it, and for those unselected methods, they became private members and suppressed to the clients.

For example, suppose Base is a base class, and the derived class will be like:

template <class>
class Derived : private Base 
{
public:
   Derived(const Type & x) : Base(x) {}
   void setX1(const Type & x)  {  Base::setX1(x); }
   void setY1(const Type & x)  {  Base::setY1(x); }
   const Type & getX1() const  {  return Base::getX1(); }
   const Type & getY1() const  (  return Base::getY1(); )
};

8.7 Methods That Are Not Inherited

The four Orthodox Canonical Form methods constructors, copy constructors, assignment operators and destructors, are not inherited in C++.

8.8 Software Engineering with Inheritance

A derived class does not need to access the source code of the base class, but only need the base class’s object code. Therefore, independent software vendors (ISV) can develop their own class libraries and provide clients with only object codes.

Modifications to a base class do not require derived classes to change, as long as the public and protected interfaces of the base class remain unchanged. Derived classes may, however, need to be recompiled.

Although in theory users do not have to know the source code of the inherited class, in practice lots of programmers still seem reluctant to use something that they don’t know. On the other hand, when performance is a major concern, programmers may want to see source code of classes they are inheriting from, so that they can tune the code to meet their performance requirements.

A base class specifies commonality - - all classes derived from a base class inherit the capabilities and interfaces of that base class. In the object oriented design process, the designer looks for commonality and “factors it out” to form a base class. Derived classes are then customized upon the base class.

In a object oriented system, classes are often closely related. So the best way is to “factor out” common attributes and behaviors and place them in a base class, then use inheritance to form derived classes.

8.9 Partial Assignment

Partial assignment is: when using base-class pointer or reference to make assignment on derived-class objects, only the base-class part of the data is assigned. The reason for this is: because assignment operator can not be virtual, when using base-class reference to assign derived-class objects, only the base-class assignment operator is called.

class Base 
{
public:
  Base(int a) : _a(a) {}
 
  const Base & operator=(const Base & obj)
  {
    cout << "Base-class assignment operator called! \n";
    _a = obj. _a;
    return *this;
  }
 
  const int get() const { return _a; }
  virtual void print() const = 0;
 
private:
  int _a;
};
 
class Derived : public Base 
{
public:
   Derived(int a, int b) : Base(a), _b(b) {}
 
   const Derived & operator=(const Derived & obj)
   {
      cout << "Derived-class assignment operator called! \n";
      _b = obj. _b;
      Base::operator=(obj);
      return *this;
   }
 
   virtual void print() const
   { cout << "_a = " << Base::get() << ", _b = " << _b << endl; }
 
private:
   int _b;
};
 
int main()
{
   Base * ptr1 = new Derived(11, 111);
   Base * ptr2 = new Derived(22, 222);
   (*ptr1) = (*ptr2);
   cout << "Derived 1: ";
   ptr1->print();
   cout << "Derived 2: ";
   ptr2->print();
}

output will be:

Base-class assignment operator called!
Derived 1: _a = 22, _b = 111
Derived 2: _a = 22, _b = 222

There is no way to make assignment without partial assignment on derived-class objects with base-class reference or pointer. To forbit doing this, declare the base-class assignment operator protected, so that it can not be invoked. Then to conform to OCF this class should be abstract because an OCF must have a public assignment operator.
Even if the base class is an ABC, if assignment operator is not protected, partial assignment will still happen.
So generally speaking, to avoid partial assignment, always inherit from ABC and make its assignment operator protected.

8.10 Sequence of Constructor Call in Inheritance

Suppose a class inherits from a base class and contains a member object, when an object is created, the base-class constructor will be called first, then the composition class, then the derived class itself. For example:

 
class Base1 
{
public:
   Base1() {  cout << "Base1 Constructor !\n"; }
};
 
class Base2 
{
public:
   Base2() {  cout << "Base2 Constructor! \n"; }
};
 
class Derived : public Base1 
{
private:
   Base2 b1;
   int d;
public:
   Derived(int d1) : d(d1) 
   {  
     cout << "Derived Constructor! \n"; 
   }
};
 
int main()
{  
  Derived d1(33); 
}

Output will be:

Base1 Constructor!
Base2 Constructor!
Derived Constructor!

8.11 Default Constructor in Inheritance

When a base class have constructors but does not have a default constructor, and in the derived class there is no explicit call to base-class constructor, compiler will prompt error. But if the base class has no constructors at all, compiler will generate one implicitly. This is the same in Java.

Popularity: 8%

You need to log on to convert this article into PDF


Related Blog Items

No Comments

No comments yet.

Leave a comment

*
To prove you're a person (not a spam script), type the security word shown in the picture.
Anti-spam image