⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter09.html

📁 《C++编程思想》中文版。。。。。。。。。。。。。
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<!--
This document was converted from RTF source: 
By rtftohtml 4.19
See http://www.sunpack.com/RTF
Filename:TIC2Vone.rtf
Application Directory:C:\TOOLS\RTF2HTML\
Subject:
Author:Bruce Eckel
Operator:Bruce Eckel
Document Comments:
Version Comments:
Comments:
Keywords:
Translation Date:09/27/2001
Translation Time:05:25:38
Translation Platform:Win32
Number of Output files:22
This File:Chapter09.html
SplitDepth=1
SkipNavPanel=1
SkipLeadingToc=1
SkipTrailingToc=1
GenContents=1
GenFrames=1
GenIndex=1
-->
<HEAD lang="en"><META http-equiv="Content-Type" content="text/html">
<TITLE>9: Inline Functions</TITLE>
</HEAD>

<BODY  BGCOLOR="#FFFFFF"><DIV ALIGN="CENTER">
  <a href="http://www.MindView.net">
  <img src="mindview-head.gif" alt="MindView Inc." BORDER = "0"></a>
  <CENTER>
    <FONT FACE="Verdana" size = "-1">
    [ <a href="README-HTML.txt">Viewing Hints</a> ]
    [ <a href="http://www.mindview.net/CPPServices/SolutionGuide.html">Exercise Solutions</a> ]
    [ <a href="http://www.mindview.net/ThinkingInCPP2e.html">Volume 2</a> ]
    [ <a href="http://www.mindview.net/MailingList.html">Free Newsletter</a> ] <br>
    [ <a href="http://www.mindview.net/CPPServices/#PublicSeminars">Seminars</a> ]
    [ <a href="http://www.mindview.net/CPPServices/#SeminarsOnCD">Seminars on CD ROM</a> ]
    [ <a href="http://www.mindview.net/CPPServices/#ConsultingServices">Consulting</a> ]
    </FONT>
  <H2><FONT FACE="Verdana">
  Thinking in C++, 2nd ed. Volume 1</FONT></H2></FONT>
  <H3><FONT FACE="Verdana">&copy;2000 by Bruce Eckel</FONT></H3></FONT>
  
    <FONT FACE="Verdana" size = "-1">
     [ <a href="Chapter08.html">Previous Chapter</a> ] 
    [ <a href="Contents.html">Table of Contents</a> ] 
    [ <a href="DocIndex.html">Index</a> ]
     [ <a href="Chapter10.html">Next Chapter</a> ] 
    </FONT>
    
  </CENTER>
  </P></DIV><A NAME="_Toc472654897"></A><A NAME="Heading277"></A><FONT FACE = "Verdana"><H1 ALIGN="LEFT">
9: Inline Functions</H1></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Verdana" SIZE=4>One of the important features C++
inherits from C is efficiency<A NAME="Index1598"></A>. If the efficiency of C++
were dramatically </FONT><BR><FONT FACE="Verdana" SIZE=4>less than C, there
would be a significant contingent of programmers who couldn&#8217;t justify its
use.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In C, one of the ways to preserve
efficiency is through the use of
<I>macros<A NAME="Index1599"></A><A NAME="Index1600"></A></I>, which allow you
to make what looks like a function call without the normal
<A NAME="Index1601"></A><A NAME="Index1602"></A>function call overhead. The
macro is implemented with the preprocessor instead of the compiler proper, and
the preprocessor replaces all macro calls directly with the macro code, so
there&#8217;s no cost involved from pushing arguments, making an
assembly-language CALL, returning arguments, and performing an assembly-language
RETURN. All the work is performed by the preprocessor, so you have the
convenience and readability of a function call but it doesn&#8217;t cost you
anything.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"> There are two problems with the use of
<A NAME="Index1603"></A>preprocessor macros in C++. The first is also true with
C: a macro looks like a function call, but doesn&#8217;t always act like one.
This can bury difficult-to-find bugs. The second problem is specific to C++: the
preprocessor has no permission to access class member data. This means
preprocessor macros cannot be used as class member functions.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To retain the efficiency of the
preprocessor macro, but to add the safety and class scoping of true functions,
C++ has the <I>inline
function<A NAME="Index1604"></A><A NAME="Index1605"></A></I>. In this chapter,
we&#8217;ll look at the problems of preprocessor macros in C++, how these
problems are solved with inline functions, and guidelines and insights on the
way inlines
work.</FONT><A NAME="_Toc305593211"></A><A NAME="_Toc305628683"></A><A NAME="_Toc312373925"></A><A NAME="_Toc472654898"></A><BR></P></DIV>
<A NAME="Heading278"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Preprocessor pitfalls<BR><A NAME="Index1606"></A><A NAME="Index1607"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The key to the problems of preprocessor
macros is that you can be fooled into thinking that the behavior of the
preprocessor is the same as the behavior of the compiler. Of course, it was
intended that a macro look and act like a function call, so it&#8217;s quite
easy to fall into this fiction. The difficulties begin when the subtle
differences appear.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As a simple example, consider the
following:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>#define F (x) (x + 1)</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now, if a call is made to <B>F</B> like
this</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>F(1)</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">the preprocessor expands it, somewhat
unexpectedly, to the following:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>(x) (x + 1)(1)</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The problem occurs because of the gap
between <B>F</B> and its opening parenthesis in the macro definition. When this
gap is removed, you can actually <I>call</I> the macro with the
gap</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>F (1)</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">and it will still expand properly
to</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>(1 + 1)</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The example above is fairly trivial and
the problem will make itself evident right away. The real difficulties occur
when using expressions as arguments in macro calls. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are two problems. The first is that
expressions may expand inside the macro so that their evaluation precedence is
different from what you expect. For example,</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>#define FLOOR(x,b) x&gt;=b?0:1</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now, if expressions are used for the
arguments</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>if</font>(FLOOR(a&amp;0x0f,0x07)) <font color=#009900>// ...</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">the macro will expand to</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>if</font>(a&amp;0x0f&gt;=0x07?0:1)</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The precedence of <B>&amp;</B> is lower
than that of <B>&gt;=</B>, so the macro evaluation will surprise you. Once you
discover the problem, you can solve it by putting parentheses around everything
in the macro definition. (This is a good practice to use when creating
preprocessor macros.) Thus,</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>#define FLOOR(x,b) ((x)&gt;=(b)?0:1)</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Discovering the problem may be difficult,
however, and you may not find it until after you&#8217;ve taken the proper macro
behavior for granted. In the un-parenthesized version of the preceding macro,
<I>most</I> expressions will work correctly because the precedence of
<B>&gt;=</B> is lower than most of the operators like +, <B>/</B>, <B>&#8211;
&#8211;</B>, and even the bitwise shift operators. So you can easily begin to
think that it works with all expressions, including those using bitwise logical
operators.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The preceding problem can be solved with
careful programming practice: parenthesize everything in a macro. However, the
second difficulty is subtler. Unlike a normal function, every time you use an
argument <A NAME="Index1608"></A><A NAME="Index1609"></A>in a macro, that
argument is evaluated. As long as the macro is called only with ordinary
variables, this evaluation is benign, but if the evaluation of an argument has
side effects, then the results can be surprising and will definitely not mimic
function behavior.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">For example, this macro determines
whether its argument falls within a certain range:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>#define BAND(x) (((x)&gt;5 &amp;&amp; (x)&lt;10) ? (x) : 0)</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As long as you use an
&#8220;ordinary&#8221; argument, the macro works very much like a real function.
But as soon as you relax and start believing it <I>is</I> a real function, the
problems start. Thus:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C09:MacroSideEffects.cpp</font>
#include <font color=#004488>"../require.h"</font>
#include &lt;fstream&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

#define BAND(x) (((x)&gt;5 &amp;&amp; (x)&lt;10) ? (x) : 0)

<font color=#0000ff>int</font> main() {
  ofstream out(<font color=#004488>"macro.out"</font>);
  assure(out, <font color=#004488>"macro.out"</font>);
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 4; i &lt; 11; i++) {
    <font color=#0000ff>int</font> a = i;
    out &lt;&lt; <font color=#004488>"a = "</font> &lt;&lt; a &lt;&lt; endl &lt;&lt; '\t';
    out &lt;&lt; <font color=#004488>"BAND(++a)="</font> &lt;&lt; BAND(++a) &lt;&lt; endl;
    out &lt;&lt; <font color=#004488>"\t a = "</font> &lt;&lt; a &lt;&lt; endl;
  }
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice the use of all upper-case
characters in the name of the macro. This is a helpful practice because it tells
the reader this is a macro and not a function, so if there are problems, it acts
as a little reminder.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here&#8217;s the output produced by the
program, which is not at all what you would have expected from a true
function:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>a = 4
  BAND(++a)=0
   a = 5
a = 5
  BAND(++a)=8
   a = 8
a = 6
  BAND(++a)=9
   a = 9
a = 7
  BAND(++a)=10
   a = 10
a = 8
  BAND(++a)=0
   a = 10
a = 9
  BAND(++a)=0
   a = 11
a = 10
  BAND(++a)=0
   a = 12</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When <B>a</B> is four, only the first
part of the conditional occurs, so the expression is evaluated only once, and
the side effect of the macro call is that <B>a</B> becomes five, which is what
you would expect from a normal function call in the same situation. However,
when the number is within the band, both conditionals are tested, which results
in two increments. The result is produced by evaluating the argument again,
which results in a third increment. Once the number gets out of the band, both
conditionals are still tested so you get two increments. The side effects are
different, depending on the argument.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is clearly not the kind of behavior
you want from a macro that looks like a function call. In this case, the obvious
solution is to make it a true function, which of course adds the extra overhead
and may reduce efficiency if you call that function a lot. Unfortunately, the
problem may not always be so obvious, and you can unknowingly get a library that
contains functions and macros mixed together, so a problem like this can hide
some very difficult-to-find bugs. For example, the

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -