2.7. The Class Constructor

Our WordCount program requires that the user provide a file name. Optionally, the user can turn on several options, such as generating trace output and timing diagnostics. The Main() entry point processes the command-line options. That done, it must create a WordCount object and pass the file name and possible options to that object before asking the object to process the file.

One solution, of course, is to create a WordCount object with the default values and then reset the values the user passed in through the command-line options. The problem with this strategy is that it requires two steps, and the crucial second one is something that the user might overlook.

The class constructor is a mechanism by which the user can provide values to assign to data members during construction of the class object. For example, here is how we would like to create a WordCount object:

WordCount theObj = new WordCount( file_name, spyOn, traceOn );
theObj.processFile();

where file_name, spyOn, and traceOn are set during the processing of the command-line options.

The constructor associated with this invocation looks like this:

public WordCount( string file_name, bool spy, traceFlags trace )
{
      m_file_name = file_name;
      m_spy = spy;
      m_trace = trace;

      if ( m_spy )
           m_times = new ArrayList();
}

A constructor is a special member function of the class. We identify it as a constructor by giving it the same name as its class. It cannot return a value, nor can it declare a return type—not even a return type of void.

When the user creates a WordCount object through the operator new, the process is broken down into two steps. In the first step, new allocates the heap memory to contain the object. In the second step, the constructor initializes the object. The constructor is automatically invoked by the compiler.

If the class provides only one constructor definition, all invocations of operator new must provide the correct number and type of arguments to pass to that constructor. For the WordCount class, we must now always supply three arguments when invoking operator new. In particular, we can no longer create a class object with no arguments. The following invocation, for example, results in a compile-time error:

WordCount theObj = new WordCount(); // error

because the only constructor we've provided expects three arguments.

One of the decisions we must make as class designers is whether to provide constructors and, if so, how many to provide. That is, how many ways do we wish to support constructing an object of our class?

We can define multiple constructors, provided that the parameter list of each constructor is unique in either the number or type of its parameters. (When we provide multiple instances of a function with the same name, we say that we have overloaded the function.) For example, a second WordCount constructor requires only that the user provide a file name:

public WordCount( string file_name )
   : this( file_name, false, traceFlags.turnOff ) {}

A class constructor can invoke a second constructor of its class. The syntax requires three elements:

  1. A colon (:) following the signature of the constructor, which alerts both the compiler and reader of the code that there will be an additional constructor invocation.

  2. The this keyword, which alerts both the compiler and the reader of the code that the constructor being invoked is a member of this class.

  3. The arguments to be passed to the other constructor, whose type and number determine exactly which other constructor is invoked.

The constructor represented by the this keyword is invoked first. In our case, this is the three-parameter constructor. When this constructor terminates, the body of the original constructor is invoked. In our case, there is nothing for it to do. This is why we've provided an empty constructor body.

The following Point3D class is another example of the dispatch-to-another-instance constructor idiom. Its purpose is to allow the user to create a Point3D object with three, two, one, or no initial values. Each absent coordinate value is assigned a default value of 0:

Point3D origin = new Point3D();      // Point3D(0,0,0)
Point3D x_offset = new Point3D(1.0)      // Point3D(1.0,0,0)
Point3D translate = new Point3D(1.0,1.0);   // Point3D(1.0,1.0,0)
Point3D mumble = new Point3D(1.0,1.0,1.0);

The Point3D constructor set to support this would look like this. (Note that the declaration order is not significant.)

class Point3D
{
   public Point3D( double v1, double v2, double v3 )
                { x = v1; y = v2; z = v3; }

   public Point3D(double v1,double v2): this(v1,v2,0.0) {}
   public Point3D( double v1) : this( v1, 0.0, 0.0 ){}
   public Point3D() : this( 0.0, 0.0, 0.0 ){}

   // ...
}

It is possible to declare a nonpublic constructor —that is, as either private or protected. A nonpublic constructor is unavailable to users of the class. However, it can be invoked within the class member functions, allowing the class to create specialized objects for internal use.

C# provides the class designer with three initialization options, only one of which requires the introduction of a class constructor. A side effect of the new expression is the automatic initialization of each data member to its type's default value. We never need to explicitly set a data member to its default value.

If a value other than the default value needs to be set, we have two choices for how to set it. If the initialization is based on user input, we'll need to solicit that input through a constructor. Otherwise we can specify it as part of the member declaration.

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

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