📄 chapter11.html
字号:
h2.print(<font color=#004488>"h2 after call to f()"</font>);
out << <font color=#004488>"Call f(), no return value"</font> << endl;
f(h);
out << <font color=#004488>"After call to f()"</font> << endl;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are a number of new twists thrown
in here so you can get a better idea of what’s happening. First, the
<B>string</B> <B>name</B> acts as an object identifier when information about
that object is printed. In the constructor, you can put an identifier string
(usually the name of the object) that is copied to <B>name</B> using the
<B>string </B>constructor. The default <B>= "" </B>creates an empty
<B>string</B>. The constructor increments the <B>objectCount</B> as before, and
the destructor decrements it.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Next is the copy-constructor,
<B>HowMany2(const HowMany2&)</B>. The copy-constructor can create a new
object only from an existing one, so the existing object’s name is copied
to <B>name</B>, followed by the word “copy” so you can see where it
came from. If you look closely, you’ll see that the call
<B>name(h.name)</B> in the constructor initializer list is actually calling the
<B>string</B> copy-constructor.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Inside the copy-constructor, the object
count is incremented just as it is inside the normal constructor. This means
you’ll now get an accurate object count when passing and returning by
value.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>print( )</B> function has
been modified to print out a message, the object identifier, and the object
count. It must now access the <B>name</B> data of a particular object, so it can
no longer be a <B>static</B> member
function<A NAME="Index1916"></A><A NAME="Index1917"></A><A NAME="Index1918"></A>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Inside <B>main( )</B>, you can see
that a second call to <B>f( )</B> has been added. However, this call uses
the common C approach of ignoring the return value. But now that you know how
the value is returned (that is, code <I>inside</I> the function handles the
return process, putting the result in a destination whose address is passed as a
hidden argument), you might wonder what happens when the return value is
ignored. The output of the program will throw some illumination on
this.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Before showing the output, here’s a
little program that uses iostreams to add line numbers to any
file:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C11:Linenum.cpp</font>
<font color=#009900>//{T} Linenum.cpp</font>
<font color=#009900>// Add line numbers</font>
#include <font color=#004488>"../require.h"</font>
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#include <cmath>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>int</font> main(<font color=#0000ff>int</font> argc, <font color=#0000ff>char</font>* argv[]) {
requireArgs(argc, 1, <font color=#004488>"Usage: linenum file\n"</font>
<font color=#004488>"Adds line numbers to file"</font>);
ifstream in(argv[1]);
assure(in, argv[1]);
string line;
vector<string> lines;
<font color=#0000ff>while</font>(getline(in, line)) <font color=#009900>// Read in entire file</font>
lines.push_back(line);
<font color=#0000ff>if</font>(lines.size() == 0) <font color=#0000ff>return</font> 0;
<font color=#0000ff>int</font> num = 0;
<font color=#009900>// Number of lines in file determines width:</font>
<font color=#0000ff>const</font> <font color=#0000ff>int</font> width =
<font color=#0000ff>int</font>(log10((<font color=#0000ff>double</font>)lines.size())) + 1;
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < lines.size(); i++) {
cout.setf(ios::right, ios::adjustfield);
cout.width(width);
cout << ++num << <font color=#004488>") "</font> << lines[i] << endl;
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The entire file is read into a
<B>vector<string></B>, using the same code that you’ve seen earlier
in the book. When printing the line numbers, we’d like all the lines to be
aligned with each other, and this requires adjusting for the number of lines in
the file so that the width allowed for the line numbers is consistent. We can
easily determine the number of lines using <B>vector::size( )</B>, but what
we really need to know is whether there are more than 10 lines, 100 lines, 1,000
lines, etc. If you take the <A NAME="Index1919"></A>logarithm, base 10, of the
number of lines in the file, truncate it to an <B>int</B> and add one to the
value, you’ll find out the maximum width that your line count will
be.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You’ll notice a couple of strange
calls inside the <B>for</B> loop:
<A NAME="Index1920"></A><A NAME="Index1921"></A><B>setf( )</B> and
<A NAME="Index1922"></A><A NAME="Index1923"></A><B>width( )</B>. These are
<B>ostream</B> calls that allow you to control, in this case, the justification
and width of the output. However, they must be called each time a line is output
and that is why they are inside the <B>for</B> loop. Volume 2 of this book has
an entire chapter explaining iostreams that will tell you more about these calls
as well as other ways to control iostreams.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When <B>Linenum.cpp</B> is applied to
<B>HowMany2.out</B>, the result is</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE> 1) HowMany2()
2) h: objectCount = 1
3) Entering f()
4) HowMany2(<font color=#0000ff>const</font> HowMany2&)
5) h copy: objectCount = 2
6) x argument inside f()
7) h copy: objectCount = 2
8) Returning from f()
9) HowMany2(<font color=#0000ff>const</font> HowMany2&)
10) h copy copy: objectCount = 3
11) ~HowMany2()
12) h copy: objectCount = 2
13) h2 after call to f()
14) h copy copy: objectCount = 2
15) Call f(), no <font color=#0000ff>return</font> value
16) HowMany2(<font color=#0000ff>const</font> HowMany2&)
17) h copy: objectCount = 3
18) x argument inside f()
19) h copy: objectCount = 3
20) Returning from f()
21) HowMany2(<font color=#0000ff>const</font> HowMany2&)
22) h copy copy: objectCount = 4
23) ~HowMany2()
24) h copy: objectCount = 3
25) ~HowMany2()
26) h copy copy: objectCount = 2
27) After call to f()
28) ~HowMany2()
29) h copy copy: objectCount = 1
30) ~HowMany2()
31) h: objectCount = 0
</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As you would expect, the first
thing that happens is that the normal constructor is called for <B>h</B>, which
increments the object count to one. But then, as <B>f( )</B> is entered,
the copy-constructor is quietly called by the compiler to perform the
pass-by-value. A new object is created, which is the copy of <B>h</B> (thus the
name “h copy”) inside the function frame of <B>f( )</B>, so the
object count becomes two, courtesy of the copy-constructor.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Line eight indicates the beginning of the
return from <B>f( )</B>. But before the local variable “h copy”
can be destroyed (it goes out of scope at the end of the function), it must be
copied into the return value, which happens to be <B>h2</B>. A previously
unconstructed object (<B>h2</B>) is created from an existing object (the local
variable inside <B>f( )</B>), so of course the copy-constructor is used
again in line nine. Now the name becomes “h copy copy” for
<B>h2</B>’s identifier because it’s being copied from the copy that
is the local object inside <B>f( )</B>. After the object is returned, but
before the function ends, the object count becomes temporarily three, but then
the local object “h copy” is destroyed. After the call to
<B>f( )</B> completes in line 13, there are only two objects, <B>h</B> and
<B>h2</B>, and you can see that <B>h2</B> did indeed end up as “h copy
copy.”</FONT><BR></P></DIV>
<A NAME="Heading338"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Temporary objects<BR><A NAME="Index1924"></A><A NAME="Index1925"></A></H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Line 15 begins the call to <B>f(h)</B>,
this time ignoring the return value. You can see in line 16 that the
copy-constructor is called just as before to pass the argument in. And also, as
before, line 21 shows the copy-constructor is called for the return value. But
the copy-constructor must have an address to work on as its destination (a
<B>this</B> <A NAME="Index1926"></A>pointer). Where does this address come
from?</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It turns out the compiler can create a
temporary object whenever it needs one to properly evaluate an expression. In
this case it creates one you don’t even see to act as the destination for
the ignored return value of <B>f( )</B>. The lifetime of this temporary
object <A NAME="Index1927"></A>is as short as possible so the landscape
doesn’t get cluttered up with temporaries waiting to be destroyed and
taking up valuable resources. In some cases, the temporary might immediately be
passed to another function, but in this case it isn’t needed after the
function call, so as soon as the function call ends by calling the destructor
for the local object (lines 23 and 24), the temporary object is destroyed (lines
25 and 26).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Finally, in lines 28-31, the <B>h2</B>
object is destroyed, followed by <B>h</B>, and the object count goes correctly
back to
zero.</FONT><A NAME="_Toc312373965"></A><A NAME="_Toc472654940"></A><BR></P></DIV>
<A NAME="Heading339"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Default
copy-constructor<BR><A NAME="Index1928"></A><A NAME="Index1929"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because the copy-constructor implements
pass and return by value, it’s important that the compiler creates one for
you in the case of simple structures – effectively, the same thing it does
in C. However, all you’ve seen so far is the default primitive behavior: a
bitcopy.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When more complex types are involved, the
C++ compiler will still automatically create a copy-constructor if you
don’t make one. Again, however, a bitcopy
<A NAME="Index1930"></A>doesn’t make sense, because it doesn’t
necessarily implement the proper meaning.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s an example to show the more
intelligent approach the compiler takes. Suppose you create a new class composed
of objects of several existing classes. This is called, appropriately enough,
<I>composition<A NAME="Index1931"></A><A NAME="Index1932"></A><A NAME="Index1933"></A></I>,
and it’s one of the ways you can make new classes from existing classes.
Now take the role of a naive user who’s trying to solve a problem quickly
by creating a new class this way. You don’t know about copy-constructors,
so you don’t create one. The example demonstrates what the compiler does
while creating the default copy-constructor for your new class:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C11:DefaultCopyConstructor.cpp</font>
<font color=#009900>// Automatic creation of the copy-constructor</font>
#include <iostream>
#include <string>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>class</font> WithCC { <font color=#009900>// With copy-constructor</font>
<font color=#0000ff>public</font>:
<font color=#009900>// Explicit default constructor required:</font>
WithCC() {}
WithCC(<font color=#0000ff>const</font> WithCC&) {
cout << <font color=#004488>"WithCC(WithCC&)"</font> << endl;
}
};
<font color=#0000ff>class</font> WoCC { <font color=#009900>// Without copy-constructor</font>
string id;
<font color=#0000ff>public</font>:
WoCC(<font color=#0000ff>const</font> string& ident = <font color=#004488>""</font>) : id(ident) {}
<font color=#0000ff>void</font> print(<font color=#0000ff>const</font> string& msg = <font color=#004488>""</font>) <font color=#0000ff>const</font> {
<font color=#0000ff>if</font>(msg.size() != 0) cout << msg << <font color=#004488>": "</font>;
cout << id << endl;
}
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -