Conversion Constructors

A constructor that can be called with a single argument is used for conversions from the type of the argument to the class type. Such a constructor is called a conversion constructor. Consider the following example:

class Point

{

public:

Point();

Point( int );

...

};

The preceding example declares two constructors: a default constructor that takes no arguments, and a constructor that converts from type int. In code that follows, any conversion from type int can use the Point( int ) constructor to convert from type int to type Point.

Sometimes a conversion is required but no conversion constructor exists in the class. These conversions cannot be performed by constructors. The compiler does not look for intermediate types through which to perform the conversion. For example, suppose a conversion exists from type Point to type Rect, and a conversion exists from type int to type Point. The compiler does not supply a conversion from type int to type Rect by constructing an intermediate object of type Point.

Conversions and Constants

While constants for built-in types such as int, long, and double can appear in expressions, no constants of class types are allowed (this is partly because classes usually describe an object complicated enough to make notation inconvenient). However, if conversion constructors from built-in types are supplied, constants of these built-in types can be used in expressions, and the conversions cause correct behavior. For example, a Money class can have conversions from types long and double:

class Money

{

public:

Money( long );

Money( double );

...

Money operator+( const Money& ); // Overloaded addition operator.

};

Therefore, expressions such as the following can specify constant values:

Money AccountBalance = 37.89;

Money NewBalance = AccountBalance + 14L;

The second example involves the use of an overloaded addition operator, which is covered in the next chapter. Both examples cause the compiler to convert the constants to type Money before using them in the expressions.

Drawbacks of Conversion Constructors

Because the compiler can select a conversion constructor implicitly, you relinquish control over what functions are called when. If it is essential to retain full control, do not declare any constructors that take a single argument; instead, define “helper” functions to perform conversions, as in the following example:

#include <stdio.h>

#include <stdlib.h>

// Declare Money class.

class Money

{

public:

Money();

// Define conversion functions that can only be called explicitly.

static Money Convert( char * ch ) { return Money( ch ); }

static Money Convert( double d ) { return Money( d ); };

void Print() { printf( "\n%f", _amount ); }

private:

Money( char *ch ) { _amount = atof( ch ); }

Money( double d ) { _amount = d; }

double _amount;

};

main()

{

// Perform a conversion from type char * to type Money.

Money Acct = Money::Convert( "57.29" );

Acct.Print();

// Perform a conversion from type double to type Money.

Acct = Money::Convert( 33.29 );

Acct.Print();

return 0;

}

In the preceding code, the conversion constructors are private and cannot be used in type conversions. However, they can be invoked explicitly by calling the Convert functions. Because the Convert functions are static, they are accessible without referencing a particular object.