📄 chapter04.html
字号:
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This brings up an interesting issue. One
of the important goals for C++ is to compile as much existing C code as possible
to allow for an easy transition to the new language. However, this doesn’t
mean any code that C allows will automatically be allowed in C++.
<A NAME="Index1064"></A><A NAME="Index1065"></A><A NAME="Index1066"></A>There
are a number of things the C compiler lets you get away with that are dangerous
and error-prone. (We’ll look at them as the book progresses.) The C++
compiler generates warnings and errors for these situations. This is often much
more of an advantage than a hindrance. In fact, there are many situations in
which you are trying to run down an error in C and just can’t find it, but
as soon as you <A NAME="Index1067"></A>recompile the program in C++, the
compiler points out the problem! In C, you’ll often find that you can get
the program to compile, but then you have to get it to work. In C++, when the
program compiles correctly, it often works, too! This is because the language is
a lot stricter about type.<A NAME="Index1068"></A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see a number of new things in the
way the C++ version of <B>Stash</B> is used in the following test
program:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C04:CppLibTest.cpp</font>
<font color=#009900>//{L} CppLib</font>
<font color=#009900>// Test of C++ library</font>
#include <font color=#004488>"CppLib.h"</font>
#include <font color=#004488>"../require.h"</font>
#include <fstream>
#include <iostream>
#include <string>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>int</font> main() {
Stash intStash;
intStash.initialize(<font color=#0000ff>sizeof</font>(<font color=#0000ff>int</font>));
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < 100; i++)
intStash.add(&i);
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> j = 0; j < intStash.count(); j++)
cout << <font color=#004488>"intStash.fetch("</font> << j << <font color=#004488>") = "</font>
<< *(<font color=#0000ff>int</font>*)intStash.fetch(j)
<< endl;
<font color=#009900>// Holds 80-character strings:</font>
Stash stringStash;
<font color=#0000ff>const</font> <font color=#0000ff>int</font> bufsize = 80;
stringStash.initialize(<font color=#0000ff>sizeof</font>(<font color=#0000ff>char</font>) * bufsize);
ifstream in(<font color=#004488>"CppLibTest.cpp"</font>);
assure(in, <font color=#004488>"CppLibTest.cpp"</font>);
string line;
<font color=#0000ff>while</font>(getline(in, line))
stringStash.add(line.c_str());
<font color=#0000ff>int</font> k = 0;
<font color=#0000ff>char</font>* cp;
<font color=#0000ff>while</font>((cp =(<font color=#0000ff>char</font>*)stringStash.fetch(k++)) != 0)
cout << <font color=#004488>"stringStash.fetch("</font> << k << <font color=#004488>") = "</font>
<< cp << endl;
intStash.cleanup();
stringStash.cleanup();
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">One thing you’ll notice is that the
variables are all defined “on the fly” (as introduced in the
previous chapter). That is, they are defined at any point in the scope, rather
than being restricted – as in C – to the beginning of the
scope.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The code is quite similar to
<B>CLibTest.cpp</B>, but when a member function is called, the call occurs using
the member selection operator <A NAME="Index1069"></A><A NAME="Index1070"></A>
<A NAME="Index1071"></A><A NAME="Index1072"></A> ‘<B>.</B>’ preceded
by the name of the variable. This is a convenient syntax because it mimics the
selection of a data member of the structure. The difference is that this is a
function member, so it has an argument list.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Of course, the call that the compiler
<I>actually</I> generates looks much more like the original C library function.
Thus, considering name
decoration<A NAME="Index1073"></A><A NAME="Index1074"></A><A NAME="Index1075"></A><A NAME="Index1076"></A>
and the passing of <B>this</B>, the C++ function call
<B>intStash.initialize(sizeof(int), 100)</B> becomes something like
<B>Stash_initialize(&intStash, sizeof(int), 100)</B>. If you ever wonder
what’s going on underneath the covers, remember that the
<A NAME="Index1077"></A><A NAME="Index1078"></A><A NAME="Index1079"></A>original
C++ compiler <B>cfront</B> from AT&T produced C code as its output, which
was then compiled by the underlying C compiler. This approach meant that
<B>cfront</B> could be quickly ported to any machine that had a C compiler, and
it helped to rapidly disseminate C++ compiler technology. But because the C++
compiler had to generate C, you know that there must be some way to represent
C++ syntax in C (some compilers still allow you to produce C
code).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There’s one other change from
<B>ClibTest.cpp</B>, which is the introduction of the
<A NAME="Index1080"></A><B>require.h</B> header file. This is a header file that
I created for this book to perform more sophisticated error checking than that
provided by <B>assert( )</B>. It contains several functions, including the
one used here called <A NAME="Index1081"></A><B>assure( ),</B> which is
used for files. This function checks to see if the file has successfully been
opened, and if not it reports to standard error that the file could not be
opened (thus it needs the name of the file as the second argument) and exits the
program. The <B>require.h</B> functions will be used throughout the book, in
particular to ensure that there are the right number of command-line arguments
and that files are opened properly. The <B>require.h</B> functions replace
repetitive and distracting error-checking code, and yet they provide essentially
useful error messages. These functions will be fully explained later in the
book.</FONT><A NAME="_Toc312373827"></A><A NAME="_Toc472654820"></A><BR></P></DIV>
<A NAME="Heading194"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
What's an <A NAME="Index1082"></A>object?</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now that you’ve seen an initial
example, it’s time to step back and take a look at some terminology. The
act of bringing functions inside structures is the root of what C++ adds to C,
and it introduces a new way of thinking about structures: as concepts. In C, a
<B>struct</B> <A NAME="Index1083"></A>is an agglomeration of data, a way to
package data so you can treat it in a clump. But it’s hard to think about
it as anything but a programming convenience. The functions that operate on
those structures are elsewhere. However, with functions in the package, the
structure becomes a new creature, capable of describing both characteristics
(like a C <B>struct</B> does) <I>and</I> behaviors. The concept of an object, a
free-standing, bounded entity that can remember <I>and</I> act, suggests
itself.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In C++, an object is just a variable, and
the purest definition is “a region of storage” (this is a more
specific way of saying, “an object must have a unique
<A NAME="Index1084"></A>identifier,” which in the case of C++ is a unique
memory address). It’s a place where you can store data, and it’s
implied that there are also operations that can be performed on this
data.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Unfortunately, there’s not complete
consistency across languages when it comes to these terms, although they are
fairly well-accepted. You will also sometimes encounter disagreement about what
an object-oriented language is, although that seems to be reasonably well sorted
out by now. There are languages that are
<I>object-based<A NAME="Index1085"></A></I>, which means that they have objects
like the C++ structures-with-functions that you’ve seen so far. This,
however, is only part of the picture when it comes to an object-oriented
language, and languages that stop at packaging functions inside data structures
are object-based, not
object-oriented.</FONT><A NAME="_Toc312373828"></A><A NAME="_Toc472654821"></A><BR></P></DIV>
<A NAME="Heading195"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Abstract data typing</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The ability to package data with
functions allows you to create a new data type. This is often called
<I>encapsulation</I></FONT><A NAME="fnB33" HREF="#fn33">[33]</A><A NAME="Index1086"></A><FONT FACE="Georgia">.<I>
</I>An existing data type may have several pieces of data packaged together. For
example, a <B>float</B> has an exponent, a mantissa, and a sign bit. You can
tell it to do things: add to another <B>float</B> or to an <B>int</B>, and so
on. It has characteristics and behavior.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The definition of <B>Stash</B> creates a
new data type. You can <B>add( )</B>, <B>fetch( )</B>, and
<B>inflate( )</B>. You create one by saying <B>Stash s</B>, just as you
create a <B>float</B> by saying <B>float f</B>. A <B>Stash</B> also has
characteristics and behavior. Even though it acts like a real, built-in data
type, we refer to it as an
<A NAME="Index1087"></A><A NAME="Index1088"></A><A NAME="Index1089"></A><I>abstract
data type</I>, perhaps because it allows us to abstract a concept from the
problem space into the solution space. In addition, the C++ compiler treats it
like a new data type, and if you say a function expects a <B>Stash</B>, the
compiler makes sure you pass a <B>Stash</B> to that function. So the same level
of type checking happens with abstract data types (sometimes called
<A NAME="Index1090"></A><A NAME="Index1091"></A><I>user-defined types</I>) as
with built-in types.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can immediately see a difference,
however, in the way you perform operations on objects. You say
<B>object.memberFunction(arglist)</B>. This is “calling a member function
<A NAME="Index1092"></A><A NAME="Index1093"></A><A NAME="Index1094"></A>for an
object.” But in object-oriented parlance, this is also referred to as
“sending a message <A NAME="Index1095"></A><A NAME="Index1096"></A>to an
object.” So for a <B>Stash s</B>, the statement <B>s.add(&i)</B>
“sends a message to <B>s</B>” saying, “<B>add( )</B> this
to yourself.” <A NAME="Index1097"></A>In fact, object-oriented programming
can be summed up in a single phrase: <I>sending messages to objects</I>. Really,
that’s all you do – create a bunch of objects and send messages to
them. The trick, of course, is figuring out what your objects and messages
<I>are</I>, but once you accomplish this the implementation in C++ is
surprisingly
straightforward.</FONT><A NAME="_Toc312373829"></A><A NAME="_Toc472654822"></A><BR></P></DIV>
<A NAME="Heading196"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Object details</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A question that often comes up in
seminars is, “How big is an object, and what does it look like?” The
answer is “about what you expect from a C <B>struct</B>.” In fact,
the code the C compiler produces for a C <B>struct</B> (with no C++ adornments)
will usually look <I>exactly</I> the same as the code produced by a C++
compiler. This is reassuring to those C programmers who depend on the details of
size and layout in their code, and for some reason directly access
structure<A NAME="Index1098"></A> bytes instead of using identifiers (relying on
a particular size and layout for a structure is a nonportable
activity).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The size of a
<B>struct<A NAME="Index1099"></A><A NAME="Index1100"></A></B> is the combined
size of all of its members. Sometimes when the compiler lays out a
<B>struct</B>, it adds extra bytes to make the boundaries come out neatly
– this may increase execution efficiency. In Chapter 15, you’ll see
how in some cases “secret” pointers are added to the structure, but
you don’t need to worry about that right now.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can determine the size of a
<B>struct</B> using the
<A NAME="Index1101"></A><A NAME="Index1102"></A><B>sizeof</B> operator.
Here’s a small example:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C04:Sizeof.cpp</font>
<font color=#009900>// Sizes of structs</font>
#include <font color=#004488>"CLib.h"</font>
#include <font color=#004488>"CppLib.h"</font>
#include <iostream>
<font color=#0000ff>using</font> <
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -