⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 14value.html

📁 C ++ in action
💻 HTML
📖 第 1 页 / 共 3 页
字号:
</table><!-- End Code -->
Here, testing for &quot;source equal to target&quot; in the assignment operator will prevent us from trying to deallocate the source before making the copy. In fact, no copying is necessary when target is identical to the source.


<p>There's also a bit of magic from the standard C++ library that I used to convert an <var>int</var> to its string representation. It's just like printing an <var>int</var> to <var>cout</var>, only that instead of <var>cout</var> I used a <var>stringstream</var>. It's a stream that has a string for its output. You can get hold of this string by calling the method <var>stringstream::str</var>. (Before you do that, make sure to null-terminate the string by outputting the special <var>ends</var> (end string) object). To get the string-stream functionality you have to <var>#include &lt;sstream&gt;</var> in your code and add the appropriate <var>using</var> statements.

<p>Finally, to make things really interesting, I added an overloading of the <i>plus operator</i> for objects of type <var>Value</var>. 

<!-- Code --><table width="100%" cellspacing=10><tr>    <td class=codetable>
<pre>inline Value <span class=method>operator+</span> (Value const &amp; v1, Value const &amp; v2 )
{
    cout &lt;&lt; "  operator + (" &lt;&lt; v1._numString &lt;&lt; ", " 
         &lt;&lt; v2._numString &lt;&lt; ")\n";
    stringstream buffer;
    buffer &lt;&lt; v1._numString &lt;&lt; " + " &lt;&lt; v2._numString &lt;&lt; ends;
    Value result;
    result.Init (buffer.str ().c_str ());
    cout &lt;&lt; "  Returning by value\n";
    return result;
}</pre>
</table><!-- End Code -->

<p>Our <var>operator+</var> takes two arguments of the type const reference to <var>Value</var> and returns another <var>Value</var>--their &quot;sum&quot;--by value. Next time the compiler sees an expression like <var>val1 + val2</var>, it will turn it into the call our method <var>Value::operator+</var>. In fact, the following two statements are equivalent:
<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>val3 = val1 + val2;
val3.operator= (operator+ (val1, val2));</pre>
</table><!-- End Code -->

<p>For us, humans, the first one is usually much easier to grasp.

<p>Notice that I haven't declared <var>operator+</var> to be a method of <var>Value</var>. Instead it is a <i>free function</i>. You can execute it outside of the context of any particular object. There is a good reason for doing that and I am about to explain it. But first, let me show you the alternative approach.

<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>Value Value::operator+ (Value const &amp; val) const
{
    cout &lt;&lt; "  Value::operator + (" &lt;&lt; val._numString &lt;&lt; ")\n";
    stringstream buffer;
    buffer &lt;&lt; _numString &lt;&lt; " + " &lt;&lt; val._numString &lt;&lt; ends;
    Value result;
    result.Init (buffer.str ());
    cout &lt;&lt; "  Returning by value\n";
    return result;
}</pre>
</table><!-- End Code -->

<p>Here, <var>operator+</var> is defined as a member function of <var>Value</var>, so it requires only one argument. The first addend is implicit as the <var>this</var> object. The syntax for using this version of <var>operator+</var> is identical to the free-function one and the equivalence works as follows,

<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>val3 = val1 + val2;
val3.operator= (val1.operator+ (val2));</pre>
</table><!-- End Code -->

<p>There is just one subtle difference between these two definitions--the way the two addends are treated in the "method" implementation is not symmetric. Normally that wouldn't be a problem, except when you try to add a regular integer to a <var>Value</var> object. Consider this,

<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>Value v (5);
Value vSum = v + 10;</pre>
</table><!-- End Code -->
<p>In general you'd need to define a separate overload of <var>operator+</var> to deal with such mixed additions. For instance, using the free-function approach,
<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>Value operator+ (Value const &amp; val, int i);</pre>
</table><!-- End Code -->
or, using the "method" approach,
<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>Value Value::operator+ (int i);</pre>
</table><!-- End Code -->
<p>You see what the problem is? You can deal with adding an <var>int</var> to a <var>Value</var> using either method. But if you want to do the opposite, add a <var>Value</var> to an <var>int</var>, only the free-function approach will work. Like this:
<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>Value operator+ (int i, Value const & val);
Value v (5);
Value vSum = 10 + v;</pre>
</table><!-- End Code -->
<p>By the way, notice how smart the compiler is when parsing arithmetic expressions. It looks at the types of operands and, based on that, finds the correct overloading of the operator. Actually, it is even smarter than that!

<p>I told you that you needed a special overloading of <var>operator+</var> to deal with mixed additions. It's not entirely true. In fact both, adding an <var>int</var> to a <var>Value</var> and adding a <var>Value</var> to an <var>int</var> would work without any additional overloads of <var>operator+</var>. How? Because there is an implicit conversion from an <var>int</var> to a <var>Value</var> that the compiler may, and will, use in its attempts to parse a mixed arithmetic expression. This is only a slight generalization of what happens when you are adding an <var>int</var> to a <var>double</var>. For instance,
<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>double x = 1.2;
int n = 3;
double result = x + n;</pre>
</table><!-- End Code -->
In order to perform this addition, the compiler converts <var>n</var> to a <var>double</var> and uses its internal implementation of "double" addition. Similarly, in
<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>Value v (12);
int n = 3
Value result = v + n;</pre>
</table><!-- End Code -->
the compiler is free to convert <var>n</var> to a <var>Value</var> and use the overloaded <var>operator+</var> that takes two (const references to) <var>Values</var>. In case you're wondering about the <i>implicit conversion</i> from <var>int</var> to <var>Value</var>, look again at the set of <var>Value</var>'s constructors.
<!-- Definition -->
<p>
<table border=4 cellpadding=10><tr>
	<td bgcolor="#ffffff" class=deftable>
A constructor that takes a single argument, defines an implicit conversion from the type of its argument to its own type.
</td></tr></table>
<!-- End Definition -->
<p>But converting an <var>int</var> into a <var>Value</var> is one thing. Here, the compiler has to do even more. It has to convert an <var>int</var> into a <i>const reference to</i> <var>Value</var>. It does it by creating a temporary <var>Value</var>, initializing it with an integer and passing a const reference to it to the called function. The tricky part is that the temporary <var>Value</var> has to be kept alive for the duration of the call. How the compiler does it is another story. Suffice it to say that <i>it works</i>.

<p>What does <i>not</i> work is if you tried to do the same with a non-<var>const</var> reference. A function that takes a non-<var>const</var> reference expects to be able to have a side effect of changing the value of that argument. So using a temporary as a go-between is out of the question. In fact, the compiler will refuse to do implicit conversions to non-<var>const</var> references even it the case of built-in primitive types. Consider the following example,

<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>void increment (double &amp; x) { x += 1.0; }
int n = 5;
increment (n);   // &lt;- error!</pre>
</table><!-- End Code -->
<p>There is no way the compiler could figure out that you were expecting <var>n</var> to change its value from 5 to 6. And it can't pass the address of an <var>int</var> where the address of a <var>double</var> is expected, because the two have a completely different memory layout (and possibly size). Therefore it will tell you something along the lines, "<i>Error: There's no conversion from int to (non-const) reference to double</i>."

<p>In contrast, the following code compiles just fine,

<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>void dontIncrement (double <b>const</b> & x);
int n = 5;
dontIncrement (n);  // &lt;- ok!</pre>
</table><!-- End Code -->
because the compiler is free to generate a temporary <var>double</var>, initialize it with the value on <var>n</var> and pass a <b><i>const</i></b> reference to it.


<p>One last thing. Why did I declare <var>operator=</var> to return a reference to <var>Value</var> and <var>operator+</var> to return <var>Value</var> by value? Let's start with operator=. It has to return something (by reference or by value) if you like this style of programming,

<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>Value v1, v2, v3 (123);
v1 = v2 = v3;</pre>
</table><!-- End Code -->
or
<!-- Code -->
<table width="100%" cellspacing=10><tr>
	<td class=codeTable>
<pre>if (v1 = v2) // notice, it's not ==
    ...</pre>
	</td></tr>
</table>
<!-- End Code -->
The chaining of assignments works, because it is interpreted as
<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>v1.operator= (v2.operator= (v3));</pre>
</table><!-- End Code -->
and the assignment as condition works because it is equivalent to
<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>if (v1.operator= (v2))
    ...</pre>
</table><!-- End Code -->
and the return value from <var>operator=</var> is interpreted as <var>true</var> if it's different from zero.
<p>On the other hand, if you don't care for this style of programming, you may as well declare <var>operator=</var> to return <var>void</var>.
<!-- Code --><table width="100%" cellspacing=10><tr>	<td class=codetable>
<pre>void Value::operator= (Value const & val)</pre>
</table><!-- End Code -->

<p>I wouldn't recommend the same when overloading <var>operator+</var>, because we are usually interested in the result of addition. So instead, let me try to explain why returning the result of addition <i>by value</i> is preferable to returning it by reference. It's simple--just ask yourself the question, "Reference to what?" It can't be the reference to any of the addends, because they are supposed to keep their original values. So you have to create a new object to store the result of addition. If you create it on the stack, as a local variable, it will quickly disappear after you return from <var>operator+</var>. You'll end up with a reference to some stack junk. If you create it using <var>new</var>, it won't disappear from under your reference, but then you'll never be able to delete it. You'll end up with a memory leak.

<p>Think of <var>operator+</var> (or any other binary operator for that matter) as a two-argument constructor. It constructs a new entity, the sum of the two addends, and it has to put it somewhere. There is very little choice as to where to store it, especially when you're inside a complicated arithmetic expression. Return it by value and let the compiler worry about the rest.

<p class=summary>To summarize, primitive built-in types are usually passed around by value. The same kind of value semantics for a user-defined type is accomplished by the use of a copy constructor and an overloaded operator=. Unless specifically overridden, the compiler will create a default copy constructor and default assignment for any user-defined type. 

<br><a href="../index.htm">Back to table of contents.</a>

</table>
<!-- End Main Table -->
</body>
</html>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -