12.9 USER-DEFINED CONVERSIONS


12.9 USER-DEFINED CONVERSIONS

Chapter 6 talked about implicit type conversions for the primitive types in C++. As mentioned there, implicit type conversions are applied automatically by the compiler.

It is possible to give the compiler the freedom to carry out implicit type conversions for class-type objects also. This is done by embedding in the class definition a member function with specially designated syntax.

To illustrate this syntax, consider the classes Point3D and Point2D in the example code below, the former for designated a point in 3D xyz space, and the latter a point in the xy plane of the 3D space. It is not difficult to imagine an application which could require a 2D point to be processed as a 3D point for some geometrical calculation. To make that happen automatically, in the code shown below we endow the class Point2Dwith a special member function in line (A). The prototype of this member function is:

      operator Point3D() 

Its implementation shown below is presented in line (B) of the program:

      operator Point3D(){ return Point3D(x, y, O); } 

That's all we have to do to enable the compiler to automatically convert a Point2D object into a Point3D object.

To demonstrate the auto-conversion of a Point2D object into a Point3D object, in main we create a Point2D object point2D in line (C). To print out this object, we call in line (D);

      cout << point2D << endl; 

But note that we have not overloaded the output operator for the class Point2D; it is overloaded for only the Point3D class. However, the compiler does not complain. It silently converts our Point2D object into a Point3D object and prints that out.

 
//UserConv.cc #include <iostream> using namespace std; class Point3D; class Point2D { int x; int y; public: Point2D(int p, int q) : x(p), y(q) {} operator Point3D(); //(A) }; class Point3D { int x; int y; int z; public: Point3D(int p, int q, int r) : x(p), y(q), z(r) {} friend ostream& operator<<(ostream& os, const Point3D& point); }; ostream& operator<<(ostream& os, const Point3D& point) os << "(" << point. x <<", " << point. y << ", " << point. z << ")"; return os; } inline Point2D:: operator Point3D()-{ return Point3D(x, y, O); } //(B) int main() { Point2D point2D(3, 4); //(C) cout << point2D << endl; //(D) // (3, 4, 0) using Point3Dapos;s operator<< return 0; }

To summarize, a user-defined conversion function takes the general form

      operator type(); 

where ‘type’ is replaced by a built-in type, a class type, or a typedef name. Also, a conversion function must be a member function. Its declaration must not specify a return type. Nor can a parameter list be specified.

Unless prevented from doing so by the explicit keyword, a compiler will also try to carry out implicit type conversion of class-type objects by using a one-argument constructor if such a constructor is available. In the following version of the above example, we do NOT provide the class Point2D with a type conversion operator. Instead, in line (E) we have equipped the class Point3D with a 1-arg constructor that takes a Point2D argument. In main, we construct an object of type Point2D and ask the output operator to print it out. The compiler has no problem with this. The compiler notices that it is allowed to construct a Point3D object from a Point2D object and that the former carries an overload definition for operator<<. So the compiler goes ahead and makes use of the 1-arg constructor of Point3D to do the job in main.

 
//UserConvConstructor.cc #include <iostream> using namespace std; class Point3D; class Point2D { int x; int y; public: Point2D( int p, int q ) : x(p), y(q) {} friend Point3D; }; class Point3D { int x; int y; int z; public: Point3D( int p, int q, int r ) : x(p), y(q), z(r) {} Point3D( Point2D point2D ) : //(E) x( point2D.x ), y( point2D.y ), z( 0 ) {} friend ostream& operator<<( ostream& os, const Point3D& point ); }; ostream& operator<<( ostream& os, const Point3D& point ) { os << "(" << point.x <<", " << point.y << ", " << point.z << ")"; return os; } int main() { Point2D point2D( 3, 4 ); cout << point2D << endl; // (3, 4, 0) using Point3Dapos;s 1-arg constructor return 0; }

If a class is equipped with a 1-arg constructor, the fact that the compiler has the license to use it for implicit type conversion can sometimes have unintended and dangerous consequences. What if you did not want a Point2D to be converted into a Point3D without your permission? What if you were involved in calculations concerning 2D points not in the xy plane, but in some other plane of the 3D space? Now the particular conversion embodied in the 1-arg constructor of Point3D would be inappropriate. Fortunately, C++ gives us a mechanism to prevent such unauthorized type conversions by using the keyword explicit, as in line (F) of the example below. If the header of a 1-arg constructor is preceded by explicit, it can be used for type conversion only with the help of explicitly invoked cast operator, as we show in line (G) of the program below.

 
//UserConvExplicit.cc #include <iostream> using namespace std; class Point3D; class Point2D { int x; int y; public: Point2D( int p, int q ) : x(p), y(q) {} friend Point3D; }; class Point3D { int x; int y; int z; public: Point3D( int p, int q, int r ) : x(p), y(q), z(r) {} explicit Point3D( Point2D point2D ) //(F) : x( point2D.x ), y( point2D.y ), z( 0 ) {} friend ostream& operator<<(ostream& os, const Point3D& point ); }; ostream& operator<<(ostream& os, const Point3D& point ) { os << "(" << point.x <<", " << point.y << ", " << point.z << ")"; return os; } int main() { Point2D point2D( 3, 4 ); // cout << point2D << endl; // will not work now // note explicit cast: cout << static_cast<Point3D>( point2D ); //(G) return 0; }

As stated earlier, implicit type conversions can be dangerous, since the compiler could invoke them when that was the last thing you wanted to see happen. Such conversions can therefore result in difficult to locate problems in large and complex programs. The solution obviously lies in not including type conversion operators in a class and declaring 1-arg constructors where the class appears as an argument as explicit.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net