📄 chapter05.html
字号:
<font color=#0000ff>friend</font> <font color=#0000ff>void</font> Y::f(X*); <font color=#009900>// Struct member friend</font>
<font color=#0000ff>friend</font> <font color=#0000ff>struct</font> Z; <font color=#009900>// Entire struct is a friend</font>
<font color=#0000ff>friend</font> <font color=#0000ff>void</font> h();
};
<font color=#0000ff>void</font> X::initialize() {
i = 0;
}
<font color=#0000ff>void</font> g(X* x, <font color=#0000ff>int</font> i) {
x->i = i;
}
<font color=#0000ff>void</font> Y::f(X* x) {
x->i = 47;
}
<font color=#0000ff>struct</font> Z {
<font color=#0000ff>private</font>:
<font color=#0000ff>int</font> j;
<font color=#0000ff>public</font>:
<font color=#0000ff>void</font> initialize();
<font color=#0000ff>void</font> g(X* x);
};
<font color=#0000ff>void</font> Z::initialize() {
j = 99;
}
<font color=#0000ff>void</font> Z::g(X* x) {
x->i += j;
}
<font color=#0000ff>void</font> h() {
X x;
x.i = 100; <font color=#009900>// Direct data manipulation</font>
}
<font color=#0000ff>int</font> main() {
X x;
Z z;
z.g(&x);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>struct Y</B> has a member function
<B>f( )</B> that will modify an object of type <B>X</B>. This is a bit of a
conundrum because the C++ compiler requires you to declare everything before you
can refer to it, so <B>struct Y</B> must be declared before its member
<B>Y::f(X*)</B> can be declared as a friend in <B>struct X</B>. But for
<B>Y::f(X*)</B> to be declared, <B>struct X</B> must be declared
first!</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s the solution. Notice that
<B>Y::f(X*)</B> takes the <I>address</I> of an <B>X</B>
object<A NAME="Index1212"></A><A NAME="Index1213"></A>. This is critical because
the compiler always knows how to pass an address, which is of a fixed size
regardless of the object being passed, even if it doesn’t have full
information about the size of the type. If you try to pass the whole object,
however, the compiler must see the entire structure definition of <B>X</B>, to
know the size and how to pass it, before it allows you to declare a function
such as <B>Y::g(X)</B>. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">By passing the address of an <B>X</B>,
the compiler allows you to make an <I>incomplete type specification</I>
<A NAME="Index1214"></A><A NAME="Index1215"></A><A NAME="Index1216"></A>of
<B>X</B> prior to declaring <B>Y::f(X*)</B>. This is accomplished in the
declaration: <A NAME="Index1217"></A><A NAME="Index1218"></A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>struct</font> X;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This declaration simply tells the
compiler there’s a <B>struct</B> by that name, so it’s OK to refer
to it as long as you don’t require any more knowledge than the
name.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now, in <B>struct X</B>, the function
<B>Y::f(X*)</B> can be declared as a <B>friend</B> with no problem. If you tried
to declare it before the compiler had seen the full specification for <B>Y</B>,
it would have given you an error. This is a safety feature to ensure consistency
and eliminate bugs.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice the two other <B>friend</B>
functions. The first declares an ordinary global function <B>g( )</B> as a
<B>friend</B>. But <B>g( )</B> has not been previously declared at the
global scope! It turns out that <B>friend</B> can be used this way to
simultaneously declare the function <I>and</I> give it <B>friend</B> status.
This extends to entire structures: </FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>friend</font> <font color=#0000ff>struct</font> Z;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">is an incomplete type specification for
<B>Z</B>, and it gives the entire structure <B>friend</B>
status.</FONT><A NAME="_Toc312373841"></A><A NAME="_Toc472654840"></A><BR></P></DIV>
<A NAME="Heading213"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Nested friends<BR><A NAME="Index1219"></A><A NAME="Index1220"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Making a structure nested doesn’t
automatically give it access to <B>private</B> members. To accomplish this, you
must follow a particular form: first, declare (without defining) the nested
structure, then declare it as a <B>friend</B>, and finally define the structure.
The structure definition must be separate from the <B>friend</B> declaration,
otherwise it would be seen by the compiler as a non-member. Here’s an
example:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C05:NestFriend.cpp</font>
<font color=#009900>// Nested friends</font>
#include <iostream>
#include <cstring> <font color=#009900>// memset()</font>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>const</font> <font color=#0000ff>int</font> sz = 20;
<font color=#0000ff>struct</font> Holder {
<font color=#0000ff>private</font>:
<font color=#0000ff>int</font> a[sz];
<font color=#0000ff>public</font>:
<font color=#0000ff>void</font> initialize();
<font color=#0000ff>struct</font> Pointer;
<font color=#0000ff>friend</font> <font color=#0000ff>struct</font> Pointer;
<font color=#0000ff>struct</font> Pointer {
<font color=#0000ff>private</font>:
Holder* h;
<font color=#0000ff>int</font>* p;
<font color=#0000ff>public</font>:
<font color=#0000ff>void</font> initialize(Holder* h);
<font color=#009900>// Move around in the array:</font>
<font color=#0000ff>void</font> next();
<font color=#0000ff>void</font> previous();
<font color=#0000ff>void</font> top();
<font color=#0000ff>void</font> end();
<font color=#009900>// Access values:</font>
<font color=#0000ff>int</font> read();
<font color=#0000ff>void</font> set(<font color=#0000ff>int</font> i);
};
};
<font color=#0000ff>void</font> Holder::initialize() {
memset(a, 0, sz * <font color=#0000ff>sizeof</font>(<font color=#0000ff>int</font>));
}
<font color=#0000ff>void</font> Holder::Pointer::initialize(Holder* rv) {
h = rv;
p = rv->a;
}
<font color=#0000ff>void</font> Holder::Pointer::next() {
<font color=#0000ff>if</font>(p < &(h->a[sz - 1])) p++;
}
<font color=#0000ff>void</font> Holder::Pointer::previous() {
<font color=#0000ff>if</font>(p > &(h->a[0])) p--;
}
<font color=#0000ff>void</font> Holder::Pointer::top() {
p = &(h->a[0]);
}
<font color=#0000ff>void</font> Holder::Pointer::end() {
p = &(h->a[sz - 1]);
}
<font color=#0000ff>int</font> Holder::Pointer::read() {
<font color=#0000ff>return</font> *p;
}
<font color=#0000ff>void</font> Holder::Pointer::set(<font color=#0000ff>int</font> i) {
*p = i;
}
<font color=#0000ff>int</font> main() {
Holder h;
Holder::Pointer hp, hp2;
<font color=#0000ff>int</font> i;
h.initialize();
hp.initialize(&h);
hp2.initialize(&h);
<font color=#0000ff>for</font>(i = 0; i < sz; i++) {
hp.set(i);
hp.next();
}
hp.top();
hp2.end();
<font color=#0000ff>for</font>(i = 0; i < sz; i++) {
cout << <font color=#004488>"hp = "</font> << hp.read()
<< <font color=#004488>", hp2 = "</font> << hp2.read() << endl;
hp.next();
hp2.previous();
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Once <B>Pointer </B>is declared, it is
granted access to the private members of <B>Holder</B> by
saying:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>friend</font> <U><font color=#0000ff>struct</font> </U>Pointer;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>struct Holder</B> contains an
array of <B>int</B>s and the <B>Pointer</B> allows you to access them. Because
<B>Pointer</B> is strongly associated with <B>Holder</B>, it’s sensible to
make it a member structure of <B>Holder</B>. But because <B>Pointer</B> is a
separate class from <B>Holder</B>, you can make more than one of them in
<B>main( )</B> and use them to select different parts of the array.
<B>Pointer</B> is a structure instead of a raw C pointer, so you can guarantee
that it will always safely point inside the <B>Holder</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The Standard C library function
<A NAME="Index1221"></A><B>memset( )</B> (in
<A NAME="Index1222"></A><A NAME="Index1223"></A><B><cstring></B>) is used
for convenience in the program above. It sets all memory starting at a
particular address (the first argument) to a particular value (the second
argument) for <B>n</B> bytes past the starting address (<B>n</B> is the third
argument). Of course, you could have simply used a loop to iterate through all
the memory, but <B>memset( )</B> is available, well-tested (so it’s
less likely you’ll introduce an error), and probably more efficient than
if you coded it by
hand.</FONT><A NAME="_Toc312373842"></A><A NAME="_Toc472654841"></A><BR></P></DIV>
<A NAME="Heading214"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Is it pure?</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The class definition gives you an audit
trail, so you can see from looking at the class which functions have permission
to modify the private parts of the class. If a function is a <B>friend</B>, it
means that it isn’t a member, but you want to give permission to modify
private data anyway, and it must be listed in the class definition so everyone
can see that it’s one of the privileged functions. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><A NAME="Index1224"></A><A NAME="Index1225"></A><A NAME="Index1226"></A><A NAME="Index1227"></A><A NAME="Index1228"></A><FONT FACE="Georgia">C++
is a hybrid object-oriented language, not a pure one, and <B>friend</B> was
added to get around practical problems that crop up. It’s fine to point
out that this makes the language less “pure,” because C++ <I>is</I>
designed to be pragmatic, not to aspire to an abstract
ideal.</FONT><A NAME="_Toc312373843"></A><A NAME="_Toc472654842"></A><BR></P></DIV>
<A NAME="Heading215"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Object layout</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Chapter 4 stated that a <B>struct</B>
written for a C compiler and later compiled with C++ would be unchanged. This
referred primarily to the object layout of the <B>struct</B>, that is, where the
storage for the individual variables is positioned in the memory allocated for
the object. If the C++ compiler changed the layout
<A NAME="Index1229"></A><A NAME="Index1230"></A><A NAME="Index1231"></A>of C
<B>struct</B>s, then any C code you wrote that inadvisably took advantage of
knowledge of the positions of variables in the <B>struct</B> would
break.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you start using access specifiers,
however, you’ve moved completely into the C++ realm, and things change a
bit. Within a particular “<A NAME="Index1232"></A>access block” (a
group of declarations delimited by access specifiers), the variables are
guaranteed to be laid out contiguously, as in C. However, the access blocks may
not appear in the object in the order that you declare them. Although the
compiler will <I>usually</I> lay the blocks out exactly as you see them, there
is no rule about it, because a particular machine architecture and/or operating
environment may have explicit support for
<A NAME="Index1233"></A><A NAME="Index1234"></A><B>private</B> and
<A NAME="Index1235"></A><A NAME="Index1236"></A><B>protected</B> that might
require those blocks to be placed in special memory locations. The language
specification doesn’t want to restrict this kind of
advantage.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Access specifiers are part of the
structure and don’t affect the objects created from the structure. All of
the access specification information disappears before the program is run;
generally this happens during compilation. In a running program, objects become
“regions of storage” and nothing more. If you really want to, you
can break all the rules and access the memory directly, as you can in C. C++ is
not designed to prevent you from doing unwise things. It just provides you with
a much easier, highly desirable alternative.</FONT><BR></P></DIV>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -