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

📄 chapter11.html

📁 Thinking in c++ 2nd edition,c++编程思想(第2版)
💻 HTML
📖 第 1 页 / 共 5 页
字号:

<font color=#0000ff>class</font> Composite {
  WithCC withcc; <font color=#009900>// Embedded objects</font>
  WoCC wocc;
<font color=#0000ff>public</font>:
  Composite() : wocc(<font color=#004488>"Composite()"</font>) {}
  <font color=#0000ff>void</font> print(<font color=#0000ff>const</font> string&amp; msg = <font color=#004488>""</font>) <font color=#0000ff>const</font> {
    wocc.print(msg);
  }
};

<font color=#0000ff>int</font> main() {
  Composite c;
  c.print(<font color=#004488>"Contents of c"</font>);
  cout &lt;&lt; <font color=#004488>"Calling Composite copy-constructor"</font>
       &lt;&lt; endl;
  Composite c2 = c;  <font color=#009900>// Calls copy-constructor</font>
  c2.print(<font color=#004488>"Contents of c2"</font>);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The class <B>WithCC</B> contains a
copy-constructor, which simply announces that it has been called, and this
brings up an interesting issue. In the class <B>Composite</B>, an object of
<B>WithCC</B> is created using a default constructor. If there were no
constructors at all in <B>WithCC</B>, the compiler would automatically create a
default constructor<A NAME="Index1934"></A><A NAME="Index1935"></A>, which would
do nothing in this case. However, if you add a copy-constructor, you&#8217;ve
told the compiler you&#8217;re going to handle constructor creation, so it no
longer creates a default constructor for you and will complain unless you
explicitly create a default constructor as was done for
<B>WithCC</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The class <B>WoCC</B> has no
copy-constructor, but its constructor will store a message in an internal
<B>string </B>that can be printed out using <B>print(&#160;)</B>. This
constructor is explicitly called in <B>Composite</B>&#8217;s<B> </B>constructor
initializer list (briefly introduced in Chapter 8 and covered fully in Chapter
14). The reason for this becomes apparent later.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The class <B>Composite</B> has member
objects of both <B>WithCC</B> and <B>WoCC </B>(note the embedded object
<B>wocc</B> is initialized in the constructor-initializer list, as it must be),
and no explicitly defined copy-constructor. However, in <B>main(&#160;)</B> an
object is created using the copy-constructor in the definition:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Composite c2 = c;</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The copy-constructor for <B>Composite
</B>is created automatically by the compiler, and the output of the program
reveals the way that it is created:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Contents of c: Composite()
Calling Composite copy-constructor
WithCC(WithCC&amp;)
Contents of c2: Composite()</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To create a copy-constructor for a class
that uses composition (and
<A NAME="Index1936"></A><A NAME="Index1937"></A><A NAME="Index1938"></A>inheritance,
which is introduced in Chapter 14), the compiler recursively calls the
copy-constructors for all the member objects and base classes. That is, if the
member object also contains another object, its copy-constructor is also called.
So in this case, the compiler calls the copy-constructor for <B>WithCC</B>. The
output shows this constructor being called. Because <B>WoCC</B> has no
copy-constructor, the compiler creates one for it that just performs a bitcopy,
and calls that inside the <B>Composite</B> copy-constructor. The call to
<B>Composite::print(&#160;)</B> in main shows that this happens because the
contents of <B>c2.wocc</B> are identical to the contents of <B>c.wocc</B>. The
process the compiler goes through to synthesize a copy-constructor is called
<I>memberwise
initialization<A NAME="Index1939"></A><A NAME="Index1940"></A><A NAME="Index1941"></A></I>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It&#8217;s always best to create your own
copy-constructor instead of letting the compiler do it for you. This guarantees
that it will be under your
control.</FONT><A NAME="_Toc312373966"></A><A NAME="_Toc472654941"></A><BR></P></DIV>
<A NAME="Heading340"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Alternatives to
copy-construction<BR><A NAME="Index1942"></A><A NAME="Index1943"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">At this point your head may be swimming,
and you might be wondering how you could have possibly written a working class
without knowing about the copy-constructor. But remember: You need a
copy-constructor only if you&#8217;re going to pass an object of your class
<I>by value</I>. If that never happens, you don&#8217;t need a
copy-constructor.</FONT><BR></P></DIV>
<A NAME="Heading341"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Preventing pass-by-value</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">&#8220;But,&#8221; you say, &#8220;if I
don&#8217;t make a copy-constructor, the compiler will create one for me. So how
do I know that an object will never be passed by value?&#8221;</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There&#8217;s a simple technique for
preventing pass-by-value: declare a <A NAME="Index1944"></A><B>private</B>
copy-constructor<A NAME="Index1945"></A>. You don&#8217;t even need to create a
definition, unless one of your member functions or a <B>friend</B> function
needs to perform a pass-by-value. If the user tries to pass or return the object
by value, the compiler will produce an error message because the
copy-constructor is <B>private</B>. It can no longer create a default
copy-constructor because you&#8217;ve explicitly stated that you&#8217;re taking
over that job.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here&#8217;s an example:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C11:NoCopyConstruction.cpp</font>
<font color=#009900>// Preventing copy-construction</font>

<font color=#0000ff>class</font> NoCC {
  <font color=#0000ff>int</font> i;
  NoCC(<font color=#0000ff>const</font> NoCC&amp;); <font color=#009900>// No definition</font>
<font color=#0000ff>public</font>:
  NoCC(<font color=#0000ff>int</font> ii = 0) : i(ii) {}
};

<font color=#0000ff>void</font> f(NoCC);

<font color=#0000ff>int</font> main() {
  NoCC n;
<font color=#009900>//! f(n); // Error: copy-constructor called</font>
<font color=#009900>//! NoCC n2 = n; // Error: c-c called</font>
<font color=#009900>//! NoCC n3(n); // Error: c-c called</font>
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice the use of the more general form
</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>NoCC(<font color=#0000ff>const</font> NoCC&amp;);</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">using the <B>const</B>.</FONT><BR></P></DIV>
<A NAME="Heading342"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Functions that modify outside
objects<BR><A NAME="Index1946"></A><A NAME="Index1947"></A></H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Reference syntax is nicer to use than
pointer syntax, yet it clouds the meaning for the reader. For example, in the
iostreams library one overloaded version of the
<B>get(&#160;)<A NAME="Index1948"></A><A NAME="Index1949"></A></B> function
takes a <B>char&amp;</B> as an argument, and the whole point of the function is
to modify its argument by inserting the result of the <B>get(&#160;)</B>.
However, when you read code using this function it&#8217;s not immediately
obvious to you that the outside object is being modified:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>char</font> c;
cin.get(c); </PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Instead, the function call looks like a
pass-by-value, which suggests the outside object is <I>not</I>
modified.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because of this, it&#8217;s probably
safer from a code maintenance standpoint to use pointers when you&#8217;re
passing the address of an argument to modify. If you <I>always</I> pass
addresses as <B>const</B> references
<A NAME="Index1950"></A><A NAME="Index1951"></A><A NAME="Index1952"></A><I>except</I>
when you intend to modify the outside object via the address, where you pass by
non-<B>const</B> pointer, then your code is far easier for the reader to
follow.</FONT><A NAME="_Toc305593230"></A><A NAME="_Toc305628702"></A><A NAME="_Toc312373967"></A><A NAME="_Toc472654942"></A><BR></P></DIV>
<A NAME="Heading343"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Pointers to members<BR><A NAME="Index1953"></A><A NAME="Index1954"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A pointer is a variable that holds the
address of some location. You can change what a pointer selects at runtime, and
the destination of the pointer can be either data or a function. The C++
<I>pointer-to-member</I> follows this same concept, except that what it selects
is a location inside a class. The dilemma here is that a pointer needs an
address, but there is no &#8220;address&#8221; inside a class; selecting a
member of a class means offsetting into that class. You can&#8217;t produce an
actual address until you combine that offset with the starting address of a
particular object. The syntax of pointers to members requires that you select an
object at the same time you&#8217;re dereferencing the pointer to
member.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To understand this syntax, consider a
simple structure, with a pointer <B>sp</B> and an object <B>so</B> for this
structure. You can select members with the syntax shown:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C11:SimpleStructure.cpp</font>
<font color=#0000ff>struct</font> Simple { <font color=#0000ff>int</font> a; };
<font color=#0000ff>int</font> main() {
  Simple so, *sp = &amp;so;
  sp-&gt;a;
  so.a;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now suppose you have an ordinary pointer
to an integer, <B>ip</B>. To access what <B>ip</B> is pointing to, you
dereference the pointer with a &#8216;<B>*</B>&#8217;:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>*ip = 4;</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Finally, consider what happens if you
have a pointer that happens to point to something inside a class object, even if
it does in fact represent an offset into the object. To access what it&#8217;s
pointing at, you must dereference it with <B>*</B>. But it&#8217;s an offset
into an object, so you must also refer to that particular object. Thus, the
<B>*</B> is combined with the object dereference. So the new syntax
becomes<A NAME="Index1955"></A> <B>&#8211;&gt;*</B> for a pointer to an object,
and <A NAME="Index1956"></A><B>.*</B> for the object or a reference, like
this:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>objectPointer-&gt;*pointerToMember = 47;
object.*pointerToMember = 47;</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now, what is the syntax for defining
<B>pointerToMember</B>? Like any pointer, you have to say what type it&#8217;s
pointing at, and you use a <B>*</B> in the definition. The only difference is
that you must say what class of objects this pointer-to-member is used with. Of
course, this is accomplished with the name of the class and the scope resolution
operator. Thus,</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>int</font> ObjectClass::*pointerToMember;</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">defines a pointer-to-member variable
called <B>pointerToMember</B> that points to any <B>int</B> inside
<B>ObjectClass</B>. You can also initialize the pointer-to-member when you
define it (or at any other time):</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>int</font> ObjectClass::*pointerToMember = &amp;ObjectClass::a;</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There is actually no
&#8220;address&#8221; of <B>ObjectClass::a</B> because you&#8217;re just
referring to the class and not an object of that class. Thus,
<B>&amp;ObjectClass::a</B> can be used only as pointer-to-member
syntax.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here&#8217;s an example that shows how to
create and use pointers to data members:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C11:PointerToMemberData.cpp</font>
#include &lt;iostream&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>class</font> Data {
<font color=#0000ff>public</font>:  
  <font color=#0000ff>int</font> a, b, c; 
  <font color=#0000ff>void</font> pri

⌨️ 快捷键说明

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