Object
?
dynamic_cast
and other run-time checking.
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.
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.
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.
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 parametersThe 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(); // okWhile 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 // ... };
class DerivedName { public: const BaseInterface* BaseInterface() const; void AddBaseInterface(BaseInterface*); void RemoveBaseInterface(); protected: BaseInterface *base_; };
class BaseInterface { // ... }; class DerivedInterface1 : public BaseInterface { // ... }; class DerivedInterface2 : public BaseInterface { // ... };
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
A
) constructor within its direct derived
classes (B
and C
) are no longer
executed during the execution of their respective
constructors. It becomes the responsibility of the most
derived class (D
for D
object,
B
for B
object) to initialize
virtual base class. This is important as both direct
derived classes may invoke virtual base class constructor
in different arguments or even it is not suitable to their
subsequent derived class. Indirect derived class must call
virtual base class constructor, otherwise compiler error
if virtual base class does not have default constructor.
A::A(string className, bool b) : className_("A") { isInitialized_ = isInitialized; }; B::B(int i, bool b) : A("B", b) { // ... }; C::C(double d, bool b) : A("C", b) { // ... }; D::D(int i, double d, bool b) : A("D", b), B(i, b), C(d, b) { };
class B : public virtual A { public: B(int i, bool b); // when be the most derived class protected: B(int i); // when be intermediate derived class }; class C : public virtual A { public: C(double d, bool b); // when be the most derived class protected: C(double d); // when be intermediate derived class }; D::D(int i, double d, bool b) : A("D", b), B(i), C(d) { };
class A { // ... }; class B : public A { // ... }; class C { // ... }; class D : public virtual C { // ... }; class F : public B, public D, public virtual E { // ... };Virtual derivation constructor (and assignment operator under bitwise copy) sequence is:
C(); // D's virtual base class E(); // immediate virtual base class A(); // B's non-virtual base class B(); // immediate non-virtual base class D(); // immediate non-virtual base class F(); // most derived class
class A { public: void Run(void); void Walk(void); void Jump(void); }; class B : public virtual A { public: bool Run(int i) const; bool Walk(int i) const; }; class C : public virtual A { public: int Run(void); }; class D : public B, public C { public: void Jump(void); }; // usage in application D d; // error: ambiguous, B::Run(int) const or C::Run(void) or A::Run(void) d.Run(); // ok: B::Walk(int) const, but ambiguous for non-virtual derivation d.Walk(); d.Jump(); // ok: D::Jump(void)Under non-virtual derivation, each inherited instance is given equal weight in resolving the reference and so an unqualified reference results in a compile-time ambiguity error; under a virtual derivation, the inheritance of a virtual base class member is given less weight than a subsequently redefined instance of that member.
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