📄 chapter11.html
字号:
<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& 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 << <font color=#004488>"Calling Composite copy-constructor"</font>
<< 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’ve
told the compiler you’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( )</B>. This
constructor is explicitly called in <B>Composite</B>’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( )</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&)
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( )</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’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’re going to pass an object of your class
<I>by value</I>. If that never happens, you don’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">“But,” you say, “if I
don’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?”</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There’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’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’ve explicitly stated that you’re taking
over that job.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’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&); <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&);</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( )<A NAME="Index1948"></A><A NAME="Index1949"></A></B> function
takes a <B>char&</B> as an argument, and the whole point of the function is
to modify its argument by inserting the result of the <B>get( )</B>.
However, when you read code using this function it’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’s probably
safer from a code maintenance standpoint to use pointers when you’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 “address” inside a class; selecting a
member of a class means offsetting into that class. You can’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’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 = &so;
sp->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 ‘<B>*</B>’:</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’s
pointing at, you must dereference it with <B>*</B>. But it’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>–>*</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->*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’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 = &ObjectClass::a;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There is actually no
“address” of <B>ObjectClass::a</B> because you’re just
referring to the class and not an object of that class. Thus,
<B>&ObjectClass::a</B> can be used only as pointer-to-member
syntax.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’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 <iostream>
<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 + -