📄 14value.html
字号:
<pre>class <span class=method>Calculator</span>
{
public:
int Execute (Input & input);
IStack const GetStack () const { return _stack; }
private:
int Calculate (int n1, int n2, int token) const;
IStack _stack;
};</pre>
</table>
<!-- End Code -->
<p>What happened here was that I omitted the ampersand in the return type of <var>Calculator::GetStack</var> and <var>IStack</var> is now returned by value. Let's have a very close look at what happens during such transfer. Also, let's assume for a moment that the compiler doesn't do any clever optimizations here. In particular, let's define <var>GetStack</var> out of line, so a regular function call has to be executed.
<!-- Code --><table width="100%" cellspacing=10><tr> <td class=codetable>
<pre>
IStack const <span class=method>Calculator::GetStack</span> () const
{
return _stack;
}
//...
IStack const stk;
stk = calc.GetStack (); // <- by value!</pre>
</table><!-- End Code -->
<p>The process of <em>returning an object by value</em> consists of two steps: copy construction and assignment.
<ul>
<li>First of all, before executing the call to <var>GetStack</var>, the compiler pre-allocates some scratch space on the stack (I'm talking here about the internal call stack, where function arguments and local variables live at runtime). This scratch space is then passed to the function being called. To copy the value of <var>_stack</var> into scratch space, <var>GetStack</var> calls <var>IStack</var>'s copy constructor.
<li>After returning from <var>GetStack</var>, the assignment operator is called to copy the value from scratch space to the local variable <var>stk</var>.
</ul>
<p>It might look pretty complicated at first, but if you think about it, this is really the only sensible fool-proof way of returning an object by value. Especially if the programmer took care of both defining a copy constructor and overloading the assignment operator for the class in question.
<!-- Sidebar -->
<table width=100% border=0 cellpadding=5><tr>
<td width=10>
<td bgcolor="#cccccc" class=sidebar>
For some classes the compiler will not be able to generate a default assignment. These are the classes that contain data members that cannot be assigned outside of the constructor's preamble. This is true for any <var>const</var> members (including const pointers) and for references. For instance, if you tried to assign one parser to another, like this:
<!-- Code --><table width=100% cellspacing=10><tr> <td class=codetable>
<pre>parser1 = parser;</pre>
</td></tr></table><!-- End Code -->
the compiler would give you the error, "No assignment operator defined for class Parser." That's because it can't re-initialize all these references to <var>Scanner</var>, <var>Store</var>, <var>FunctionTable</var> and <var>SymbolTable</var> that are inside <var>parser1</var>. Once a reference refers to something, you can't make it refer to something else. Notice, however, that these restrictions don't apply to the default copy constructor. The code:
<!-- Code --><table width=100% cellspacing=10><tr> <td class=codetable>
<pre>Parser parser1 (parser);</pre>
</td></tr></table><!-- End Code -->
<i>will</i> compile. The references in <var>parser1</var> will be correctly initialized to refer to the same objects as the ones in <var>parser</var>. However, since value semantics requires both--the copy constructor and the assignment--class Parser in its present form cannot be passed by value.
</table>
<!-- End Sidebar -->
<h3>Value Semantics</h3>
<tr>
<td class=margin valign=top>
<br>
<a href="source/value.zip">
<img src="Images/brace.gif" width=16 height=16 border=1 alt="Download!"><br>source</a>
<td>
<p>Let's try to make some sense of this mess. We've just learned about several features of C++ that, each taken separately, may turn our code against us. It's time to find out what they can do for us when used in proper context.
<p>The question whether a given object should be passed by reference or by value should be decided on the level of its class. For some objects it makes little sense to be passed by value, for others it makes a lot of sense. By designing the class properly, we can accommodate either case.
<p>First of all, we can easily protect objects of a given class from being passed by value by declaring a private copy constructor and a private assignment operator. No implementation of these methods is necessary. They are there just to stop the compiler from doing the magic behind our backs.
<p>Suppose we want to prevent <var>IStack</var> objects from being passed by value. All we have to do is add these two lines to its class definition (strictly speaking, one of them would suffice).
<!-- Code --><table width="100%" cellspacing=10><tr> <td class=codetable>
<pre>class IStack
{
public:
// ...
private:
<b>IStack (IStack const & i);</b>
<b>IStack & operator = (IStack const & i);</b>
// ...
};</pre>
</table><!-- End Code -->
<p>Now try compiling any of the code that attempted to pass an <var>IStack</var> to a function, return an <var>IStack</var> from a function or assign one <var>IStack</var> to another. It simply won't compile! If you make a mistake of omitting an ampersand following <var>IStack</var>'s class name in any such context, the compiler will immediately catch it for you.
<p>Should you, therefore, be adding copy constructors and assignment overloads to every class you create? Frankly, I don't think it's practical. There are definitely some classes that are being passed around a lot--these should have this type of protection in their declarations. With others--use your better judgment.
<p>However, there are some classes of objects for which passing by value makes a lot of sense. Such classes are said to have <i>value semantics</i>. Usually, but not always, objects of such classes have relatively small footprint. Size is important if you care about performance. On the other hand, quite often the only alternative to passing some objects by value is to keep allocating new copies from the free store using <var>new</var>--a much more expensive choice. So you shouldn't dismiss value semantics out of hand even for larger object. Such would be the case, for instance, with some algebraic classes that we'll discuss in the following chapter.
<p>In many cases giving a class value semantics does not require any work. As we've seen earlier, the compiler will gladly provide a default copy constructor and the default assignment operator. If the default shallow copy does adequate job of duplicating all the relevant data stored in the object, you shouldn't look any further.
<p>In case shallow copy is not enough, you should provide the appropriate copy constructor and overload the assignment operator. Let's consider a simple example.
<p>Object of class <var>Value</var> represents an integer value. However, the value is not stored as an <var>int</var>--it's stored as a string. What's more, the strings records the "arithmetic history" of the number.
<!-- Code --><table width="100%" cellspacing=10><tr> <td class=codetable>
<pre>class <span class=method>Value</span>
{
public:
Value ()
{
cout << " Default constructor\n";
_numString = new char [1];
_numString [0] = '\0';
}
Value (int i)
{
cout << " Construction/conversion from int " << i << endl;
stringstream buffer;
buffer << i << ends; // terminate string
Init (buffer.str ().c_str ());
Display ();
}
<span class=method>Value</span> (Value const & v)
{
cout << " Copy constructor ( " << v._numString << " )\n";
Init (v._numString);
Display ();
}
Value & <span class=method>operator=</span> (Value const & v)
{
cout << " operator = ( " << v._numString << " )\n";
if (_numString != v._numString)
{
delete _numString;
Init (v._numString);
}
Display ();
return *this;
}
friend Value operator+ (Value const & v1, Value const & v2 );
private:
void Init (char const * buf)
{
int len = strlen (buf);
_numString = new char [len + 1];
strcpy (_numString, buf);
}
void Display ()
{
cout << "\t" << _numString << endl;
}
char * _numString;
};</pre>
</table><!-- End Code -->
<p>A lot of things are happening here. First of all, we have a bunch of constructors. Let's use this opportunity to learn more about various types of constructors.
<ul>
<li>The <i>default constructor</i> with no arguments. You must have it, if you want to create arrays of objects of this class. A default constructor could also be a constructor with the defaults provided for all its arguments. For instance, <var>Value::Value (int i = 0);</var> would work just fine.
<li>The constructor with a single argument.
<br>Unless declared as <var>explicit</var>, such constructor provides <i>implicit conversion</i> from the type of its argument to the type represented by the class. In this case, we are giving the compiler a go-ahead to convert any <var>int</var> to a <var>Value</var>, if such conversion is required to make sense of our code. We'll see examples of such conversions in a moment.
<li>The <i>copy constructor</i>. It takes a (usually <var>const</var>) reference to an object of the same class and clones it.
</ul>
<p>Next, we have the overloading of the assignment operator. By convention it takes a (usually <var>const</var>) reference to an object and copies it into an existing object of the same class. Unlike the copy constructor, the assignment operator must be able to deal with an already initialized object as its target. Special precautions, therefore, are required. We usually have to deallocate whatever resources the target object stores and allocate new resources to hold the copy of the source. However, beware of the following, perfectly legitimate use of assignment:
<!-- Code --><table width="100%" cellspacing=10><tr> <td class=codetable>
<pre>Value val (1);
val = val;</pre>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -