📄 chapter04.html
字号:
<font color=#0000ff>int</font> count();
<font color=#0000ff>void</font> inflate(<font color=#0000ff>int</font> increase);
}; <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">First, notice there is no
<B>typedef<A NAME="Index1032"></A><A NAME="Index1033"></A></B>. Instead of
requiring you to create a <B>typedef</B>, the C++ compiler turns the name of the
structure into a new type name for the program (just as <B>int</B>, <B>char</B>,
<B>float</B> and <B>double</B> are type names).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">All the data members are exactly the same
as before, but now the functions are inside the body of the <B>struct</B>. In
addition, notice that the first argument from the C version of the library has
been removed. In <A NAME="Index1034"></A>C++, instead of forcing you to pass the
address of the structure as the first argument to all the functions that operate
on that structure, the compiler secretly does this for you. Now the only
arguments for the functions are concerned with what the function <I>does</I>,
not the mechanism of the function’s operation.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It’s important to realize that the
function code is effectively the same as it was with the C version of the
library. The number of arguments is the same (even though you don’t see
the structure address being passed in, it’s still there), and
there’s only one function body for each function. That is, just because
you say</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Stash A, B, C;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">doesn’t mean you get a different
<B>add( )</B> function for each variable.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">So the code that’s generated is
almost identical to what you would have written for the C version of the
library. Interestingly enough, this includes the
<A NAME="Index1035"></A><A NAME="Index1036"></A><A NAME="Index1037"></A><A NAME="Index1038"></A>
“name decoration” you probably would have done to produce
<B>Stash_initialize( )</B>, <B>Stash_cleanup( )</B>, and so on. When
the function name is inside the <B>struct</B>, the compiler effectively does the
same thing. Therefore, <B>initialize( )</B> inside the structure
<B>Stash</B> will not collide with a function named <B>initialize( )</B>
inside any other structure, or even a global function named
<B>initialize( )</B>. Most of the time you don’t have to worry about
the function name decoration – you use the undecorated name. But sometimes
you do need to be able to specify that this <B>initialize( )</B> belongs to
the <B>struct</B> <B>Stash</B>, and not to any other <B>struct</B>. In
particular, when you’re defining the function you need to fully specify
which one it is. To accomplish this full specification, C++ has an operator
<A NAME="Index1039"></A>(<A NAME="Index1040"></A><B>::</B>) called the
<A NAME="Index1041"></A><A NAME="Index1042"></A><A NAME="Index1043"></A><I>scope
resolution operator</I> (named so because names can now be in different scopes:
at global scope or within the scope of a <B>struct</B>). For example, if you
want to specify <B>initialize( )</B>, which belongs to <B>Stash</B>, you
say <B>Stash::initialize(int size)</B>. You can see how the scope resolution
operator is used in the function definitions:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C04:CppLib.cpp {O}</font>
<font color=#009900>// C library converted to C++</font>
<font color=#009900>// Declare structure and functions:</font>
#include <font color=#004488>"CppLib.h"</font>
#include <iostream>
#include <cassert>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#009900>// Quantity of elements to add</font>
<font color=#009900>// when increasing storage:</font>
<font color=#0000ff>const</font> <font color=#0000ff>int</font> increment = 100;
<font color=#0000ff>void</font> Stash::initialize(<font color=#0000ff>int</font> sz) {
size = sz;
quantity = 0;
storage = 0;
next = 0;
}
<font color=#0000ff>int</font> Stash::add(<font color=#0000ff>const</font> <font color=#0000ff>void</font>* element) {
<font color=#0000ff>if</font>(next >= quantity) <font color=#009900>// Enough space left?</font>
inflate(increment);
<font color=#009900>// Copy element into storage,</font>
<font color=#009900>// starting at next empty space:</font>
<font color=#0000ff>int</font> startBytes = next * size;
<font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>* e = (<font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>*)element;
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < size; i++)
storage[startBytes + i] = e[i];
next++;
<font color=#0000ff>return</font>(next - 1); <font color=#009900>// Index number</font>
}
<font color=#0000ff>void</font>* Stash::fetch(<font color=#0000ff>int</font> index) {
<font color=#009900>// Check index boundaries:</font>
assert(0 <= index);
<font color=#0000ff>if</font>(index >= next)
<font color=#0000ff>return</font> 0; <font color=#009900>// To indicate the end</font>
<font color=#009900>// Produce pointer to desired element:</font>
<font color=#0000ff>return</font> &(storage[index * size]);
}
<font color=#0000ff>int</font> Stash::count() {
<font color=#0000ff>return</font> next; <font color=#009900>// Number of elements in CStash</font>
}
<font color=#0000ff>void</font> Stash::inflate(<font color=#0000ff>int</font> increase) {
assert(increase > 0);
<font color=#0000ff>int</font> newQuantity = quantity + increase;
<font color=#0000ff>int</font> newBytes = newQuantity * size;
<font color=#0000ff>int</font> oldBytes = quantity * size;
<font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>* b = <font color=#0000ff>new</font> <font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>[newBytes];
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < oldBytes; i++)
b[i] = storage[i]; <font color=#009900>// Copy old to new</font>
<font color=#0000ff>delete</font> []storage; <font color=#009900>// Old storage</font>
storage = b; <font color=#009900>// Point to new memory</font>
quantity = newQuantity;
}
<font color=#0000ff>void</font> Stash::cleanup() {
<font color=#0000ff>if</font>(storage != 0) {
cout << <font color=#004488>"freeing storage"</font> << endl;
<font color=#0000ff>delete</font> []storage;
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are several other things that are
different between C and C++. First, the declarations in the header
files<A NAME="Index1044"></A><A NAME="Index1045"></A> are <I>required</I> by the
compiler. In C++ you cannot call a function without declaring it first. The
compiler will issue an error message otherwise. This is an important way to
ensure that function calls are consistent between the point where they are
called and the point where they are defined. By forcing you to
declare<A NAME="Index1046"></A> the <A NAME="Index1047"></A>function before you
call it, the C++ compiler virtually ensures that you will perform this
declaration by including the header file. If you also include the same header
file in the place where the functions are defined, then the compiler checks to
make sure that the declaration in the header and the function definition match
up. This means that the header file becomes a validated repository for function
declarations and ensures that functions are used consistently throughout all
translation units in the project.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Of course, global functions
<A NAME="Index1048"></A><A NAME="Index1049"></A>can still be declared by hand
every place where they are defined and used. (This is so tedious that it becomes
very unlikely.) However, structures must always be declared before they are
defined or used, and the most convenient place to put a
<A NAME="Index1050"></A><A NAME="Index1051"></A><A NAME="Index1052"></A><A NAME="Index1053"></A>structure
definition is in a header file, except for those you intentionally hide in a
file.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see that all the member functions
look almost the same as when they were C functions, except for the scope
resolution and the fact that the first argument from the C version of the
library is no longer explicit. It’s still there, of course, because the
function has to be able to work on a particular <B>struct</B> variable. But
notice, inside the member function, that the member selection is also gone!
Thus, instead of saying <B>s–>size = sz;</B> you say <B>size = sz;</B>
and eliminate the tedious <B>s–></B>, which didn’t really add
anything to the meaning of what you were doing anyway. The C++ compiler is
apparently doing this for you. Indeed, it is taking the “secret”
first argument (the address of the structure that we were previously passing in
by hand) and applying the member selector whenever you refer to one of the data
members of a <B>struct</B>.
<A NAME="Index1054"></A><A NAME="Index1055"></A><A NAME="Index1056"></A><A NAME="Index1057"></A><A NAME="Index1058"></A>This
means that whenever you are inside the member function of another <B>struct</B>,
you can refer to any member (including another member function) by simply giving
its name. The compiler will search through the local structure’s names
before looking for a global version of that name. You’ll find that this
feature means that not only is your code easier to write, it’s a lot
easier to read.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">But what if, for some reason, you
<I>want</I> to be able to get your hands on the address of the structure? In the
C version of the library it was easy because each function’s first
argument was a <B>CStash*</B> called <B>s</B>. In C++, things are even more
consistent. There’s a special keyword, called
<A NAME="Index1059"></A><B>this<A NAME="Index1060"></A></B>, which produces the
address of the <B>struct</B>. It’s the equivalent of the
‘<B>s</B>’ in the C version of the library. So we can revert to the
C style of things by saying</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>this</font>->size = Size;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The code generated by the compiler is
exactly the same, so you don’t need to use <B>this</B> in such a fashion;
occasionally, you’ll see code where people explicitly use <B>this-></B>
everywhere but it doesn’t add anything to the meaning of the code and
often indicates an inexperienced programmer. Usually, you don’t use
<B>this</B> often, but when you need it, it’s there (some of the examples
later in the book will use <B>this</B>).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There’s one last item to mention.
In C, you could assign a <B>void*</B> to any other pointer like
this:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>int</font> i = 10;
<font color=#0000ff>void</font>* vp = &i; <font color=#009900>// OK in both C and C++</font>
<font color=#0000ff>int</font>* ip = vp; <font color=#009900>// Only acceptable in C</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><A NAME="Index1061"></A><A NAME="Index1062"></A><FONT FACE="Georgia">and
there was no complaint from the compiler. But in C++, this statement is not
allowed. Why? Because C is not so particular about type information, so it
allows you to assign a pointer with an unspecified type to a pointer with a
specified type. Not so with C++. Type is critical in C++, and the compiler
stamps its foot when there are any violations of type information. This has
always been important, but it is especially important in C++ because you have
member functions in <B>struct</B>s. If you could pass pointers to <B>struct</B>s
around with impunity in C++, then you could end up calling a member function for
a <B>struct</B> that doesn’t even logically exist for that <B>struct</B>!
A real recipe for disaster. Therefore, while C++ allows the assignment of any
type of pointer to a <A NAME="Index1063"></A><B>void*</B> (this was the original
intent of <B>void*</B>, which is required to be large enough to hold a pointer
to any type), it will <I>not</I> allow you to assign a <B>void</B> pointer to
any other type of pointer. A cast is always required to tell the reader and the
compiler that you really do want to treat it as the destination type.
</FONT><BR></P></DIV>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -