📄 14value.html
字号:
<html>
<head>
<title>Passing by Value</title>
<meta name="description" content="Value semantics in C++">
<meta name="keywords" content="value semantics, passing, variable, by value, copy semantics">
<link rel="stylesheet" href="../../rs.css">
</head>
<body background="../../images/margin.gif" bgcolor="#FFFFDC">
<!-- Main Table -->
<table cellpadding="6">
<tr>
<td width="78">
<td>
<h3>Passing by Value</h3>
<p class=topics>Copy constructor, overloading the assignment operator, default copy constructor and operator =, return by value, passing by value, implicit type conversions.
<p>So far we've been careful to pass objects from and to methods using references or pointers. For instance, in the following line of code
<!-- Code --><table width="100%" cellspacing=10><tr> <td class=codetable>
<pre>Parser parser (scanner, store, funTab, symTab);</pre>
</table><!-- End Code -->
all the arguments to the <var>Parser</var>'s constructor--<var>scanner</var>, <var>store</var>, <var>funTab</var> and <var>symTab</var>--are passed by reference. We know that, because we've seen the following declaration (and so did the compiler):
<!-- Code --><table width="100%" cellspacing=10><tr> <td class=codetable>
<pre>Parser (Scanner & scanner,
Store & store,
FunctionTable & funTab,
SymbolTable & symTab);</pre>
</table><!-- End Code -->
When we construct the parser, we don't give it a <i>copy</i> of a symbol table. We give it <i>access</i> to an existing symbol table. If we gave it a private copy, we wouldn't have been able to see the changes the parser made to it. The parser may, for instance, add a new variable to the symbol table. We want our symbol table to remember this variable even after the current parser is destroyed. The same goes for <var>store</var>--it must remember the values assigned to symbolic variables across the invocations of the parser.
<p>But what about the scanner? We don't really care whether the parser makes a scratch copy of it for its private use. Neither do we care what the parser does to the function table. What we <i>do</i> care about in this case is performance. Creating a scratch copy of a large object is quite time consuming.
<p>But suppose we didn't care about performance. Would the following work?
<!-- Code --><table width="100%" cellspacing=10><tr> <td class=codetable>
<pre>Parser (<font color="Red">Scanner scanner</font>,
Store & store,
<font color="Red">FunctionTable funTab</font>,
SymbolTable & symTab);</pre>
</table><!-- End Code -->
<p>Notice the absence of ampersands after <var>Scanner</var> and <var>FunctionTable</var>. What we are telling the compiler is this: When the caller creates a Parser, passing it a scanner, make a temporary copy of this scanner and let the Parser's constructor operate on that copy.
<!-- Sidebar -->
<table width="100%" border=0 cellpadding=5><tr>
<td width=10>
<td bgcolor="#cccccc" class=sidebar>
This is, after all, the way built-in types are passed around. When you call a method that expects an integer, it's the copy of that integer that's used inside the method. You can modify that copy to your heart's content and you'll never change the original. Only if you explicitly request that the method accept a <i>reference</i> to an integer, can you change the original.</table>
<!-- End Sidebar -->
<p>There are many reasons why such approach will not work as expected, unless we make several further modifications to our code. First of all, the temporary copy of the scanner (and the function table) will disappear as soon as the execution of the Parser's constructor is finished. The parser will store a reference to it in its member variable, but that's useless. After the end of construction the reference will point to a non-exitsent scratch copy of a scanner. That's not good.
<p>If we decide to pass a copy of the scanner to the parser, we should also <i>store</i> a copy of the scanner inside the parser. Here's how you do it--just omit the ampersand.
<!-- Code --><table width="100%" cellspacing=10><tr> <td class=codetable>
<pre>class Parser
{
...
private:
<font color="Red">Scanner _scanner;</font>
Node * _pTree;
Status _status;
Store & _store;
<font color="Red">FunctionTable _funTab;</font>
SymbolTable & _symTab;
};</pre>
</table><!-- End Code -->
<p>But what is really happening inside the constructor? Now that neither the argument, <var>scanner</var>, nor the member variable, <var>_scanner</var>, are references, how is <var>_scanner</var> initialized with <var>scanner</var>? The syntax is misleadingly simple.
<!-- Code --><table width="100%" cellspacing=10><tr> <td class=codetable>
<pre>Parser::Parser (<font color="Red">Scanner scanner</font>,
Store & store,
<font color="Red">FunctionTable funTab</font>,
SymbolTable & symTab)
: <font color="Red">_scanner (scanner)</font>,
_pTree (0),
_status (stOk),
<font color="Red">_funTab (funTab)</font>,
_store (store),
_symTab (symTab)
{
}</pre>
</table><!-- End Code -->
What happens behind the scenes is that <var>Scanner</var>'s <i>copy constructor</i> is called. A copy constructor is the one that takes a (possibly <var>const</var>) reference to the object of the same class and clones it. In our case, the appropriate constructor would be declared as follows,
<!-- Code --><table width="100%" cellspacing=10><tr> <td class=codetable>
<pre>Scanner::Scanner (Scanner const & scanner);</pre>
</table><!-- End Code -->
<p>But wait a minute! <var>Scanner</var> does <i>not</i> have a constructor of this signature. Why doesn't the compiler protest, like it always does when we try to call an undefined member function? The unexpected answer is that, if you don't explicitly declare a copy constructor for a given class, the compiler will create one for you. If this doesn't sound scary, I don't know what does.
<tr>
<td class=margin valign=top>
<img src="../../images/bug.gif" width=25 height=25 border=0 alt="Rule of thumb.">
<td>
<!-- Definition -->
<p>
<table border=4 cellpadding=10><tr>
<td bgcolor="#ffffff" class=deftable>
Beware of default copy constructors!</table>
<!-- End Definition -->
<p>The copy constructor generated by the compiler is probably wrong! After all, what can a dumb compiler know about copying user defined classes? Sure, it tries to do its best--it
<ul>
<li>does a bitwise copy of all the data members that are of built-in types and
<li>calls respective copy constructors for user-defined embedded objects.
</ul>
<p>But that's it. Any time it encounters a pointer it simply duplicates it. It <i>does not</i> create a copy of the object pointed to by the pointer. That might be okay, or not--only the creator of the class knows for sure.
<p>This kind of operation is called a <i>shallow copy</i>, as opposed to a <i>deep copy</i> which follows all the pointers. Shallow copy is fine when the pointed-to data structures can be easily shared between multiple instances of the object.
<p>But consider, as an example, what happens when we make a shallow copy of the top node of a parse tree. If the top node has children, a shallow copy will not clone the child nodes. We will end up with two top nodes, both pointing to the same child nodes. That's not a problem until the destructor of one of the top nodes is called. It promptly deletes its children. And what is the second top node pointing to now? A piece of garbage! The moment it tries to access the children, it will stomp over reclaimed memory with disastrous results. But even if it does nothing, eventually its own destructor is called. And that destructor will attempt to delete the same children that have already been deleted by the first top node. The result? Memory corruption.
<p>But wait, there's more! C++ not only sneaks a default copy constructor on you. It also provides you with a convenient default assignment operator.
<tr>
<td class=margin valign=top>
<img src="../../images/bug.gif" width=25 height=25 border=0 alt="Rule of thumb.">
<td>
<!-- Definition -->
<p>
<table border=4 cellpadding=10><tr>
<td bgcolor="#ffffff" class=deftable>
Beware of default assignments!</table>
<!-- End Definition -->
<p>The following code is perfectly legal.
<!-- Code --><table width="100%" cellspacing=10><tr> <td class=codetable>
<pre>SymbolTable symTab1 (100);
SymbolTable symTab2 (200);
// ...
symTab1 = symTab2;</pre>
</table><!-- End Code -->
<p>Not only does it perform a shallow copy of <var>symTab2</var> into <var>symTab1</var>, but it also clobbers whatever already was there in <var>symTab1</var>. All memory that was allocated in <var>symTab1</var> is lost, never to be reclaimed. Instead, the memory allocated in <var>symTab2</var> will be double deleted. Now that's a bargain!
<!-- Sidebar -->
<table width="100%" border=0 cellpadding=5><tr>
<td width=10>
<td bgcolor="#cccccc" class=sidebar>
Why does C++ quietly let a skunk into our house and waits for us to run into it in the dark? If you've followed this book closely, you know the answer--compatibility with C! You see, in C you can't define a copy constructor, because there aren't any constructors. So every time you wanted to copy something more complex than an <var>int</var>, you'd have to write a special function or a macro. So, in the traditional spirit of letting programmers shoot themselves in the foot, C provided this additional facility of quietly copying all the user-defined <var>struct</var>'s. Now, in C this wasn't such a big deal--a C <var>struct</var> is just raw data. There is no data hiding, no methods, no inheritance. Besides, there are no references in C, so you are much less likely to inadvertently copy a <var>struct</var>, just because you forgot one ampersand. But in C++ it's a completely different story. Beware--many a bug in C++ is a result of a missing ampersand.
</table>
<!-- End Sidebar -->
<p>But wait, there's even more! C++ not only offers a free copy constructor and a free assignment, it will also quietly use these two to return objects from functions. Here's a fragment of code from our old implementation of a stack-based calculator, except for one small modification. Can you spot it?
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -