Objects Can Initialize Themselves

Up until now we've been a little loose with terminology. Often we've said that both this:

    double x = 0.0;

and this:

    double x;
    x = 0.0;

and even this:

    double x;
    cin >> x;

were initialization. However, only the first is truly initialization. The second form is assignment, of course. And the third form is input. All three do place a new value into the variable's memory location. However, initialization is often restricted to be a process that takes place at the time a variable is first declared -- not later on in the program.

So, how do objects -- variables of a class type -- initialize themselves? There are three (3) basic ways classes provide initialization. All three are termed constructors -- since they are used during the construction of a new memory location for an object. To tell you the truth, the built-in types had 3 kinds of initialization, too. Let's look at the two in parallel:

Built-Inclassinitialization style name
    double x;
    Point a;
default
    double x = 4.2;
    Point a(4.2,-2.3);
initialization

(or parameterized)

    double x;
    double y = x;
    Point a;
    Point b = a;
copy

Interesting. In the default construction case, the programmer declaring the variable provides no indication about what value(s) should be placed into the memory location. We all know that the built-in default behavior is to leave whatever random bits were there there and interpret them as the data type of the variable using that memory space. What do classes do? Unless you tell them otherwise, they'll fill everything in with whatever bit pattern represents a 0 value for the member data types (false for bool or '\0' for char). But the nice thing is, you can teach the compiler to do it differently! You can write a class method to show it how you'd like the class objects to be filled in by default -- when the programmer doesn't specify any initial values.

Then there is the initialization construction case. Here the programmer declaring the variable has value(s) in mind from the get-go and tells the compiler so. With a built-in type, the proper bit pattern to represent the desired value is stored into the memory location. With a class type, the compiler cannot act alone. It doesn't know what value to store in what member variable. So, if you want your class to support initialization construction, you will have to write an initialization constructor for it. The one for the Point class used above takes two arguments because the Point class has two member variables. Inside the method definition you would specify which object member variable should be filled in with which argument.

Finally we have the copy construction case. Here the programmer already has a variable of the same type as is being created and wants the new variable to be a copy of the old variable -- to contain the same value. For built-in types, the compiler simply copies all the bits from the currently existing location to the new memory location. For a class type, the compiler will do the same thing -- automatically. However, although this will work fine for the rest of this semester and even through a few weeks of next semester, it won't work forever. When we get to the topic of dynamic memory this bit-wise copy will begin to cause problems -- often with catastrophic results. So, we'll be looking at how to write our own copy constructors now so you'll be masterful of it by the time they really become necessary.

By The Way (BTW), the copy constructor is probably the most used if not the most important constructor of the three. Above we showed an example of it being used to declare a new variable initialized with an old variable. It is also used in two other situations. Whenever you pass an argument to a function with the value mechanism (pass-by-value), the copy constructor is called to construct the formal argument as a copy of the actual argument. Whenever you use the return mechanism to return a value to the caller, the copy constructor is called to construct the returned value from whatever expression is on the return statement. Thinking back to some of the functions we've written, we've used a lot of copy construction already!

What's a Constructor Look Like?

Constructors look really funny. Odd. Strange. Not like other functions. Maybe you should just see for yourself. Here are the constructor prototypes for the Point class:

    class Point
    {
        double x, y;
    public:
        Point(void);
        Point(double new_x, double new_y);
        Point(const Point & p);
        // ...other stuff as before...
    };

First note that all of them have the same name: Point. Constructors are always named after the class they are constructing. Because they all are forced to have the same name, they must have different overload signatures -- or else we could only construct objects in a single way! (By the way, this is another reason a function's overload signature can't include the return type -- some special functions like constructors have no return type to speak of...)

Second you'll notice that there is no return type. There are a couple of theories on this. Some will tell you that it is because constructors are called from variable declarations and that, since variable declarations are the only statements in all of C++ that don't return a value, neither need the constructors return a value. Others will say that constructors automatically return the object they are creating. Which theory is correct will become evident by the end of these notes.

Okay, they prototype weird, but what about their definitions? Yes, those are weird, too:

    Point::Point(void)
    {
        x = y = 0.0;
    }

    Point::Point(double new_x, double new_y)
    {
        set_x(new_x);
        set_y(new_y);
    }

    Point::Point(const Point & p)
    {
        x = p.x;
        y = p.y;
    }

Again note the lack of any return type. As class methods we still define them with the Point:: in front, of course. But also notice that none has a return statement. I've often preached about placing a return statement inside ALL functions -- even those with return type void. But constructors are not allowed to have return statements since they have no return type (per-se). Yet another tribute to how special constructors really are.

But let's look at the function bodies to see how objects are typically constructed. In the default constructor we've filled in both x and y with the value 0.0. This means that a default constructed Point object will represent the origin. This seems a perfectly sound place for any Point to start out and that's the point...er...main idea...yeah. The default constructor will typically do one of two things: fill in the member variables with sane and reasonable values for what they represent OR fill in the member variables with an obviously non-sense value so that default constructed objects will stand out in some way from properly initialized objects. Either can be useful in practice, but the latter (non-sense values) can be tricky to manage sometimes so I'll mostly stick with the former (reasonable values).

The initialization constructor accepts the new values via the argument list. Since these values came from outside the class, they are not to be trusted and so we call the mutators to perform any requisite error checking/validation. (In a Point class there is none, but it is still a good habit to get into!)

So, if calling the mutators is such a good habit, why didn't we do it from the default constructor? Well, there we the class designer/programmer chose values we knew were either reasonable or purposefully non-sensical. They originate from inside the class and so we trust them. (Also, if using the non-sense values approach, the mutators would correct the values we were trying to store!)

Finally, the copy constructor simply assigns the values of the Point argument's member variables into the member variables of the object being constructed. Again, the values inside the p object are currently inside a Point object and so must have been set/mutated at some time and so are fine to trust.

But what's up with that const reference argument to the copy constructor? Well, the const is there just to be polite and tell the caller -- whatever is trying to construct this new object as a copy of the other object -- that we won't change (even accidentally) the other object. The reference is absolutely required, however. To see why we need to look back at the three times a copy constructor is called:

The first and third don't apply here since they don't deal with function arguments. That leaves the middle one which occurs during any pass-by-value operation. If the copy constructor's argument weren't a reference, it would indeed be by-value. So, if we had a copy constructor with a value argument, this is what would happen:

 +->while calling the copy constructor for reason X, the actual argument
 |      needs to be copied to the formal argument so we call the
 |      copy constructor-----+
 |                           |
 +---------------------------+

Leading to an infinite loop of function calls. (Of course, it won't really be infinite since each function call uses more memory in the computer and so you'll eventually crash. On my machine it was after about 13008 calls. *grin*)

With the reference in there, however, this doesn't occur because references aren't copies and so don't call the copy constructor!

But You Said, "Small Functions --> inline!"

Indeed. And the Point class is full of tiny little functions which really should be inline'd for efficiency (a.k.a. speed). But these functions are inside a class! That's gotta make something different. And indeed it does. Making a class method is actually easier than inline'ing a normal function. First, the inline keyword is not required. Second, you simply define the function inside the class definition -- in place of its prototype. Let's look at inline'ing the default constructor since it was only a single line:

    class Point
    {
        double x, y;
    public:
        Point(void) { x = y = 0.0; }
        Point(double new_x, double new_y);
        Point(const Point & p);
        // ...other stuff as before...
    };

Notice how it is so small that I've defined it on the same line as the function's head! (Historical note: many people thought that this is where 'inline' got its name -- that the function definition was 'in line' with the function head. Perhaps we should ask Bjarne for some clarification? *smile*)

The compiler will basically say, "If this programmer is willing to 'clutter' up their class definition with a function definition, they must think it is small and fast. So, I'll try to inline it!" But, again, don't just define all your class' methods inline -- apply it sparingly to functions that really deserve it. The more inline functions there are, the longer it will take to compile your program!

The Point class actually has many good candidates for inline'ing. I'll let you inline the accessors, mutators, and other constructors yourself. But here's an inline for the distance function:

    class Point
    {
        double x, y;
    public:
        Point(void) { x = y = 0.0; }
        Point(double new_x, double new_y);
        Point(const Point & p);
        double distance(Point p2)
            { return sqrt(pow(x-p2.x,2.0)+pow(y-p2.y,2.0)); }
        // ...other stuff as before...
    };

Note that in this style the function is longer and so is placed -- still on a single line -- but indented so you know it is the body that goes with the head on the previous line.

Speaking of Data Security

Well, not just now, but the other day we talked of how the private and public sections of a class (in conjuction with the accessors and mutators) gave us primitive data security. Well, I've been thinking about how the calling object to a class method is always referenced. That's kinda a security hole. If a function were to accidentally alter a member variable when it didn't mean to (like 'x = 4' instead of 'x == 4' or 'x=4' instead of 'x+4'), the data would simply be changed with no-one the wiser (no one would know about it). This kind of simple mistake could be easily caught if we could make the calling object a const reference instead of just a reference. But how? Let's experiment. First what function shouldn't be changing the calling object's data? How about the print() method? Okay. Here's the print() method's prototype:

    class Point
    {
        double x, y;
    public:
        void print(void);
        // ...other stuff as before...
    };

Where can we place a const keyword that might indicate that the calling object is to be constant? We could place it before the return type:

    const void print(void);

But this actually means that the function will return a constant void (or whatever the return type is). Not what we wanted -- and illegal in the case of the void return. How about before the function name:

    void const print(void);

This, oddly enough, means the same thing. The compiler will treat 'const type x' and 'type const x' as both meaning that x is a constant of the specified type. *snaps fingers* Darn. Okay, moving on we can try to put it after the function name:

    void print const (void);

Well, this wouldn't be bad, but I guess all the people on the standards committee thought it just looked too weird. So we move on. If we place it inside the argument list, it would apply to some argument or other -- no good. Only one place left -- after the argument list:

    void print(void) const;

This is indeed where the standard says to place the const keyword to tell the compiler (and any callers) that the calling object will be treated as a constant during the method's execution.

What should be a const method? Any method that doesn't or doesn't need to change the contents of the calling object should be made const. This adds extra safety to your class' data management. Typically this means accessors, printing functions, and many specialty and utility functions (like midpoint and distance). Mutators, constructors, and input functions are never const -- their very job is to alter the calling object's data!

In addition to the normal properties of making function arguments const (the calling object being like an implicit argument, after all) -- caller knows it won't change and compiler enforces that it isn't changed in the definition -- there is another implication with this use of const. If an object isn't a variable but a constant, the only methods you are allowed to call with it are ones declared const. When do you make constant objects? Well, you don't make constant objects per-se, but you make objects act constant during function calls -- like the const reference passed to the copy constructor.

In fact, many programmers will use const reference arguments for objects instead of pass-by-value. Their reasons are twofold. First, it has the same effect as a value argument with extra speed (because it doesn't call the copy constructor). Second, objects tend to have many data members and it would be terribly wasteful in terms of memory to continue making copies of them with value arguments.

Yet Another Hurrah!

There is one last thing to look at here. In light of our new knowledge of constructors, inline'ing, and const-ness, let's look at our (arguably) most complex function so far: midpoint().

    Point2D Point2D::midpoint(Point2D p2) const
    {
        Point2D mid;
        mid.set_x((x+p2.x)/2);
        mid.set_y((y+p2.y)/2);
        return mid;
    }

(Note how I've made the function const since it shouldn't -- and doesn't -- change the calling object.)

What is happening when this function executes? First, we copy construct the formal argument p2 from whatever actual argument is provided. Then we default construct a local variable mid. Next we call both mutators to change the values in that local variable which was just filled in with default values -- that seems wasteful. Finally we copy construct the function's return value from the return statement expression: mid.

Whew! That's a lot of stuff going on:

Is there anything we can do about this? Well, let's start at the beginning: the formal argument. To avoid the copy construction here we can use a reference argument. We'll make it a const reference to be polite to our caller:

    Point2D Point2D::midpoint(const Point2D & p2) const
    {
        Point2D mid;
        mid.set_x((x+p2.x)/2);
        mid.set_y((y+p2.y)/2);
        return mid;
    }

Next we are default constructing the local variable and then changing those default values right away. Hmm...shouldn't there be a way to construct the variable with the values we want in it from the start? Construct an object with values we specify... Construct an object with initial values given by the programmer... Hey! Maybe a constructor can help? Well, there's the default constructor...but that doesn't take any values from the programmer. Then there's the copy constructors, but that needs an existing object -- which we don't have. Oh, yeah! The initialization constructor takes initial values specified by the programmer! Let's try it out:

    Point2D Point2D::midpoint(const Point2D & p2) const
    {
        Point2D mid((x+p2.x)/2, (y+p2.y)/2);
        return mid;
    }

Wow! That's much shorter and even a bit cleaner. How's the list now?

Interesting. The first is completely gone. The second has been transformed. The two mutations are now hidden inside the initialization constructor -- but at least we don't store one thing and then immediately change it to another! Just one left -- perhaps... Let's look at that again -- in more general terms:

    ret_type f(args)
    {
        ret_type local = expression;
        return local;
    }

I've seen that before. In fact, we worked hard to get rid of it. Remember when we first wrote functions we'd do silly things like:

    ret_type f(args)
    {
        ret_type local;
        local = expression;
        return local;
    }

Which we then shortened to:

    ret_type f(args)
    {
        return expression;
    }

This saved on local variable space and programmer and user time as well as made the function more inline-able. But that was with simple, built-in data typed expressions. Can we do that with a class type? Almost. Here's the solution:

    Point2D Point2D::midpoint(const Point2D & p2) const
    {
        return Point2D((x+p2.x)/2, (y+p2.y)/2);
    }

Notice how we can't simply return a comma-separated list of values for the compiler to initialize the returned object with, but we can explicitly call the constructor ourselves to create the object we want to return! In fact, when the compiler sees such a return expression, it can generate more efficient code so that it won't have to re-copy the return expression into the return value. Now our list is this:

That's nice and small. We call one function which calls two others. All three of those are inline and so should be as quick as possible! On top of that, we are so small we should probably be inlined, too! Let's see that:

    class Point
    {
        double x, y;
    public:
        Point2D midpoint(const Point2D & p2) const
            { return Point2D((x+p2.x)/2, (y+p2.y)/2); }
        // ...other stuff as before...
    };

(This is also our first (seen) const inline class method -- just to prove that you can indeed combine the two.)

BTW, this way of coding -- with an explicit constructor call on the return statement -- is called (at least informally) a constructor return.