📄 chapter09.html
字号:
infinite.)</FONT><A NAME="_Toc312373933"></A><A NAME="_Toc472654907"></A><BR></P></DIV>
<A NAME="Heading288"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Hidden activities in constructors & destructors</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Constructors
<A NAME="Index1659"></A><A NAME="Index1660"></A>and destructors
<A NAME="Index1661"></A><A NAME="Index1662"></A>are two places where you can be
fooled into thinking that an inline
<A NAME="Index1663"></A><A NAME="Index1664"></A>is more efficient than it
actually is. Constructors and destructors may have hidden activities, because
the class can contain subobjects whose constructors and destructors must be
called. These subobjects may be member objects, or they may exist because of
inheritance (covered in Chapter 14). As an example of a class with member
objects:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C09:Hidden.cpp</font>
<font color=#009900>// Hidden activities in inlines</font>
#include <iostream>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>class</font> Member {
<font color=#0000ff>int</font> i, j, k;
<font color=#0000ff>public</font>:
Member(<font color=#0000ff>int</font> x = 0) : i(x), j(x), k(x) {}
~Member() { cout << <font color=#004488>"~Member"</font> << endl; }
};
<font color=#0000ff>class</font> WithMembers {
Member q, r, s; <font color=#009900>// Have constructors</font>
<font color=#0000ff>int</font> i;
<font color=#0000ff>public</font>:
WithMembers(<font color=#0000ff>int</font> ii) : i(ii) {} <font color=#009900>// Trivial?</font>
~WithMembers() {
cout << <font color=#004488>"~WithMembers"</font> << endl;
}
};
<font color=#0000ff>int</font> main() {
WithMembers wm(1);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The constructor for <B>Member</B> is
simple enough to inline, since there’s nothing special going on – no
inheritance or member objects are causing extra hidden activities. But in
<B>class WithMembers</B> there’s more going on than meets the eye. The
constructors and destructors for the member objects <B>q</B>, <B>r</B>, and
<B>s</B> are being called automatically, and <I>those</I> constructors and
destructors are also inline, so the difference is significant from normal member
functions. This doesn’t necessarily mean that you should always make
constructor and destructor definitions non-inline; there are cases in which it
makes sense. Also, when you’re making an initial “sketch” of a
program by quickly writing code, it’s often more
<A NAME="Index1665"></A>convenient to use inlines. But if you’re concerned
about efficiency, it’s a place to
look.</FONT><A NAME="_Toc305593214"></A><A NAME="_Toc305628686"></A><A NAME="_Toc312373934"></A><A NAME="_Toc472654908"></A><BR></P></DIV>
<A NAME="Heading289"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Reducing clutter</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In a book like this, the simplicity and
terseness of putting inline definitions inside classes is very useful because
more fits on a page or screen (in a seminar). However, Dan
Saks<A NAME="Index1666"></A></FONT><A NAME="fnB46" HREF="#fn46">[46]</A><A NAME="Index1667"></A><FONT FACE="Georgia">
has pointed out that in a real project this has the effect of needlessly
cluttering the class interface and thereby making the class harder to use. He
refers to member functions defined within classes using the Latin <I>in situ</I>
<A NAME="Index1668"></A><A NAME="Index1669"></A>(in place) and maintains that
all definitions should be placed outside the class to keep the interface clean.
Optimization, he argues, is a separate issue. If you want to optimize, use the
<A NAME="Index1670"></A><A NAME="Index1671"></A><B>inline</B> keyword. Using
this approach, the earlier <B>Rectangle.cpp</B> example
becomes:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C09:Noinsitu.cpp</font>
<font color=#009900>// Removing in situ functions</font>
<font color=#0000ff>class</font> Rectangle {
<font color=#0000ff>int</font> width, height;
<font color=#0000ff>public</font>:
Rectangle(<font color=#0000ff>int</font> w = 0, <font color=#0000ff>int</font> h = 0);
<font color=#0000ff>int</font> getWidth() <font color=#0000ff>const</font>;
<font color=#0000ff>void</font> setWidth(<font color=#0000ff>int</font> w);
<font color=#0000ff>int</font> getHeight() <font color=#0000ff>const</font>;
<font color=#0000ff>void</font> setHeight(<font color=#0000ff>int</font> h);
};
<font color=#0000ff>inline</font> Rectangle::Rectangle(<font color=#0000ff>int</font> w, <font color=#0000ff>int</font> h)
: width(w), height(h) {}
<font color=#0000ff>inline</font> <font color=#0000ff>int</font> Rectangle::getWidth() <font color=#0000ff>const</font> {
<font color=#0000ff>return</font> width;
}
<font color=#0000ff>inline</font> <font color=#0000ff>void</font> Rectangle::setWidth(<font color=#0000ff>int</font> w) {
width = w;
}
<font color=#0000ff>inline</font> <font color=#0000ff>int</font> Rectangle::getHeight() <font color=#0000ff>const</font> {
<font color=#0000ff>return</font> height;
}
<font color=#0000ff>inline</font> <font color=#0000ff>void</font> Rectangle::setHeight(<font color=#0000ff>int</font> h) {
height = h;
}
<font color=#0000ff>int</font> main() {
Rectangle r(19, 47);
<font color=#009900>// Transpose width & height:</font>
<font color=#0000ff>int</font> iHeight = r.getHeight();
r.setHeight(r.getWidth());
r.setWidth(iHeight);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now if you want to compare the effect of
inline functions to non-inline functions, you can simply remove the
<B>inline</B> keyword. (Inline functions should normally be put in header files,
however, while non-inline functions must reside in their own translation unit.)
If you want to put the functions into documentation, it’s a simple
cut-and-paste operation. <I>In situ</I> functions require more work and have
greater potential for errors. Another argument for this approach is that you can
always produce a consistent formatting style for function definitions, something
that doesn’t always occur with <I>in situ</I>
functions.</FONT><A NAME="_Toc305593215"></A><A NAME="_Toc305628687"></A><A NAME="_Toc312373935"></A><A NAME="_Toc472654909"></A><BR></P></DIV>
<A NAME="Heading290"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
More preprocessor features</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Earlier, I said that you <I>almost</I>
always want to use <B>inline</B> functions instead of preprocessor macros. The
exceptions are when you need to use three special features in the C preprocessor
(which is also the C++ preprocessor):
stringizing<A NAME="Index1672"></A><A NAME="Index1673"></A>, string
concatenation, <A NAME="Index1674"></A><A NAME="Index1675"></A>and token
pasting<A NAME="Index1676"></A><A NAME="Index1677"></A>. Stringizing, introduced
earlier in the book, is performed with the <B>#</B> directive and allows you to
take an identifier and turn it into a character array. String concatenation
takes place when two adjacent character arrays have no intervening punctuation,
in which case they are combined. These two features are especially useful when
writing <A NAME="Index1678"></A>debug code. Thus,</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>#define DEBUG(x) cout << #x <font color=#004488>" = "</font> << x << endl</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This prints the value of any variable.
You can also get a trace that prints out the statements as they
execute:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>#define TRACE(s) cerr << #s << endl; s</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>#s</B> stringizes the statement
for output, and the second <B>s</B> reiterates the statement so it is executed.
Of course, this kind of thing can cause problems, especially in one-line
<B>for</B> loops:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < 100; i++)
TRACE(f(i));</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because there are actually two statements
in the <B>TRACE( )</B> macro, the one-line <B>for</B> loop executes only
the first one. The solution is to replace the semicolon with a comma in the
macro.</FONT><A NAME="_Toc312373936"></A><A NAME="_Toc472654910"></A><BR></P></DIV>
<A NAME="Heading291"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Token pasting</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Token pasting, implemented with the
<B>##</B> directive, is very useful when you are manufacturing code. It allows
you to take two identifiers and paste them together to automatically create a
new identifier. For example,</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>#define FIELD(a) <font color=#0000ff>char</font>* a##_string; <font color=#0000ff>int</font> a##_size
<font color=#0000ff>class</font> Record {
FIELD(one);
FIELD(two);
FIELD(three);
<font color=#009900>// ...</font>
}; </PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Each call to the <B>FIELD( )</B>
macro creates an identifier to hold a character array and another to hold the
length of that array. Not only is it easier to read, it can eliminate coding
errors and make maintenance easier.
</FONT><A NAME="_Toc312373937"></A><A NAME="_Toc472654911"></A><BR></P></DIV>
<A NAME="Heading292"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Improved error checking</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The
<A NAME="Index1679"></A><A NAME="Index1680"></A><B>require.h </B>functions have
been used up to this point without defining them (although
<A NAME="Index1681"></A><B>assert( )</B> has also been used to help detect
programmer errors where it’s appropriate). Now it’s time to define
this header file. <A NAME="Index1682"></A><A NAME="Index1683"></A>Inline
functions are convenient here because they allow everything to be placed in a
header file, which simplifies the process of using the package. You just include
the header file and you don’t need to worry about linking an
implementation file.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You should note that exceptions
(presented in detail in Volume 2 of this book) provide a much more effective way
of handling many kinds of errors – especially those that you’d like
to recover from – instead of just halting the program. The conditions that
<B>require.h</B> handles, however, are ones which prevent the continuation of
the program, such as if the user doesn’t provide enough command-line
arguments or if a file cannot be opened. Thus, it’s acceptable that they
call the Standard C Library function
<A NAME="Index1684"></A><B>exit( )</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The following header file is placed in
the book’s root directory so it’s easily accessed from all
chapters.</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: :require.h</font>
<font color=#009900>// Test for error conditions in programs</font>
<font color=#009900>// Local "using namespace std" for old compilers</font>
#ifndef REQUIRE_H
#define REQUIRE_H
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <string>
<font color=#0000ff>inline</font> <font color=#0000ff>void</font> require(<font color=#0000ff>bool</font> requirement,
<font color=#0000ff>const</font> std::string& msg = <font color=#004488>"Requirement failed"</font>){
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>if</font> (!requirement) {
fputs(msg.c_str(), stderr);
fputs(<font color=#004488>"\n"</font>, stderr);
exit(1);
}
}
<font color=#0000ff>inline</font> <font color=#0000ff>void</font> requireArgs(<font color=#0000ff>int</font> argc, <font color=#0000ff>int</font> args,
<font color=#0000ff>const</font> std::string& msg =
<font color=#004488>"Must use %d arguments"</font>) {
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>if</font> (argc != args + 1) {
fprintf(stderr, msg.c_str(), args);
fputs(<font color=#004488>"\n"</font>, stderr);
exit(1);
}
}
<
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -