📄 chapter09.html
字号:
<B>putc( )<A NAME="Index1610"></A></B> macro in <B>cstdio </B>may evaluate
its second argument twice. This is specified in Standard C. Also, careless
implementations of <B>toupper( )<A NAME="Index1611"></A></B> as a macro may
evaluate the argument more than once, which will give you unexpected results
with
<B>toupper(*p++)</B>.</FONT><A NAME="fnB45" HREF="#fn45">[45]</A><A NAME="Index1612"></A><A NAME="_Toc312373926"></A><A NAME="_Toc472654899"></A><BR></P></DIV>
<A NAME="Heading279"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Macros and access</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Of course, careful coding and use of
preprocessor macros is required with C, and we could certainly get away with the
same thing in C++ if it weren’t for one problem: a macro has no concept of
the scoping <A NAME="Index1613"></A>required with member functions. The
preprocessor <A NAME="Index1614"></A>simply performs text substitution, so you
cannot say something like</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>class</font> X {
<font color=#0000ff>int</font> i;
<font color=#0000ff>public</font>:
#define VAL(X::i) <font color=#009900>// Error</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">or anything even close. In addition,
there would be no indication of which object you were referring to. There is
simply no way to express class scope in a macro. Without some alternative to
preprocessor macros, programmers will be tempted to make some data members
<B>public</B> for the sake of efficiency, thus exposing the underlying
implementation and preventing changes in that implementation, as well as
eliminating the guarding that <B>private</B>
provides.</FONT><A NAME="_Toc305593212"></A><A NAME="_Toc305628684"></A><A NAME="_Toc312373927"></A><A NAME="_Toc472654900"></A><BR></P></DIV>
<A NAME="Heading280"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Inline functions</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In solving the C++ problem of a macro
with access to <A NAME="Index1615"></A><B>private</B> class members, <I>all</I>
the problems associated with preprocessor macros were eliminated. This was done
by bringing the concept of macros under the control of the compiler where they
belong. C++ implements the macro as <I>inline
function<A NAME="Index1616"></A><A NAME="Index1617"></A></I>, which is a true
function in every sense. Any behavior you expect from an ordinary function, you
get from an inline function. The only difference is that an inline function is
expanded in place, like a preprocessor macro, so the overhead of the function
call <A NAME="Index1618"></A><A NAME="Index1619"></A>is eliminated. Thus, you
should (almost) never use macros, only inline functions.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Any function defined within a class body
is automatically inline, but you can also make a non-class function inline by
preceding it with the <B>inline </B>keyword. However, for it to have any effect,
you must include the function body with the declaration, otherwise the compiler
will treat it as an ordinary function declaration. Thus,</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>inline</font> <font color=#0000ff>int</font> plusOne(<font color=#0000ff>int</font> x);</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">has no effect at all other than declaring
the function (which may or may not get an inline definition sometime later). The
successful approach provides the function body:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>inline</font> <font color=#0000ff>int</font> plusOne(<font color=#0000ff>int</font> x) { <font color=#0000ff>return</font> ++x; }</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice that the compiler will check (as
it always does) for the proper use of the function argument list and return
value (performing any necessary conversions), something the preprocessor is
incapable of. Also, if you try to write the above as a preprocessor macro, you
get an unwanted side effect.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You’ll almost always want to put
inline definitions in a header
file<A NAME="Index1620"></A><A NAME="Index1621"></A>. When the compiler sees
such a definition, it puts the function type (the signature combined with the
return value) <I>and</I> the function body in its symbol table. When you use the
function, the compiler checks to ensure the call is correct and the return value
is being used correctly, and then substitutes the function body for the function
call, thus eliminating the overhead. The inline code does occupy space, but if
the function is small, this can actually take less space than the code generated
to do an ordinary function call (pushing arguments on the stack and doing the
CALL).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">An inline function in a header file has a
special status, since you must include the header file containing the function
<I>and</I> its definition in every file where the function is used, but you
don’t end up with multiple definition errors (however, the definition must
be identical in all places where the inline function is
included).</FONT><A NAME="_Toc312373928"></A><A NAME="_Toc472654901"></A><BR></P></DIV>
<A NAME="Heading281"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Inlines inside classes</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To define an inline function, you must
ordinarily precede the function definition with the <B>inline</B> keyword.
However, this is not necessary inside a class
definition<A NAME="Index1622"></A><A NAME="Index1623"></A>. Any function you
define inside a class definition is automatically an inline. For
example:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C09:Inline.cpp</font>
<font color=#009900>// Inlines inside classes</font>
#include <iostream>
#include <string>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>class</font> Point {
<font color=#0000ff>int</font> i, j, k;
<font color=#0000ff>public</font>:
Point(): i(0), j(0), k(0) {}
Point(<font color=#0000ff>int</font> ii, <font color=#0000ff>int</font> jj, <font color=#0000ff>int</font> kk)
: i(ii), j(jj), k(kk) {}
<font color=#0000ff>void</font> print(<font color=#0000ff>const</font> string& msg = <font color=#004488>""</font>) <font color=#0000ff>const</font> {
<font color=#0000ff>if</font>(msg.size() != 0) cout << msg << endl;
cout << <font color=#004488>"i = "</font> << i << <font color=#004488>", "</font>
<< <font color=#004488>"j = "</font> << j << <font color=#004488>", "</font>
<< <font color=#004488>"k = "</font> << k << endl;
}
};
<font color=#0000ff>int</font> main() {
Point p, q(1,2,3);
p.print(<font color=#004488>"value of p"</font>);
q.print(<font color=#004488>"value of q"</font>);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here, the two constructors and the
<B>print( )</B> function are all inlines by default. Notice in
<B>main( ) </B>that the fact you are using inline functions is transparent,
as it should be. The logical behavior of a function must be identical regardless
of whether it’s an inline (otherwise your compiler is broken). The only
difference you’ll see is in performance.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Of course, the temptation is to use
inlines everywhere inside class declarations because they save you the extra
step of making the external member function definition. Keep in mind, however,
that the idea of an inline is to provide improved opportunities for
<A NAME="Index1624"></A>optimization by the compiler. But inlining a big
function will cause that code to be duplicated everywhere the function is
called, producing code bloat that may mitigate the speed benefit (the only
reliable course of action is to experiment to discover the effects of inlining
on your program with your
compiler).</FONT><A NAME="_Toc312373929"></A><A NAME="_Toc472654902"></A><BR></P></DIV>
<A NAME="Heading282"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Access functions</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">One of the most important uses of inlines
inside classes is the <I>access
function<A NAME="Index1625"></A><A NAME="Index1626"></A></I>. This is a small
function that allows you to read or change part of the state of an object
– that is, an internal variable or variables. The reason inlines are so
important for access functions can be seen in the following
example:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C09:Access.cpp</font>
<font color=#009900>// Inline access functions</font>
<font color=#0000ff>class</font> Access {
<font color=#0000ff>int</font> i;
<font color=#0000ff>public</font>:
<font color=#0000ff>int</font> read() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> i; }
<font color=#0000ff>void</font> set(<font color=#0000ff>int</font> ii) { i = ii; }
};
<font color=#0000ff>int</font> main() {
Access A;
A.set(100);
<font color=#0000ff>int</font> x = A.read();
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here, the class user never has direct
contact with the state variables inside the class, and they can be kept
<A NAME="Index1627"></A><A NAME="Index1628"></A><B>private</B>, under the
control of the class designer. All the access to the <B>private</B> data members
can be controlled through the member function interface. In addition, access is
remarkably efficient. Consider the <B>read( )</B>, for example. Without
inlines, the code generated for the call to <B>read( )</B> would typically
include pushing <A NAME="Index1629"></A><A NAME="Index1630"></A><B>this</B> on
the stack and making an assembly language CALL. With most machines, the size of
this code would be larger than the code created by the inline, and the execution
time would certainly be longer.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Without inline functions, an
efficiency-conscious class designer will be tempted to simply make <B>i</B> a
public member, eliminating the overhead by allowing the user to directly access
<B>i</B>. From a design <A NAME="Index1631"></A>standpoint, this is disastrous
because <B>i</B> then becomes part of the public interface, which means the
class designer can never change it. You’re stuck with an <B>int</B> called
<B>i</B>. This is a problem because you may learn sometime later that it would
be much more useful to represent the state information as a <B>float</B> rather
than an <B>int</B>, but because <B>int i</B> is part of the public interface,
you can’t change it. Or you may want to perform some additional
calculation as part of reading or setting <B>i</B>, which you can’t do if
it’s <B>public</B>.<B> </B>If, on the other hand, you’ve always used
member functions to read and change the state information of an object, you can
modify the underlying representation of the object to your heart’s
<A NAME="Index1632"></A><A NAME="Index1633"></A>content.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In addition, the use of member functions
to control data members allows you to add code to the member function to detect
when that data is being changed, which can be very useful during debugging. If a
data member is <B>public</B>, anyone can change it anytime without you knowing
about it.</FONT><BR></P></DIV>
<A NAME="Heading283"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Accessors and mutators</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Some people further divide the concept of
access functions into <I>accessors</I> <A NAME="Index1634"></A>(to read state
information from an object) and <I>mutators</I> <A NAME="Index1635"></A>(to
change the state of an object). In addition, function overloading may be used to
provide the same function name for both the accessor and mutator; how you call
the function determines whether you’re reading or modifying state
information. Thus,</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C09:Rectangle.cpp</font>
<font color=#009900>// Accessors & mutators</font>
<font color=#0000ff>class</font> Rectangle {
<font color=#0000ff>int</font> wide, high;
<font color=#0000ff>public</font>:
Rectangle(<font color=#0000ff>int</font> w = 0, <font color=#0000ff>int</font> h = 0)
: wide(w), high(h) {}
<font color=#0000ff>int</font> width() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> wide; } <font color=#009900>// Read</font>
<font color=#0000ff>void</font> width(<font color=#0000ff>int</font> w) { wide = w; } <font color=#009900>// Set</font>
<font color=#0000ff>int</font> height() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> high; } <font color=#009900>// Read</font>
<font color=#0000ff>void</font> height(<font color=#0000ff>int</font> h) { high = h; } <font color=#009900>// Set</font>
};
<font color=#0000ff>int</font> main() {
Rectangle r(19, 47);
<font color=#009900>// Change width & height:</font>
r.height(2 * r.width());
r.width(2 * r.height());
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The constructor uses the constructor
initializer list (briefly introduced in Chapter 8 and covered fully in Chapter
14) to initialize the values of <B>wide</B> and <B>high</B> (using the
<A NAME="Index1636"></A><A NAME="Index1637"></A>pseudoconstructor form for
built-in types).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You cannot have member function names
using the same identifiers as data members, so you might be tempted to
distinguish the data members with a
<A NAME="Index1638"></A><A NAME="Index1639"></A>leading underscore. However,
identifiers with leading underscores are reserved so you should not use them.
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You may choose instead to use
<A NAME="Index1640"></A><A NAME="Index1641"></A>“get” and
“set” to indicate accessors and mutators:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C09:Rectangle2.cpp</font>
<font color=#009900>// Accessors & mutators with "get" and "set"</font>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -