Saturday, November 21, 2009

Constructors as Member Functions




I l@ve RuBoard


Constructors as Member Functions


Class objects can be initialized implicitly, using a constructor. A constructor is a class member function, but its syntax is more rigid than it is for other member functions. It cannot have an arbitrary name; you should give the constructor function the same name as the class. The constructor interface cannot specify a return type, not even void. It cannot return values even if it contains a return statement.





class Cylinder {
double radius, height;
public:
Cylinder () // same name as class, no return type
{ radius=1.0; height=0.0; } // no return statement
�} ;



When client code creates an object, the default constructor is called.





Cylinder c1; // default constructor: no parameters



It is called a default constructor because it has no parameters. I know, this is a strange reason, but that's the way it is.



A constructor cannot be called explicitly at will, as can any other member function.





c1.Cylinder(); // syntax error: no explicit calls to constructors



The constructor can be called only when an object is created, not later. The compiler generates code that implicitly calls the constructor immediately after the instance of the object is created. This is why constructors are usually placed in the public section of the class specification. Otherwise, an attempt to create a class instance would generate an error as does any access to a private class member.



In general, an instance of the object can be created in the following ways:





  • at the beginning of the program (extern and static objects)





  • at entry into the scope with the object definition (automatic objects)





  • when a variable is passed to a function (or returned from a function) by value





  • when a variable is created dynamically using the operator new (but not malloc)





Now you see why constructors have no return values: Constructors are called implicitly by the code generated by the compiler, and there is nobody around to use this return value.



As member functions, constructors can have parameters; hence, constructors can be overloaded. If necessary, constructor parameters can have default values. When a class has more than one constructor, each one could be called when an object is created. Which constructor is called depends on the context, that is, on a set of arguments supplied by the client code at the time of object creation (the number and type of arguments).



Supplying constructors in the class means providing services to class clients: Client programmers do not have to call initializing functions explicitly anymore. However, they should worry about supplying the arguments for the constructor. Here is an example of a constructor with two parameters.





class Cylinder {
double radius, height; // initialized in constructors
public:
Cylinder(double r, double h); // member function prototype
void setCylinder(double r, double h);
. . . . . } ;
Cylinder::Cylinder(double r, double h) // scope operator
{ radius = r; height = h; }



Notice the name of the constructor implemented outside the class boundaries. The first Cylinder denotes the class to which the member function belongs. The second Cylinder denotes the name of the member function (the same as the name of the class). Constructors that have fewer than two parameters have special names. (You will see them shortly.) Constructors with two or more parameters do not have special names梩hey are just general constructors.



This constructor with two parameters does the same thing as setCylinder(): It sets the values of data members to the values of the arguments supplied by the client. The difference is that setCylinder() can be called many times for the same object instance in the client code. The constructor is called only once梬hen the object is created.



Here are a few examples of constructor invocations in client code. These different syntactic forms call the same general constructor with two parameters. Notice that an assignment operator in the second statement does not mean that an assignment operation is performed. Despite the appearance of using an assignment, this is not what you think (quite a common situation in C++. Remember that story about the crocodile?): There is no assignment there. It is just a different syntactic form for a constructor call.





Cylinder c1(3.0,5.0); // a constructor call for a named object
Cylinder c2 = Cylinder(3,5); // it is still a constructor call
Cylinder *r = new Cylinder(3.0,5.0); // unnamed object



Notice the syntax for a variable with arguments. This is a new syntax. One of the implicit ambitions of C++ language design is the uniform treatment of variables of built-in and programmer-defined types. For built-in types, we used the assignment operator for initialization. With the advent of programmer-defined types, you can use the syntax with arguments for variables of built-in types as well as class objects.





int x1(20); // same as int x1=20



When an object is allocated with a call to malloc(), no constructor is called. Hence, the client code has to initialize class objects explicitly.





Cylinder *p = (Cylinder*)malloc(sizeof(Cylinder)); // no constructor call
p->setCylinder(3,5); // object fields are assigned values



A call to malloc() is the only way in C++ to create an object without a constructor call. Creation of all other objects, named objects and dynamic objects, is followed by a call to a constructor. Without any fanfare, we crossed a point of no return. From now on, there will be no situation where you just create an object instance and give it a chunk of memory. Any creation of an object will be accompanied by a function call梐 call to a constructor. Again, this requires a change in thinking. Every time you see an object instance created (remember that story about the brick?), you should remind yourself: "OK, this means that a constructor is called. Which constructor?"







I l@ve RuBoard

No comments: