📄 ec4.htm
字号:
</PRE>
</UL><A NAME="5858"></A>
<P><A NAME="dingp35"></A>
By declaring the same function twice, once <CODE>const</CODE> and once non-<CODE>const</CODE>, you provide support for both <CODE>const</CODE> and non-<CODE>const</CODE> <CODE>Array</CODE> objects. The difference in return types is significant, as is explained in <A HREF="#6003">Item 21</A>.<SCRIPT>create_link(35);</SCRIPT>
</P>
<A NAME="5862"></A>
<P><A NAME="dingp36"></A>
As it now stands, the <CODE>Array</CODE> template supports construction, destruction, pass-by-value, assignment, and indexing, which may strike you as a complete interface. But look closer. Suppose a client wants to loop through an array of integers, printing out each of its elements, like <NOBR>so:<SCRIPT>create_link(36);</SCRIPT>
</NOBR></P>
<A NAME="5863"></A>
<UL><PRE>Array<int> a(10, 20); // bounds on a are 10 to 20
</PRE>
</UL><A NAME="5864"></A>
<UL><PRE>...
</PRE>
</UL><A NAME="5865"></A>
<UL><PRE>for (int i = <I>lower bound of a</I>; i <= <I>upper bound of a</I>; ++i)
cout << "a[" << i << "] = " << a[i] << '\n';
</PRE>
</UL><A NAME="5866"></A>
<P><A NAME="dingp37"></A>
How is the client to get the bounds of <CODE>a</CODE>? The answer depends on what happens during assignment of <CODE>Array</CODE> objects, i.e., on what happens inside <CODE>Array::operator=</CODE>. In particular, if assignment can change the bounds of an <CODE>Array</CODE> object, you must provide member functions to return the current bounds, because the client has no way of knowing <I>a priori</I> what the bounds are at any given point in the program. In the example above, if <CODE>a</CODE> was the target of an assignment between the time it was defined and the time it was used in the loop, the client would have no way to determine the current bounds of <CODE>a</CODE>.<SCRIPT>create_link(37);</SCRIPT>
</P>
<A NAME="5868"></A>
<P><A NAME="dingp38"></A>
On the other hand, if the bounds of an <CODE>Array</CODE> object cannot be changed during assignment, then the bounds are fixed at the point of <A NAME="p83"></A>definition, and it would be possible (though cumbersome) for a client to keep track of these bounds. In that case, though it would be convenient to offer functions to return the current bounds, such functions would not be part of a truly minimal <NOBR>interface.<SCRIPT>create_link(38);</SCRIPT>
</NOBR></P>
<A NAME="5869"></A>
<P><A NAME="dingp39"></A>
Proceeding on the assumption that assignment can modify the bounds of an object, the bounds functions could be declared <NOBR>thus:<SCRIPT>create_link(39);</SCRIPT>
</NOBR></P>
<A NAME="5870"></A>
<UL><PRE>int lowBound() const;
int highBound() const;
</PRE>
</UL><A NAME="5871"></A>
<P><A NAME="dingp40"></A>
Because these functions don't modify the object on which they are invoked, and because you prefer to use <CODE>const</CODE> whenever you can (see <A HREF="#6003">Item 21</A>), these are both declared <CODE>const</CODE> member functions. Given these functions, the loop above would be written as <NOBR>follows:<SCRIPT>create_link(40);</SCRIPT>
</NOBR></P>
<A NAME="5875"></A>
<UL><PRE>for (int i = a.lowBound(); i <= a.highBound(); ++i)
cout << "a[" << i << "] = " << a[i] << '\n';
</PRE>
</UL><A NAME="5876"></A>
<P><A NAME="dingp41"></A>Needless to say, for such a loop to work for an array of objects of type <CODE>T</CODE>, an <CODE>operator<<</CODE> function must be defined for objects of type <CODE>T</CODE>. (That's not quite true. What must exist is an <CODE>operator<<</CODE> for <CODE>T</CODE> or for some other type to which <CODE>T</CODE> may be implicitly converted (see <A HREF="../MEC/MC2_FR.HTM#5970" TARGET="_top">Item M5</A>). But you get the <NOBR>idea.)<SCRIPT>create_link(41);</SCRIPT>
</NOBR></P>
<A NAME="5877"></A>
<P><A NAME="dingp42"></A>
Some designers would argue that the <CODE>Array</CODE> class should also offer a function to return the number of elements in an <CODE>Array</CODE> object. The number of elements is simply <CODE>highBound()-lowBound()+1</CODE>, so such a function is not really necessary, but in view of the frequency of off-by-one errors, it might not be a bad idea to add such a <NOBR>function.<SCRIPT>create_link(42);</SCRIPT>
</NOBR></P>
<A NAME="5879"></A>
<P><A NAME="dingp43"></A>
Other functions that might prove worthwhile for this class include those for input and output, as well as the various relational operators (e.g., <CODE><</CODE>, <CODE>></CODE>, <CODE>==</CODE>, etc.). None of those functions is part of a minimal interface, however, because they can all be implemented in terms of loops containing calls to <CODE>operator[]</CODE>.<SCRIPT>create_link(43);</SCRIPT>
</P>
<A NAME="5881"></A>
<P><A NAME="dingp44"></A>
Speaking of functions like <CODE>operator<<</CODE>, <CODE>operator>></CODE>, and the relational operators, <A HREF="#5887">Item 19</A> discusses why they are frequently implemented as non-member friend functions instead of as member functions. That being the case, don't forget that friend functions are, for all practical purposes, part of a class's interface. That means that friend functions count toward a class interface's completeness and <NOBR>minimalness.<SCRIPT>create_link(44);</SCRIPT>
</NOBR></P>
<!-- SectionName="E19: Member funcs, non-member funcs, friends" -->
<A NAME="5887"></A><A NAME="p84"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="#17774">Item 18: Strive for class interfaces that are complete and minimal.</A>
<BR>Continue to <A HREF="#5976">Item 20: Differentiate among member functions, non-member functions, and friend functions.</A></FONT></DIV>
<P><A NAME="dingp45"></A><FONT ID="eititle">Item 19: Differentiate among member functions, non-member functions, and friend functions.</FONT><SCRIPT>create_link(45);</SCRIPT>
</P>
<A NAME="5889"></A>
<P><A NAME="dingp46"></A>
The biggest difference between member functions and non-member functions is that member functions can be virtual and non-member functions can't. As a result, if you have a function that has to be dynamically bound (see <A HREF="./EC6_FR.HTM#177948" TARGET="_top">Item 38</A>), you've got to use a virtual function, and that virtual function must be a member of some class. It's as simple as that. If your function doesn't need to be virtual, however, the water begins to muddy a <NOBR>bit.<SCRIPT>create_link(46);</SCRIPT>
</NOBR></P>
<A NAME="5893"></A>
<P><A NAME="dingp47"></A>
Consider a class for representing rational <NOBR>numbers:<SCRIPT>create_link(47);</SCRIPT>
</NOBR></P>
<A NAME="5895"></A>
<UL><PRE>class Rational {
public:
Rational(int numerator = 0, int denominator = 1);
int numerator() const;
int denominator() const;
</PRE>
</UL><A NAME="16284"></A>
<UL><PRE>private:
...
};
</PRE>
</UL>
<A NAME="5900"></A>
<P><A NAME="dingp48"></A>
As it stands now, this is a pretty useless class. (Using the terms of <A HREF="#17774">Item 18</A>, the interface is certainly minimal, but it's <I>far</I> from complete.) You know you'd like to support arithmetic operations like addition, subtraction, multiplication, etc., but you're unsure whether you should implement them via a member function, a non-member function, or possibly a non-member function that's a <NOBR>friend.<SCRIPT>create_link(48);</SCRIPT>
</NOBR></P>
<A NAME="5902"></A>
<P><A NAME="dingp49"></A>
When in doubt, be object-oriented. You know that, say, multiplication of rational numbers is related to the <CODE>Rational</CODE> class, so try bundling the operation with the class by making it a member <NOBR>function:<SCRIPT>create_link(49);</SCRIPT>
</NOBR></P>
<A NAME="5903"></A>
<UL><PRE>class Rational {
public:
</PRE>
</UL><A NAME="5904"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="5905"></A>
<UL><PRE> const Rational operator*(const Rational& rhs) const;
};
</PRE>
</UL><A NAME="5906"></A>
<P><A NAME="dingp50"></A>
(If you're unsure why this function is declared the way it is — returning a <CODE>const</CODE> by-value result, but taking a reference-to-<CODE>const</CODE> as its argument — consult Items <A HREF="#6003">21</A>-<A HREF="#6210">23</A>.)<SCRIPT>create_link(50);</SCRIPT>
</P>
<A NAME="5913"></A>
<P><A NAME="dingp51"></A>
Now you can multiply rational numbers with the greatest of <NOBR>ease:<SCRIPT>create_link(51);</SCRIPT>
</NOBR></P>
<A NAME="5914"></A>
<UL><PRE>Rational oneEighth(1, 8);
Rational oneHalf(1, 2);
</PRE>
</UL><A NAME="5915"></A>
<UL><PRE>Rational result = oneHalf * oneEighth; // fine
</PRE>
</UL><A NAME="5916"></A>
<UL><PRE>result = result * oneEighth; // fine
</PRE>
</UL><A NAME="5917"></A>
<P><A NAME="dingp52"></A>
<A NAME="p85"></A>But you're not satisfied. You'd also like to support mixed-mode operations, where <CODE>Rational</CODE>s can be multiplied with, for example, <CODE>int</CODE>s. When you try to do this, however, you find that it works only half the <NOBR>time:<SCRIPT>create_link(52);</SCRIPT>
</NOBR></P>
<A NAME="5918"></A>
<UL><PRE>result = oneHalf * 2; // fine
</PRE>
</UL><A NAME="5919"></A>
<UL><PRE>result = 2 * oneHalf; // error!
</PRE>
</UL><A NAME="5920"></A>
<P><A NAME="dingp53"></A>
This is a bad omen. Multiplication is supposed to be commutative, <NOBR>remember?<SCRIPT>create_link(53);</SCRIPT>
</NOBR></P>
<A NAME="5921"></A>
<P><A NAME="dingp54"></A>
The source of the problem becomes apparent when you rewrite the last two examples in their equivalent functional <NOBR>form:<SCRIPT>create_link(54);</SCRIPT>
</NOBR></P>
<A NAME="5922"></A>
<UL><PRE>result = oneHalf.operator*(2); // fine
</PRE>
</UL><A NAME="5923"></A>
<UL><PRE>result = 2.operator*(oneHalf); // error!
</PRE>
</UL><A NAME="5924"></A>
<P><A NAME="dingp55"></A>
The object <CODE>oneHalf</CODE> is an instance of a class that contains an <CODE>operator*</CODE>, so your compilers call that function. However, the integer 2 has no associated class, hence no <CODE>operator*</CODE> member function. Your compilers will also look for a non-member <CODE>operator*</CODE> (i.e., one that's in a visible namespace or is global) that can be called like <NOBR>this,<SCRIPT>create_link(55);</SCRIPT>
</NOBR></P>
<A NAME="5925"></A>
<UL><PRE>result = operator*(2, oneHalf); // error!
</PRE>
</UL><A NAME="5926"></A>
<P><A NAME="dingp56"></A>
but there is no non-member <CODE>operator*</CODE> taking an <CODE>int</CODE> and a <CODE>Rational</CODE>, so the search <NOBR>fails.<SCRIPT>create_link(56);</SCRIPT>
</NOBR></P>
<A NAME="5927"></A>
<P><A NAME="dingp57"></A>
Look again at the call that succeeds. You'll see that its second parameter is the integer 2, yet <CODE>Rational::operator*</CODE> takes a <CODE>Rational</CODE> object as its argument. What's going on here? Why does 2 work in one position and not in the <NOBR>other?<SCRIPT>create_link(57);</SCRIPT>
</NOBR></P>
<A NAME="5930"></A>
<P><A NAME="dingp58"></A>
What's going on is implicit type conversion. Your compilers know you're passing an <CODE>int</CODE> and the function requires a <CODE>Rational</CODE>, but they also know that they can conjure up a suitable <CODE>Rational</CODE> by calling the <CODE>Rational</CODE> constructor with the <CODE>int</CODE> you provided, so that's what they do (see <A HREF="../MEC/MC4_FR.HTM#41177" TARGET="_top">Item M19</A>). In other words, they treat the call as if it had been written more or less like <NOBR>this:<SCRIPT>create_link(58);</SCRIPT>
</NOBR></P>
<A NAME="5931"></A>
<UL><PRE>
const Rational temp(2); // create a temporary
// Rational object from 2
</PRE>
</UL><A NAME="5932"></A>
<UL><PRE>
result = oneHalf * temp; // same as
// oneHalf.operator*(temp);
</PRE>
</UL><A NAME="76965"></A>
<P><A NAME="dingp59"></A>
Of course, they do this only when non-<CODE>explicit</CODE> constructors are involved, because <CODE>explicit</CODE> constructors can't be used for implicit conversions; that's what <CODE>explicit</CODE> means. If <CODE>Rational</CODE> were defined like <NOBR>this,<SCRIPT>create_link(59);</SCRIPT>
</NOBR></P>
<A NAME="76967"></A>
<UL><PRE><A NAME="p86"></A>class Rational {
public:
explicit Rational(int numerator = 0, // this ctor is
int denominator = 1); // now <I>explicit</I>
...
</PRE>
</UL><A NAME="76958"></A>
<UL><PRE> const Rational operator*(const Rational& rhs) const;
</PRE>
</UL><A NAME="76960"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="76959"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="76939"></A>
<P><A NAME="dingp60"></A>
<I>neither</I> of these statements would <NOBR>compile:<SCRIPT>create_link(60);</SCRIPT>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -