📄 chapter04.html
字号:
<font color=#0000ff>int</font> main() {
<font color=#009900>// Define variables at the beginning</font>
<font color=#009900>// of the block, as in C:</font>
CStash intStash, stringStash;
<font color=#0000ff>int</font> i;
<font color=#0000ff>char</font>* cp;
ifstream in;
string line;
<font color=#0000ff>const</font> <font color=#0000ff>int</font> bufsize = 80;
<font color=#009900>// Now remember to initialize the variables:</font>
initialize(&intStash, <font color=#0000ff>sizeof</font>(<font color=#0000ff>int</font>));
<font color=#0000ff>for</font>(i = 0; i < 100; i++)
add(&intStash, &i);
<font color=#0000ff>for</font>(i = 0; i < count(&intStash); i++)
cout << <font color=#004488>"fetch(&intStash, "</font> << i << <font color=#004488>") = "</font>
<< *(<font color=#0000ff>int</font>*)fetch(&intStash, i)
<< endl;
<font color=#009900>// Holds 80-character strings:</font>
initialize(&stringStash, <font color=#0000ff>sizeof</font>(<font color=#0000ff>char</font>)*bufsize);
in.open(<font color=#004488>"CLibTest.cpp"</font>);
assert(in);
<font color=#0000ff>while</font>(getline(in, line))
add(&stringStash, line.c_str());
i = 0;
<font color=#0000ff>while</font>((cp = (<font color=#0000ff>char</font>*)fetch(&stringStash,i++))!=0)
cout << <font color=#004488>"fetch(&stringStash, "</font> << i << <font color=#004488>") = "</font>
<< cp << endl;
cleanup(&intStash);
cleanup(&stringStash);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Following the form required by C, all the
variables are created at the beginning of the scope of <B>main( )</B>. Of
course, you must remember to initialize the <B>CStash</B> variables later in the
block by calling <B>initialize( )</B>. One of the problems with C libraries
is that you must carefully convey to the user the importance of the
initialization <A NAME="Index999"></A>and cleanup
<A NAME="Index1000"></A>functions. If these functions aren’t called, there
will be a lot of trouble. Unfortunately, the user doesn’t always wonder if
initialization and cleanup are mandatory. They know what <I>they</I> want to
accomplish, and they’re not as concerned about you jumping up and down
saying, “Hey, wait, you have to do <I>this</I> first!” Some users
have even been known to initialize the elements of a structure themselves.
There’s certainly no mechanism in C to prevent it (more
foreshadowing).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>intStash</B> is filled up with
integers, and the <B>stringStash</B> is filled with character arrays. These
character arrays are produced by opening the source code file,
<B>CLibTest.cpp</B>, and reading the lines from it into a
<A NAME="Index1001"></A><B>string </B>called <B>line</B>, and then producing a
pointer to the character representation of <B>line</B> using the member function
<B>c_str( )</B>.<B> </B></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">After each <B>Stash</B> is loaded, it is
displayed. The <B>intStash</B> is printed using a <B>for</B> loop, which uses
<B>count( )</B> to establish its limit. The <B>stringStash</B> is printed
with a <B>while</B>, which breaks out when <B>fetch( )</B> returns zero to
indicate it is out of bounds.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You’ll also notice an additional
cast in </FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>cp = (<font color=#0000ff>char</font>*)fetch(&stringStash,i++)</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is due to the
<A NAME="Index1002"></A><A NAME="Index1003"></A>stricter type checking in C++,
which does not allow you to simply assign a <B>void*</B> to any other type (C
allows this).</FONT><A NAME="_Toc472654817"></A><BR></P></DIV>
<A NAME="Heading191"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Bad guesses</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There is one more important issue you
should understand before we look at the general problems in creating a C
library. Note that the <B>CLib.h</B> header file <I>must</I> be included in any
file that refers to <B>CStash</B> because the compiler can’t even guess at
what that structure looks like. However, it <I>can</I> guess at what a function
looks like; this sounds like a feature but it turns out to be a major
<A NAME="Index1004"></A>C <A NAME="Index1005"></A>pitfall.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Although you should always declare
functions by including a header file,
<A NAME="Index1006"></A><A NAME="Index1007"></A>function declarations
aren’t essential in C. It’s possible in C (but <I>not </I>in C++) to
call a function that you haven’t declared. A good compiler will warn you
that you probably ought to declare a function first, but it isn’t enforced
by the C language standard. This is a dangerous practice, because the C compiler
can assume that a function that you call with an <B>int</B> argument has an
argument list containing <B>int</B>, even if it may actually contain a
<B>float</B>.<B> </B>This can produce bugs that are very difficult to find, as
you will see.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Each separate C implementation file (with
an extension of <B>.c</B>)<B> </B>is a
<A NAME="Index1008"></A><A NAME="Index1009"></A><I>translation unit</I>. That
is, the compiler is run separately on each translation unit, and when it is
running it is aware of only that unit. Thus, any information you provide by
including header files is quite important because it determines the
compiler’s understanding of the rest of your program. Declarations in
header files are particularly important, because everywhere the header is
included, the compiler will know exactly what to do. If, for example, you have a
declaration in a header file that says <B>void func(float)</B>, the compiler
knows that if you call that function with an integer argument, it should
convert<A NAME="Index1010"></A><A NAME="Index1011"></A><A NAME="Index1012"></A>
the <B>int</B> to a <B>float</B> as it passes the argument (this is called
<I>promotion</I>). Without the declaration, the C compiler would simply assume
that a function <B>func(int)</B> existed, it wouldn’t do the promotion,
and the wrong data would quietly be passed into
<B>func( )</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">For each translation unit, the compiler
creates an <A NAME="Index1013"></A>object file, with an extension of <B>.o</B>
or <B>.obj</B> or something similar. These object files, along with the
necessary start-up code, must be collected by the linker
<A NAME="Index1014"></A>into the executable program. During linking, all the
external references must be resolved. For example, in <B>CLibTest.cpp</B>,
functions such as <B>initialize( )</B> and <B>fetch( )</B> are
declared (that is, the compiler is told what they look like) and used, but not
defined. They are defined elsewhere, in <B>CLib.cpp</B>. Thus, the calls in
<B>CLib.cpp</B> are external references. The linker must, when it puts all the
object files together, take the unresolved external references
<A NAME="Index1015"></A><A NAME="Index1016"></A>and find the addresses they
actually refer to. Those addresses are put into the executable program to
replace the external references.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It’s important to realize that in
C, the external references that the linker searches for are simply function
names, generally with an underscore in front of them. So all the linker has to
do is match up the function name where it is called and the function body in the
object file, and it’s done. If you accidentally made a call that the
compiler interpreted as <B>func(int)</B> and there’s a function body for
<B>func(float)</B> in some other object file, the linker will see <B>_func</B>
in one place and <B>_func</B> in another, and it will think everything’s
OK. The <B>func( )</B> at the calling location will push an <B>int</B> onto
the stack, and the <B>func( )</B> function body will expect a <B>float</B>
to be on the stack. If the function only reads the value and doesn’t write
to it, it won’t blow up the stack. In fact, the <B>float</B> value it
reads off the stack might even make some kind of sense. That’s worse
because it’s harder to find the
bug.</FONT><A NAME="_Toc312373825"></A><A NAME="_Toc472654818"></A><BR></P></DIV>
<A NAME="Heading192"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
What's wrong?</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">We are remarkably adaptable, even in
situations in which perhaps we <I>shouldn’t</I> adapt. The style of the
<B>CStash</B> library has been a staple for C programmers, but if you look at it
for a while, you might notice that it’s rather . . . awkward. When you use
it, you have to pass the address of the structure to every single function in
the library. When reading the code, the mechanism of the library gets mixed with
the meaning of the function calls, which is confusing when you’re trying
to understand what’s going on.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">One of the biggest obstacles, however, to
using libraries in C is the problem of <I>name
clashes<A NAME="Index1017"></A></I>.<A NAME="Index1018"></A> C has a single
<A NAME="Index1019"></A>name space for functions; that is, when the linker looks
for a function name, it looks in a single master list. In addition, when the
compiler is working on a translation unit, it can work only with a single
function with a given name.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now suppose you decide to buy two
libraries from two different vendors, and each library has a structure that must
be initialized and cleaned up. Both vendors decided that
<B>initialize( )</B> and <B>cleanup( )</B> are good names. If you
include both their header files in a single translation unit, what does the C
compiler do? Fortunately, C gives you an error, telling you there’s a type
mismatch in the two different argument lists of the declared functions. But even
if you don’t include them in the same translation unit, the linker will
still have problems. A good linker will detect that there’s a name clash,
but some linkers take the first function name they find, by searching through
the list of object files in the order you give them in the link list. (This can
even be thought of as a feature because it allows you to replace a library
function with your own version.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In either event, you can’t use two
C libraries that contain a function with the identical name. To solve this
problem, C library vendors will often prepend a sequence of unique characters to
the beginning of all their function names. So <B>initialize( )</B> and
<B>cleanup( )</B> might become <B>CStash_initialize( )</B> and
<B>CStash_cleanup( )</B>. This is a logical thing to do because it
“decorates” the name of the <B>struct</B> the function works on with
the name of the
function.<A NAME="Index1020"></A><A NAME="Index1021"></A><A NAME="Index1022"></A><A NAME="Index1023"></A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now it’s time to take the first
step toward creating classes in C++. Variable names inside a
<A NAME="Index1024"></A><A NAME="Index1025"></A><B>struct</B> do not clash with
global variable names. So why not take advantage of this for function names,
when those functions operate on a particular <B>struct</B>? That is, why not
make functions members of
<B>struct</B>s?</FONT><A NAME="_Toc312373826"></A><A NAME="_Toc472654819"></A><BR></P></DIV>
<A NAME="Heading193"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
The basic object</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Step one is exactly that. C++ functions
can be placed inside <B>struct</B>s as
“<A NAME="Index1026"></A><A NAME="Index1027"></A>member functions.”
Here’s what it looks like after
<A NAME="Index1028"></A><A NAME="Index1029"></A><A NAME="Index1030"></A>converting
the C version of <B>CStash</B> to the C++ <B>Stash</B>:
<A NAME="Index1031"></A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C04:CppLib.h</font>
<font color=#009900>// C-like library converted to C++</font>
<font color=#0000ff>struct</font> Stash {
<font color=#0000ff>int</font> size; <font color=#009900>// Size of each space</font>
<font color=#0000ff>int</font> quantity; <font color=#009900>// Number of storage spaces</font>
<font color=#0000ff>int</font> next; <font color=#009900>// Next empty space</font>
<font color=#009900>// Dynamically allocated array of bytes:</font>
<font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>* storage;
<font color=#009900>// Functions!</font>
<font color=#0000ff>void</font> initialize(<font color=#0000ff>int</font> size);
<font color=#0000ff>void</font> cleanup();
<font color=#0000ff>int</font> add(<font color=#0000ff>const</font> <font color=#0000ff>void</font>* element);
<font color=#0000ff>void</font>* fetch(<font color=#0000ff>int</font> index);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -