📄 chapter05.html
字号:
casting<A NAME="Index1267"></A>. In all these situations, it’s valuable to
have the actual structure compiled inside an implementation file rather than
exposed in a header
file.</FONT><A NAME="_Toc312373849"></A><A NAME="_Toc472654848"></A><BR></P></DIV>
<A NAME="Heading221"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Reducing recompilation</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The project manager in your programming
environment will cause a recompilation of a file if that file is touched (that
is, modified) <I>or</I> if another file it’s dependent upon – that
is, an included header file – is touched. This means that any time you
make a change to a class, whether it’s to the public interface or to the
private member declarations, you’ll force a recompilation of anything that
includes that header file. This is often referred to as the
<A NAME="Index1268"></A><A NAME="Index1269"></A><A NAME="Index1270"></A><I>fragile
base-class problem</I>. For a large project in its early stages this can be very
unwieldy because the underlying implementation may change often; if the project
is very big, the time for compiles can prohibit rapid
turnaround.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The technique to solve this is sometimes
called <I>handle classes <A NAME="Index1271"></A></I>or the “Cheshire
cat”<A NAME="Index1272"></A><A NAME="Index1273"></A></FONT><A NAME="fnB37" HREF="#fn37">[37]</A><A NAME="Index1274"></A><A NAME="Index1275"></A><FONT FACE="Georgia">
– everything about the implementation disappears except for a single
pointer, the “smile.” The pointer refers to a structure whose
definition is in the implementation file along with all the member function
definitions. Thus, as long as the interface is unchanged, the header file is
untouched. The implementation can change at will, and only the implementation
file needs to be recompiled and relinked with the project.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s a simple example
demonstrating the technique. The header file contains only the public interface
and a single pointer of an incompletely specified class:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C05:Handle.h</font>
<font color=#009900>// Handle classes</font>
#ifndef HANDLE_H
#define HANDLE_H
<font color=#0000ff>class</font> Handle {
<font color=#0000ff>struct</font> Cheshire; <font color=#009900>// Class declaration only</font>
Cheshire* smile;
<font color=#0000ff>public</font>:
<font color=#0000ff>void</font> initialize();
<font color=#0000ff>void</font> cleanup();
<font color=#0000ff>int</font> read();
<font color=#0000ff>void</font> change(<font color=#0000ff>int</font>);
};
#endif <font color=#009900>// HANDLE_H ///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is all the client programmer is able
to see. The line </FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>struct</font> Cheshire;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">is an <I>incomplete type
specification</I>
<A NAME="Index1276"></A><A NAME="Index1277"></A><A NAME="Index1278"></A>or a
<I>class declaration</I> <A NAME="Index1279"></A><A NAME="Index1280"></A>(A
<I>class definition</I> <A NAME="Index1281"></A><A NAME="Index1282"></A>includes
the body of the class.) It tells the compiler that <B>Cheshire</B> is a
structure name, but it doesn’t give any details about the <B>struct</B>.
This is only enough information to create a pointer to the <B>struct</B>; you
can’t create an object until the structure body has been provided. In this
technique, that structure body is hidden away in the implementation
file:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C05:Handle.cpp {O}</font>
<font color=#009900>// Handle implementation</font>
#include <font color=#004488>"Handle.h"</font>
#include <font color=#004488>"../require.h"</font>
<font color=#009900>// Define Handle's implementation:</font>
<font color=#0000ff>struct</font> Handle::Cheshire {
<font color=#0000ff>int</font> i;
};
<font color=#0000ff>void</font> Handle::initialize() {
smile = <font color=#0000ff>new</font> Cheshire;
smile->i = 0;
}
<font color=#0000ff>void</font> Handle::cleanup() {
<font color=#0000ff>delete</font> smile;
}
<font color=#0000ff>int</font> Handle::read() {
<font color=#0000ff>return</font> smile->i;
}
<font color=#0000ff>void</font> Handle::change(<font color=#0000ff>int</font> x) {
smile->i = x;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>Cheshire</B> is a nested structure, so
it must be defined with scope
resolution<A NAME="Index1283"></A><A NAME="Index1284"></A>:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>struct</font> Handle::Cheshire {</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>Handle::initialize( )</B>,
storage is allocated for a <B>Cheshire</B> structure, and in
<B>Handle::cleanup( )</B> this storage is released. This storage is used in
lieu of all the data elements you’d normally put into the <B>private</B>
section of the class. When you compile <B>Handle.cpp</B>, this structure
definition is hidden away in the object file where no one can see it. If you
change the elements of <B>Cheshire</B>, the only file that must be recompiled is
<B>Handle.cpp</B> because the header file is untouched.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The use of <B>Handle</B> is like the use
of any class: include the header, create objects, and send
messages.</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C05:UseHandle.cpp</font>
<font color=#009900>//{L} Handle</font>
<font color=#009900>// Use the Handle class</font>
#include <font color=#004488>"Handle.h"</font>
<font color=#0000ff>int</font> main() {
Handle u;
u.initialize();
u.read();
u.change(1);
u.cleanup();
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The only thing the client programmer can
access is the public interface, so as long as the implementation is the only
thing that changes, the file above never needs recompilation. Thus, although
this isn’t perfect implementation hiding, it’s a big
improvement.</FONT><A NAME="_Toc312373850"></A><A NAME="_Toc472654849"></A><BR></P></DIV>
<A NAME="Heading222"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Summary</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Access control in C++ gives valuable
control to the creator of a class. The users of the class can clearly see
exactly what they can use and what to ignore. More important, though, is the
ability to ensure that no client programmer becomes dependent on any part of the
underlying implementation of a class. If you know this as the creator of the
class, you can change the underlying implementation with the knowledge that no
client programmer will be affected by the changes because they can’t
access that part of the class.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you have the ability to change the
underlying implementation, you can not only improve your design
<A NAME="Index1285"></A>at some later time, but you also have the freedom to
make mistakes<A NAME="Index1286"></A>. No matter how carefully you plan and
design, you’ll make mistakes. Knowing that it’s relatively safe to
make these mistakes means you’ll be more experimental, you’ll learn
faster, and you’ll finish your project sooner.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The public interface to a class is what
the client programmer <I>does</I> see, so that is the most important part of the
class to get “right” during analysis and design. But even that
allows you some leeway for change. If you don’t get the interface right
the first time, you can <I>add</I> more functions<A NAME="Index1287"></A>, as
long as you don’t remove any that client programmers have already used in
their
code.</FONT><A NAME="_Toc312373851"></A><A NAME="_Toc472654850"></A><BR></P></DIV>
<A NAME="Heading223"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Exercises</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia" SIZE=2>Solutions to selected exercises
can be found in the electronic document <I>The Thinking in C++ Annotated
Solution Guide</I>, available for a small fee from
www.BruceEckel.com.</FONT><BR></P></DIV>
<OL>
<LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Create a class with
<B>public</B>, <B>private</B>, and <B>protected</B> data members and function
members. Create an object of this class and see what kind of compiler messages
you get when you try to access all the class
members.</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Write a
<B>struct</B> called <B>Lib</B> that contains three <B>string</B> objects
<B>a</B>, <B>b,</B> and <B>c</B>. In <B>main( )</B> create a <B>Lib</B>
object called <B>x </B>and assign to <B>x.a</B>, <B>x.b</B>, and <B>x.c</B>.
Print out the values. Now replace <B>a</B>, <B>b,</B> and <B>c</B> with an array
of <B>string s[3]</B>. Show that your code in <B>main( )</B> breaks as a
result of the change.<B> </B>Now create a <B>class</B> called <B>Libc</B>, with
<B>private</B> <B>string</B> objects <B>a</B>, <B>b,</B> and <B>c</B>, and
member functions <B>seta( )</B>, <B>geta( )</B>, <B>setb( )</B>,
<B>getb( )</B>, <B>setc( )</B>, and <B>getc( )</B> to set and get
the values. Write <B>main( )</B> as before. Now change the <B>private</B>
<B>string</B> objects <B>a</B>, <B>b,</B> and <B>c</B> to a <B>private </B>array
of <B>string s[3]</B>. Show that the code in <B>main( )</B> does <I>not</I>
break as a result of the
change.</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Create a
class and a global <B>friend</B> function that manipulates the <B>private</B>
data in the
class.</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Write two
classes, each of which has a member function that takes a pointer to an object
of the other class. Create instances of both objects in <B>main( )</B> and
call the aforementioned member function in each
class.</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Create three
classes. The first class contains <B>private </B>data, and grants friendship to
the entire second class and to a member function of the third class. In
<B>main( )</B>, demonstrate that all of these work
correctly.</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Create a
<B>Hen</B> class. Inside this, nest a <B>Nest</B> class. Inside <B>Nest</B>,
place an <B>Egg</B> class. Each class should have a <B>display( )</B>
member function. In <B>main( )</B>, create an instance of each class and
call the <B>display( )</B> function for each
one.</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Modify Exercise
6 so that <B>Nest</B> and <B>Egg</B> each contain <B>private</B> data. Grant
friendship to allow the enclosing classes access to this <B>private</B>
data.</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Create a class
with data members distributed among numerous <B>public</B>, <B>private,</B> and
<B>protected</B> sections. Add a member function <B>showMap( )</B> that
prints the names of each of these data members and their addresses. If possible,
compile and run this program on more than one compiler and/or computer and/or
operating system to see if there are layout differences in the
object.</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Copy the
implementation and test files for <B>Stash </B>in Chapter 4 so that you can
compile and test <B>Stash.h</B> in this
chapter.</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Place
objects of the <B>Hen</B> class from Exercise 6 in a <B>Stash</B>. Fetch them
out and print them (if you have not already done so, you will need to add
<B>Hen::print( )</B>).</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Copy
the implementation and test files for <B>Stack </B>in Chapter 4 so that you can
compile and test <B>Stack2.h</B> in this
chapter.</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Place
objects of the <B>Hen</B> class from Exercise 6 in a <B>Stack</B>. Fetch them
out and print them (if you have not already done so, you will need to add
<B>Hen::print( )</B>).</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Modify
<B>Cheshire</B> in <B>Handle.cpp</B>, and verify that your project manager
recompiles and relinks only this file, but doesn’t recompile
<B>UseHandle.cpp</B>.</FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Create
a <B>StackOfInt</B> class (a stack that holds <B>int</B>s) using the
“Cheshire cat” technique that hides the low-level data structure you
use to store the elements in a class called <B>StackImp</B>. Implement two
versions of <B>StackImp</B>: one that uses a fixed-length array of <B>int</B>,
and one that uses a <B>vector<int></B>. Have a preset maximum size for the
stack so you don’t have to worry about expanding the array in the first
version. Note that the <B>StackOfInt.h</B> class doesn’t have to change
with
<B>StackImp</B>.</FONT><A NAME="_Toc465909219"></A><A NAME="_Toc465909632"></A><A NAME="_Toc466014543"></A><A NAME="_Toc466073453"></A><A NAME="_Toc466083254"></A><A NAME="_Toc468608060"></A><A NAME="_Toc468771381"></A><A NAME="_Toc312373852"></A><A NAME="_Toc469811384"></A><A NAME="_Toc469821269"></A><A NAME="_Toc469821685"></A><A NAME="_Toc469825366"></A><A NAME="_Toc469874271"></A><A NAME="_Toc470615927"></A><A NAME="_Toc470655065"></A><A NAME="_Toc470821049"></A><A NAME="_Toc470821466"></A><A NAME="_Toc470911563"></A><A NAME="_Toc471359044"></A><A NAME="_Toc471489464"></A><A NAME="_Toc471528895"></A><A NAME="_Toc471795051"></A><A NAME="_Toc471965628"></A><A NAME="_Toc472045630"></A><A NAME="_Toc472255834"></A><A NAME="_Toc472654437"></A><A NAME="_Toc472654851"></A></OL><FONT FACE = "Verdana"><H1 ALIGN="LEFT">
</H1></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Verdana"><B></B></FONT><BR></P></DIV>
<FONT FACE = "Verdana"><H1 ALIGN="LEFT">
</H1></FONT>
<HR><DIV ALIGN="LEFT"><P><A NAME="fn36" HREF="#fnB36">[36]</A><FONT FACE="Georgia" SIZE=2>
As noted before, sometimes access control is referred to as
encapsulation.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><A NAME="fn37" HREF="#fnB37">[37]</A><FONT FACE="Georgia" SIZE=2>
This name is attributed to John Carolan, one of the early pioneers in C++, and
of course, Lewis Carroll. This technique can also be seen as a form of the
“bridge” design pattern, described in Volume 2.</FONT><BR></P></DIV>
<DIV ALIGN="CENTER">
<FONT FACE="Verdana" size = "-1">
[ <a href="Chapter04.html">Previous Chapter</a> ]
[ <a href="Contents.html">Table of Contents</a> ]
[ <a href="DocIndex.html">Index</a> ]
[ <a href="Chapter06.html">Next Chapter</a> ]
</FONT>
<BR>
Last Update:09/27/2001</P></DIV>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -