In Section 11.8 and Section 11.9, we discussed that any single-argument constructor can be used by the compiler to perform an implicit conversionthe type received by the constructor is converted to an object of the class in which the constructor is defined. The conversion is automatic and the programmer need not use a cast operator. In some situations, implicit conversions are undesirable or error-prone. For example, our Array class in Fig. 11.6 defines a constructor that takes a single int argument. The intent of this constructor is to create an Array object containing the number of elements specified by the int argument. However, this constructor can be misused by the compiler to perform an implicit conversion.
Common Programming Error 11.9
Unfortunately, the compiler might use implicit conversions in cases that you do not expect, resulting in ambiguous expressions that generate compilation errors or resulting in execution-time logic errors. |
Accidentally Using a Single-Argument Constructor as a Conversion Constructor
The program (Fig. 11.16) uses the Array class of Figs. 11.611.7 to demonstrate an improper implicit conversion.
Figure 11.16. Single-argument constructors and implicit conversions.
1 // Fig. 11.16: Fig11_16.cpp 2 // Driver for simple class Array. 3 #include 4 using std::cout; 5 using std::endl; 6 7 #include "Array.h" 8 9 void outputArray( const Array & ); // prototype 10 11 int main() 12 { 13 Array integers1( 7 ); // 7-element array 14 outputArray( integers1 ); // output Array integers1 15 outputArray( 3 ); // convert 3 to an Array and output Array's contents 16 return 0; 17 } // end main 18 19 // print Array contents 20 void outputArray( const Array &arrayToOutput ) 21 { 22 cout << "The Array received has " << arrayToOutput.getSize() 23 << " elements. The contents are: " << arrayToOutput << endl; 24 } // end outputArray
|
Line 13 in main instantiates Array object integers1 and calls the single argument constructor with the int value 7 to specify the number of elements in the Array. Recall from Fig. 11.7 that the Array constructor that receives an int argument initializes all the array elements to 0. Line 14 calls function outputArray (defined in lines 2024), which receives as its argument a const Array & to an Array. The function outputs the number of elements in its Array argument and the contents of the Array. In this case, the size of the Array is 7, so seven 0s are output.
Line 15 calls function outputArray with the int value 3 as an argument. However, this program does not contain a function called outputArray that takes an int argument. So, the compiler determines whether class Array provides a conversion constructor that can convert an int into an Array. Since any constructor that receives a single argument is considered to be a conversion constructor, the compiler assumes the Array constructor that receives a single int is a conversion constructor and uses it to convert the argument 3 into a temporary Array object that contains three elements. Then, the compiler passes the temporary Array object to function outputArray to output the Array's contents. Thus, even though we do not explicitly provide an outputArray function that receives an int argument, the compiler is able to compile line 15. The output shows the contents of the three-element Array containing 0s.
Preventing Accidental Use of a Single-Argument Constructor as a Conversion Constructor
C++ provides the keyword explicit to suppress implicit conversions via conversion constructors when such conversions should not be allowed. A constructor that is declared explicit cannot be used in an implicit conversion. Figure 11.17 declares an explicit constructor in class Array. The only modification to Array.h was the addition of the keyword explicit to the declaration of the single-argument constructor at line 15. No modifications are required to the source-code file containing class Array's member-function definitions.
Figure 11.17. Array class definition with explicit constructor.
(This item is displayed on page 619 in the print version)
1 // Fig. 11.17: Array.h 2 // Array class for storing arrays of integers. 3 #ifndef ARRAY_H 4 #define ARRAY_H 5 6 #include 7 using std::ostream; 8 using std::istream; 9 10 class Array 11 { 12 friend ostream &operator<<( ostream &, const Array & ); 13 friend istream &operator>>( istream &, Array & ); 14 public: 15 explicit Array( int = 10 ); // default constructor 16 Array( const Array & ); // copy constructor 17 ~Array(); // destructor 18 int getSize() const; // return size 19 20 const Array &operator=( const Array & ); // assignment operator 21 bool operator==( const Array & ) const; // equality operator 22 23 // inequality operator; returns opposite of == operator 24 bool operator!=( const Array &right ) const 25 { 26 return ! ( *this == right ); // invokes Array::operator== 27 } // end function operator!= 28 29 // subscript operator for non-const objects returns lvalue 30 int &operator[]( int ); 31 32 // subscript operator for const objects returns rvalue 33 const int &operator[]( int ) const; 34 private: 35 int size; // pointer-based array size 36 int *ptr; // pointer to first element of pointer-based array 37 }; // end class Array 38 39 #endif |
Figure 11.18 presents a slightly modified version of the program in Fig. 11.16. When this program is compiled, the compiler produces an error message indicating that the integer value passed to outputArray at line 15 cannot be converted to a const Array &. The compiler error message is shown in the output window. Line 16 demonstrates how the explicit constructor can be used to create a temporary Array of 3 elements and pass it to function outputArray.
Figure 11.18. Demonstrating an explicit constructor.
1 // Fig. 11.18: Fig11_18.cpp 2 // Driver for simple class Array. 3 #include 4 using std::cout; 5 using std::endl; 6 7 #include "Array.h" 8 9 void outputArray( const Array & ); // prototype 10 11 int main() 12 { 13 Array integers1( 7 ); // 7-element array 14 outputArray( integers1 ); // output Array integers1 15 outputArray( 3 ); // convert 3 to an Array and output Array's contents 16 outputArray( Array( 3 ) ); // explicit single-argument constructor call 17 return 0; 18 } // end main 19 20 // print array contents 21 void outputArray( const Array &arrayToOutput ) 22 { 23 cout << "The Array received has " << arrayToOutput.getSize() 24 << " elements. The contents are: " << arrayToOutput << endl; 25 } // end outputArray
|
Common Programming Error 11.10
Attempting to invoke an explicit constructor for an implicit conversion is a compilation error. |
Common Programming Error 11.11
Using the explicit keyword on data members or member functions other than a single-argument constructor is a compilation error. |
Error-Prevention Tip 11.3
Use the explicit keyword on single-argument constructors that should not be used by the compiler to perform implicit conversions. |
Introduction to Computers, the Internet and World Wide Web
Introduction to C++ Programming
Introduction to Classes and Objects
Control Statements: Part 1
Control Statements: Part 2
Functions and an Introduction to Recursion
Arrays and Vectors
Pointers and Pointer-Based Strings
Classes: A Deeper Look, Part 1
Classes: A Deeper Look, Part 2
Operator Overloading; String and Array Objects
Object-Oriented Programming: Inheritance
Object-Oriented Programming: Polymorphism
Templates
Stream Input/Output
Exception Handling
File Processing
Class string and String Stream Processing
Web Programming
Searching and Sorting
Data Structures
Bits, Characters, C-Strings and structs
Standard Template Library (STL)
Other Topics
Appendix A. Operator Precedence and Associativity Chart
Appendix B. ASCII Character Set
Appendix C. Fundamental Types
Appendix D. Number Systems
Appendix E. C Legacy Code Topics
Appendix F. Preprocessor
Appendix G. ATM Case Study Code
Appendix H. UML 2: Additional Diagram Types
Appendix I. C++ Internet and Web Resources
Appendix J. Introduction to XHTML
Appendix K. XHTML Special Characters
Appendix L. Using the Visual Studio .NET Debugger
Appendix M. Using the GNU C++ Debugger
Bibliography