Inheritance

Why doesn't C++ have a universal class Object?

Constructor is not inherited. Each derived class must provide its own set of constructors. The derived class constructor then will call its base class constructor before start construct and initialize its data member. Thus base class cosntructor should be the first in the member initialization list of derived class constructor. The reasoning is that this could lead too easily to the introduction of uninitialized derived class data member errors. For example, imagine if subsequently add one or another non-class data member to derived class. The inherited base class constructor is no longer adequate for the initialization of the derived class. The error, however, reveals itself not in the construction of the derived class object, but in some subsequent use of the object. In practice, these sorts of errors prove difficult to trace. An overloaded instance of the base class new and delete operators are inherited and do sometimes result in just this sort of problem.

For derived class with pointer data members, remember to call base class copy constructor and assignment operator when overriding these two functions to handle memory allocation and deallocation.
DerivedName& DerivedName::DerivedName(const DerivedName& original)
  : BaseName(original) // ...
{
  // ...
}

DerivedName& DerivedName::operator=(const DerivedName& original)
{
  if (this == &original) return *this;

  BaseName::operator=(original); // call this->BaseName::operator=
  // some compilers (incorrectly) reject this call
  // if that assignment operator was generated by the compiler, so
  static_cast<Base&>(*this) = original;
  // ...

  return *this;
}

Destructor do not require such explicit call, but remember to declare the destructor virtual.


Friendship is not inherited. The derived class does not become a friend of a class that granted friendship to one of its base classes. If the derived class requires one or more of the same friendships, each must be granted explicitly by the respective class.


A common misunderstanding is while the derived class can access to protected members of its base class and other objects of its own class, it does not have access to the protected members of an independent base class object.
bool NameQuery::compare(const Query* pquery)
{
  int myMatches = loc_.size(); // ok: protected member of its Query subobject
  int itsMatches = pquery->loc_.size(); // error: no direct access rights
  return myMatches == itsMatches;
}

The immediate solution might be providing public location() data-extraction function or declare the derived class explicitly to be a friend of base class (can access private members too). The real problem, however, is the incorrect design. Because loc_ is a member of the Query base class, compare() properly belongs as a member of the Query class, not to the derived NameQuery class. Often, member access problems between a derived and base class can be resolved by moving the operation to the class that contains the inaccessible member, as in this case.


In C++, there is no overloading across scope.
class A {
public:
  void Run(int i, int j) { i = j; }
};

class B : public A {
public:
  void Run(int i) { i = 1; }
};

// usage in application
B b;
b.Run(1);
b.Run(1, 2); // compile error: Add(int) does not takes 2 parameters
The solution is using declaration or using directive.
class A {
public:
  void Run(int i, int j) { i = j; }
};

class B : public A {
public:
  using A::Run;	// make every Run from A available
  void Run(int i) { i = 1; }
};

// usage in application
B b;
b.Run(1);
b.Run(1, 2); // ok

Under multiple inheritance, base class constructor execution based on order of declaration, but the search of member overload resolution (member function, data member and nested type) is simultaneous examination of each base class inheritance subtree. If a declaration is found in only one base class subtree, the identifier is resolved and the lookup algorithm concludes; if a declaration is found in two or more base class subtrees, the reference is ambiguous and a compile-time error message is generated.

A program-level solution is to qualify explicitly the instance wish to invoke using the class scope operator.
class A {
public:
  void Run(void);
};
class B {
public:
  bool Run(int i) const;
};
class C : public A, public B {
public:
  C(void); // call A(), then call B() and C()
};

// usage in application
C c;
c.Run(); // error: ambiguous, A::Run() or B::Run(int) const
c.A::Run(); // ok
While this solves the problem, in general it is not a satisfactory solution. The reason is that the user has now been placed in the position of having to decide what the right behavior is for the Panda class. This burden of responsibility should never be placed on the user of a class. Rather, the class itself need to resolve any ambiguities intrinsic to its inheritance hierarchy. The simplest way to do this is to define a named instance within the derived class that provides the desired behavior.
class C : public A, public B {
public:
  inline void Run(void) const { A::Run(); }
};

A public derivation is referred to as type inheritance. The derived class is a subtype of the base class; it overrides the implementation of all type-specific member functions of the base class while inheriting those that are shared. The derived class in general reflects an is-a relationship. A subtype can be substituted transparently anywhere the program expects its public base type.

A private derivation is referred to as implementation inheritance. The derived class does not support the public interface of the base class directly; rather, it wishes to reuse the implementation of the base class while providing its own public interface. This is a has-a relationship to its base class. In general it is best supported by composition rather than by inheritance.

For private inheritance, can use using declaration to exempt individual members of a base class from hidden. Another reason to exempt individual members is to allow subsequent derivations access to the protected members of the private base class by putting using declaration into base class.

class DerivedName : private BaseName {
public:
  using BaseName::Public; // maintain public access level
protected:
  using BaseName::protected_; // derived class of DerievdName can access
};
The derived class can only restore the inherited member to its original access level. The access level cannot be made either more or less restrictive than the level originally specified within the base class.

A common pattern of multiple inheritance is to inherit the public interface of one class and the private implementation of a second class.
template<class item, class container>
class Unbounded_Queue:
  private Simple_List<item>, // implementation
  public Queue<item> {       // interface
  // ...
};

A very broad guideline of using composition vs private inheritance in a class design in which a has-a relationship exists: Composition by value vs by reference:
By default, inheritance in C++ is a specialized form of composition by value. Under single inheritance, this specialized form of composition by value supported by inheritance provides the most efficient and compact object representation. It becomes a problem only under multiple inheritance when a base class occurs multiple times within the derivation hierarchy. Under virtual inheritance, only a single, shared base class subobject is inherited regardless of how many times the base class occurs within the derivation hierarchy. The shared base class subobject is called a virtual base class. The downside is performance impact and added complexity of subsequent class derivations.
class A {
  // ...
};
class B : public virtual A {
  // ...
};
class C : virtual public A { // order is not significant
  // ...
};
class D : public B, public C {
  // ...
};
In a virtual derivation

Virtual inheritance provides for composition by reference. That is, access of the subobject and its non-static members is carried out by indirection. This provides the necessary flexibility to combine the multiply inherited virtual base class subobjects into a single shared instance within the derived class.

In practice, in nearly all successful uses of virtual inheritance, the entire hierarchical subtree requiring virtual inheritance is designed at one time by either the same individual or the project design group. In general, unless virtual inheritance provides a solution to an immediate design problem, it is recommended against its use.

Index