3.9. Defining a Derived Class

In general, a derived class needs to program only those aspects of its behavior that differ from or extend the behavior of its base class. NotQuery, for example, must provide an eval() definition that implements the not semantics. It must also introduce an instance member to store the query operand it is negating, and a property to allow get and set access to the operand, if appropriate. Both the AndQuery and OrQuery classes must support left and right operands. The NameQuery member must support a string member. All must provide definition of the abstract eval() virtual method.

The derived class is considered abstract if either it introduces an abstract member or it does not provide an implementation for an inherited abstract member. In either case it must explicitly be declared as an abstract class. The following example shows a BinaryQuery derived class. It is abstract because it does not provide an implementation of the abstract eval() method:

abstract public class BinaryQuery : Query
{
    protected BinaryQuery( Query leftOp, Query rightOp,
                           int [] solution )
        : base( solution )
        { m_lop = leftOp; m_rop = rightOp; }

    protected Query m_lop, m_rop;
    public Query LeftOp
         { get{ return m_lop; } set{ m_lop = value; }}

    public Query RightOp
         { get{ return m_rop; } set{ m_rop = value; }}
}

The use of the base keyword in the BinaryQuery constructor following the colon represents the invocation of the Query base-class constructor with solution as its argument. The base-class constructor is invoked before the body of the derived-class constructor is evaluated. This order of invocation guarantees that the inherited base-class members are initialized before execution of the body of the derived-class constructor within which they may be referenced.

C# does not permit the invocation list of the constructor to contain both a base and a this construct. If we need both, we must factor them into distinct constructor groups, such as in the following 3DPoint class:

public class 3DPoint :  2DPoint
{
   // cannot have base() and this() in one constructor
   public point3D( float x, float y, float z )
           : base( x, y ){ m_z = z; }

   public 3DPoint() : this( 0.0F, 0.0F, 0.0F ){}

   public 3DPoint( float x ) : this( x, 0.0F, 0.0F ){}
   public 3DPoint( float x, float y )
                : this( x, y, 0.0F ){}

      // ...
}

What about the AndQuery derived class? It inherits support for its two operands from its immediate abstract BinaryQuery class. It inherits support for the solution set from the Query base class from which BinaryQuery is derived. It seems that all it has to do is implement the inherited abstract eval() method. Well, almost.

There are two additional considerations. The first is the proverbial exception to the we-inherit-everything-from-the-base-class rule. Actually, a derived class does not inherit the constructor(s) of its base class. Even if all the derived-class constructor has to do is accept a set of parameters and pass them to its base-class constructor, it still has to be explicitly defined to do it. There is simply no inheritance of the base-class constructor(s).

The second consideration is that the derived class needs to define a constructor for each form of initialization it wishes to permit. For example, the AndQuery class allows an instance to be created only if it is passed either two operands of type Query, or two operands and an integer array:

public class AndQuery : BinaryQuery
{
    public AndQuery( Query leftOp, Query rightOp,
                     int [] solution )
        : base( leftOp, rightOp, solution ){}
    public AndQuery( Query leftOp, Query rightOp )
          : this( leftOp, rightOp, null ){}

    public virtual eval() { ... }
    // ... anything more ???
}

An attempt to create an AndQuery object with any other combination of arguments results in a compile-time error. This is true even for an attempt to create an AndQuery object with no arguments:

// OK: invokes the associated constructor
Query q1 = new AndQuery ( new NameQuery( "Cival" ),
                          new NameQuery( "War" ) );

Query q2 = new AndQuery ( q1, q1, q1.Solution );

// error: no constructor provided to support this
Query q3 = new AndQuery();

The no-argument constructor receives special treatment. If we provide no constructors for a class, the no-argument constructor is automatically supplied. Once we have defined one or more constructors, however, the no-argument constructor is no longer supplied automatically.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.213.238