📄 mc2.htm
字号:
<A NAME="65111"></A>
};</PRE>
</UL>
<A NAME="65112"></A>
<P><A NAME="dingp20"></A>
The first constructor in the class allows clients to specify a range of array indices, for example, from 10 to 20. As a two-argument constructor, this function is ineligible for use as a type-conversion function. The second constructor, which allows clients to define <CODE>Array</CODE> objects by specifying only the number of elements in the array (in a manner similar to that used with built-in arrays), is different. It <I>can</I> be used as a type conversion function, and that can lead to endless <NOBR>anguish.<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P> <A NAME="79151"></A>
<P><A NAME="dingp21"></A>
For example, consider a template specialization for comparing <CODE>Array<int></CODE> objects and some code that uses such <NOBR>objects:<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="74655"></A>
<UL><PRE>
bool operator==( const Array<int>& lhs,
const Array<int>& rhs);
<A NAME="65114"></A>
<A NAME="p28"></A>Array<int> a(10);
Array<int> b(10);
<A NAME="65115"></A>
...
<A NAME="65116"></A>
for (int i = 0; i < 10; ++i)
if (a == b[i]) { // oops! "a" should be "a[i]"
<I>do something for when</I>
<I>a[i] and b[i] are equal;</I>
}
else {
<I>do something for when they're not;</I>
}</PRE>
</UL>
<A NAME="65117"></A>
<P><A NAME="dingp22"></A>
We intended to compare each element of <CODE>a</CODE> to the corresponding element in <CODE>b</CODE>, but we accidentally omitted the subscripting syntax when we typed <CODE>a</CODE>. Certainly we expect this to elicit all manner of unpleasant commentary from our compilers, but they will complain not at all. That's because they see a call to <CODE>operator==</CODE> with arguments of type <CODE>Array<int></CODE> (for <CODE>a</CODE>) and <CODE>int</CODE> (for <CODE>b[i]</CODE>), and though there is no <CODE>operator==</CODE> function taking those types, our compilers notice they can convert the <CODE>int</CODE> into an <CODE>Array<int></CODE> object by calling the <CODE>Array<int></CODE> constructor that takes a single <CODE>int</CODE> as an argument. This they proceed to do, thus generating code for a program we never meant to write, one that looks like <NOBR>this:<SCRIPT>create_link(22);</SCRIPT>
</NOBR></p><A NAME="65118"></A>
<UL><PRE>for (int i = 0; i < 10; ++i)
if (a == static_cast< Array<int> >(b[i])) ...
</PRE>
</UL><A NAME="65119"></A>
<P><A NAME="dingp23"></A>
Each iteration through the loop thus compares the contents of <CODE>a</CODE> with the contents of a temporary array of size <CODE>b[i]</CODE> (whose contents are presumably undefined). Not only is this unlikely to behave in a satisfactory manner, it is also tremendously inefficient, because each time through the loop we both create and destroy a temporary <CODE>Array<int></CODE> object (see <A HREF="./MC4_FR.HTM#41177" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 19'; return true" onMouseOut = "self.status = self.defaultStatus">Item 19</A>).<SCRIPT>create_link(23);</SCRIPT>
</P> <A NAME="65147"></A>
<P><A NAME="dingp24"></A>
The drawbacks to implicit type conversion operators can be avoided by simply failing to declare the operators, but single-argument constructors cannot be so easily waved away. After all, you may really <I>want</I> to offer single-argument constructors to your clients. At the same time, you may wish to prevent compilers from calling such constructors indiscriminately. Fortunately, there is a way to have it all. In fact, there are two ways: the easy way and the way you'll have to use if your compilers don't yet support the easy <NOBR>way.<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P> <A NAME="34678"></A>
<P><A NAME="dingp25"></A>
The easy way is to avail yourself of one of the newest C++ features, the <CODE>explicit</CODE> keyword. This feature was introduced specifically to address the problem of implicit type conversion, and its use is about as straightforward as can be. Constructors can be declared <CODE>explicit</CODE>, and if they are, compilers are prohibited from invoking them for pur<A NAME="p29"></A>poses of implicit type conversion. Explicit conversions are still legal, <NOBR>however:<SCRIPT>create_link(25);</SCRIPT>
</NOBR></p><A NAME="77506"></A>
<UL><PRE>template<class T>
class Array {
public:
...
explicit Array(int size); // note use of "explicit"
...
};
<A NAME="77524"></A>
Array<int> a(10); // okay, explicit ctors can
// be used as usual for
// object construction
<A NAME="77526"></A>
Array<int> b(10); // also okay
<A NAME="77528"></A>
if (a == b[i]) ... // error! no way to
// implicitly convert
// int to Array<int>
<A NAME="77535"></A>
if (a == Array<int>(b[i])) ... // okay, the conversion
// from int to Array<int> is
// explicit (but the logic of
// the code is suspect)
<A NAME="77569"></A>
if (a == static_cast< Array<int> >(b[i])) ...
// equally okay, equally
// suspect
<A NAME="77542"></A>
if (a == (Array<int>)b[i]) ... // C-style casts are also
// okay, but the logic of
// the code is still suspect</PRE>
</UL>
<A NAME="77565"></A>
<P><A NAME="dingp26"></A>
In the example using <CODE>static_cast</CODE> (see <A HREF="./MC1_FR.HTM#77216" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 2'; return true" onMouseOut = "self.status = self.defaultStatus">Item 2</A>), the space separating the two "<CODE>></CODE>" characters is no accident. If the statement were written like <NOBR>this,<SCRIPT>create_link(26);</SCRIPT>
</NOBR></p><A NAME="77586"></A>
<UL><PRE>if (a == static_cast<Array<int>>(b[i])) ...
</PRE>
</UL><A NAME="6362"></A>
<P><A NAME="dingp27"></A>
it would have a different meaning. That's because C++ compilers parse "<CODE>>></CODE>" as a single token. Without a space between the "<CODE>></CODE>" characters, the statement would generate a syntax <NOBR>error.<SCRIPT>create_link(27);</SCRIPT>
</NOBR></P> <A NAME="6363"></A>
<P><A NAME="dingp28"></A>
If your compilers don't yet support <CODE>explicit</CODE>, you'll have to fall back on home-grown methods for preventing the use of single-argument constructors as implicit type conversion functions. Such methods are obvious only <I>after</I> you've seen <NOBR>them.<SCRIPT>create_link(28);</SCRIPT>
</NOBR></P> <A NAME="92826"></A>
<P><A NAME="dingp29"></A>
I mentioned earlier that there are complicated rules governing which sequences of implicit type conversions are legitimate and which are not. One of those rules is that no sequence of conversions is allowed to contain more than one user-defined conversion (i.e., a call to a single-argument constructor or an implicit type conversion operator). By con<A NAME="p30"></A>structing your classes properly, you can take advantage of this rule so that the object constructions you want to allow are legal, but the implicit conversions you don't want to allow are <NOBR>illegal.<SCRIPT>create_link(29);</SCRIPT>
</NOBR></P> <A NAME="34708"></A>
<P><A NAME="dingp30"></A>
Consider the <CODE>Array</CODE> template again. You need a way to allow an integer specifying the size of the array to be used as a constructor argument, but you must at the same time prevent the implicit conversion of an integer into a temporary <CODE>Array</CODE> object. You accomplish this by first creating a new class, <CODE>ArraySize</CODE>. Objects of this type have only one purpose: they represent the size of an array that's about to be created. You then modify <CODE>Array</CODE>'s single-argument constructor to take an <CODE>ArraySize</CODE> object instead of an <CODE>int</CODE>. The code looks like <NOBR>this:<SCRIPT>create_link(30);</SCRIPT>
</NOBR></p><A NAME="34709"></A>
<UL><PRE>template<class T>
class Array {
public:
<A NAME="34710"></A>
class ArraySize { // this class is new
public:
ArraySize(int numElements): theSize(numElements) {}
int size() const { return theSize; }
<A NAME="57410"></A>
private:
int theSize;
};
<A NAME="34712"></A>
Array(int lowBound, int highBound);
Array(ArraySize size); // note new declaration
<A NAME="34713"></A>
...
<A NAME="71609"></A>
};
</PRE>
</UL><A NAME="34715"></A>
<P><A NAME="dingp31"></A>
Here you've nested <CODE>ArraySize</CODE> inside <CODE>Array</CODE> to emphasize the fact that it's always used in conjunction with that class. You've also made <CODE>ArraySize</CODE> public in <CODE>Array</CODE> so that anybody can use it. <NOBR>Good.<SCRIPT>create_link(31);</SCRIPT>
</NOBR></P> <A NAME="34716"></A>
<P><A NAME="dingp32"></A>
Consider what happens when an <CODE>Array</CODE> object is defined via the class's single-argument <NOBR>constructor:<SCRIPT>create_link(32);</SCRIPT>
</NOBR></p><A NAME="34717"></A>
<UL><PRE>Array<int> a(10);
</PRE>
</UL><A NAME="34718"></A>
<P><A NAME="dingp33"></A>
Your compilers are asked to call a constructor in the <CODE>Array<int></CODE> class that takes an <CODE>int</CODE>, but there is no such constructor. Compilers realize they can convert the <CODE>int</CODE> argument into a temporary <CODE>ArraySize</CODE> object, and that <CODE>ArraySize</CODE> object is just what the <CODE>Array<int></CODE> constructor needs, so compilers perform the conversion with their usual gusto. This allows the function call (and the attendant object construction) to <NOBR>succeed.<SCRIPT>create_link(33);</SCRIPT>
</NOBR></P> <A NAME="34720"></A>
<P><A NAME="dingp34"></A>
The fact that you can still construct <CODE>Array</CODE> objects with an <CODE>int</CODE> argument is reassuring, but it does you little good unless the type conver<A NAME="p31"></A>sions you want to avoid are prevented. They are. Consider this code <NOBR>again:<SCRIPT>create_link(34);</SCRIPT>
</NOBR></p><A NAME="82126"></A>
<UL><PRE>bool operator==( const Array<int>& lhs,
const Array<int>& rhs);
</PRE>
</UL><A NAME="82127"></A>
<UL><PRE>Array<int> a(10);
Array<int> b(10);
</PRE>
</UL><A NAME="82128"></A>
<UL><PRE>...
</PRE>
</UL><A NAME="82116"></A>
<UL><PRE>for (int i = 0; i < 10; ++i)
if (a == b[i]) ... // oops! "a" should be "a[i]";
// this is now an error
</PRE>
</UL><A NAME="6346"></A>
<P><A NAME="dingp35"></A>
Compilers need an object of type <CODE>Array<int></CODE> on the right-hand side of the "<CODE>==</CODE>" in order to call <CODE>operator==</CODE> for <CODE>Array<int></CODE> objects, but there is no single-argument constructor taking an <CODE>int</CODE> argument. Furthermore, compilers cannot consider converting the <CODE>int</CODE> into a temporary <CODE>ArraySize</CODE> object and then creating the necessary <CODE>Array<int></CODE> object from this temporary, because that would call for two user-defined conversions, one from <CODE>int</CODE> to <CODE>ArraySize</CODE> and one from <CODE>ArraySize</CODE> to <CODE>Array<int></CODE>. Such a conversion sequence is <I>verboten</I>, so compilers must issue an error for the code attempting to perform the <NOBR>comparison.<SCRIPT>create_link(35);</SCRIPT>
</NOBR></P> <A NAME="6347"></A>
<P><A NAME="dingp36"></A>
The use of the <CODE>ArraySize</CODE> class in this example might look like a special-purpose hack, but it's actually a specific instance of a more general technique. Classes like <CODE>ArraySize</CODE> are often called <I>proxy classes</I>, because each object of such a class stands for (is a proxy for) some other object. An <CODE>ArraySize</CODE> object is really just a stand-in for the integer used to specify the size of the <CODE>Array</CODE> being created. Proxy objects can give you control over aspects of your software's behavior — in this case implicit type conversions — that is otherwise beyond your grasp, so it's well worth your while to learn how to use them. How, you might wonder, can you acquire such learning? One way is to turn to <A HREF="./MC5_FR.HTM#6074" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 30'; return true" onMouseOut = "self.status = self.defaultStatus">Item 30</A>; it's devoted to proxy <NOBR>classes.<SCRIPT>create_link(36);</SCRIPT>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -