The first of these statements converts poppins weight to a double value, and then assignment converts the double value to type long. Similarly, the second statement converts poppins first to type int and then to long.

Like conversion constructors, conversion functions can be a mixed blessing. The problem with providing functions that make automatic, implicit conversions is that they may make conversions when you don’t expect them. Suppose, for example, that you happen to write the following code when you’re sleep deprived:

int ar[20];

...

Stonewt temp(14, 4);

...

int Temp = 1;

...

cout << ar[temp] << "!\n";  // used temp instead of Temp

Normally, you’d expect the compiler to catch a blunder such as using an object instead of an integer as an array index. But the Stonewt class defines an operator int(), so the Stonewt object temp is converted to the int 200 and be used as an array index. The moral is that often it’s best to use explicit conversions and exclude the possibility of implicit conversions. In C++98, the keyword explicit doesn’t work with conversion functions, but C++11 removes that limitation. So with C++11, you can declare a conversion operator as explicit:

class Stonewt

{

...

// conversion functions

    explicit operator int() const;

    explicit operator double() const;

};

With these declarations in place, you would use a type cast to invoke the operators.

Another approach is to replace a conversion function with a nonconversion function that does the same task—but only if called explicitly. That is, you can replace

Stonewt::operator int() { return int (pounds + 0.5); }

with

int Stonewt::Stone_to_Int() { return int (pounds + 0.5); }

This disallows the following:

int plb = poppins;

But if you really need a conversion, it allows the following:

int plb = poppins.Stone_to_Int();

Caution

You should use implicit conversion functions with care. Often a function that can only be invoked explicitly is the best choice.

In summary, then, C++ provides the following type conversions for classes:

• A class constructor that has but a single argument serves as an instruction for converting a value of the argument type to the class type. For example, the Stonewt class constructor with a type int argument is invoked automatically when you assign a type int value to a Stonewt object. However, using explicit in the constructor declaration eliminates implicit conversions and allows only explicit conversions.

• A special class member operator function called a conversion function serves as an instruction for converting a class object to some other type. The conversion function is a class member, has no declared return type, has no arguments, and is called operator typeName(), where typeName is the type to which the object is to be converted. This conversion function is invoked automatically when you assign a class object to a variable of that type or use the type cast operator to that type.

Conversions and Friends

Let’s bring addition to the Stonewt class. As mentioned in the discussion of the Time class, you can use either a member function or a friend function to overload addition. (To simplify matters, assume that no conversion functions of the operator double() form are defined.) You can implement addition with the following member function:

Stonewt Stonewt::operator+(const Stonewt & st) const

{

    double pds = pounds + st.pounds;

    Stonewt sum(pds);

    return sum;

}

Or you can implement addition as a friend function this way:

Stonewt operator+(const Stonewt & st1, const Stonewt & st2)

{

    double pds = st1.pounds + st2.pounds;

    Stonewt sum(pds);

    return sum;

}

Remember, you can provide the method definition or the friend definition but not both. Either form lets you do the following:

Stonewt jennySt(9, 12);

Stonewt bennySt(12, 8);

Stonewt total;

total = jennySt + bennySt;

Also given the Stonewt(double) constructor, each form lets you do the following:

Stonewt jennySt(9, 12);

double kennyD = 176.0;

Stonewt total;

total = jennySt + kennyD;

But only the friend function lets you do this:

Stonewt jennySt(9, 12);

double pennyD = 146.0;

Stonewt total;

total = pennyD + jennySt;

To see why, you can translate each addition into the corresponding function calls. First,

total = jennySt + bennySt;

becomes

total = jennySt.operator+(bennySt);   // member function

or else

total = operator+(jennySt, bennySt);  // friend function

In either case, the actual argument types match the formal arguments. Also the member function is invoked, as required, by a Stonewt object.

Next,

total = jennySt + kennyD;

becomes

total = jennySt.operator+(kennyD);   // member function

or else

total = operator+(jennySt, kennyD);  // friend function

Again, the member function is invoked, as required, by a Stonewt object. This time, in each case, one argument (kennyD) is type double, which invokes the Stonewt(double) constructor to convert the argument to a Stonewt object.

Перейти на страницу:

Все книги серии Developer's Library

Похожие книги