📄 ec1.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<title>Effective C++, 2E | Shifting from C to C++</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 0; setCurrentMax(0);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT>
var dingbase = "EC1_DIR.HTM";
var dingtext = "EC++ C to C++, P";
if (self == top) {
top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="EC++ Chapter Intro: Shifting from C to C++" -->
<A NAME="222826"></A><A NAME="222823"></A><A NAME="p13"></A><DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./ECINTRFR.HTM" TARGET="_top">Introduction</A>
<BR>Continue to <A HREF="#1790">Item 1: Prefer const and inline to #define.</A></FONT></DIV>
<P><A NAME="dingp1"></A><FONT ID="egtitle">Shifting from C to C++</FONT><SCRIPT>create_link(1);</SCRIPT>
</P>
<P><A NAME="dingp2"></A>
Getting used to C++ takes a little while for everyone, but for grizzled C programmers, the process can be especially unnerving. Because C is effectively a subset of C++, all the old C tricks continue to work, but many of them are no longer appropriate. To C++ programmers, for example, a pointer to a pointer looks a little funny. Why, we wonder, wasn't a reference to a pointer used <NOBR>instead?<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>
<A NAME="1787"></A>
<P><A NAME="dingp3"></A>
C is a fairly simple language. All it really offers is macros, pointers, structs, arrays, and functions. No matter what the problem is, the solution will always boil down to macros, pointers, structs, arrays, and functions. Not so in C++. The macros, pointers, structs, arrays and functions are still there, of course, but so are private and protected members, function overloading, default parameters, constructors and destructors, user-defined operators, inline functions, references, friends, templates, exceptions, namespaces, and more. The design space is much richer in C++ than it is in C: there are just a lot more options to <NOBR>consider.<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P>
<A NAME="1788"></A>
<P><A NAME="dingp4"></A>
When faced with such a variety of choices, many C programmers hunker down and hold tight to what they're used to. For the most part, that's no great sin, but some C habits run contrary to the spirit of C++. Those are the ones that have simply <I>got</I> to <NOBR>go.<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<!-- SectionName="E1: Prefer const and inline to #define" -->
<A NAME="1790"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="#222823">Shifting from C to C++</NOBR></A><BR>Continue to <A HREF="#95970">Item 2: Prefer <CODE><iostream></CODE> to <CODE><stdio.h></CODE></A>.</FONT></DIV>
<P><A NAME="dingp5"></A><FONT ID="eititle">Item 1: Prefer <CODE>const</CODE> and <CODE>inline</CODE> to <CODE>#define</CODE>.</FONT><SCRIPT>create_link(5);</SCRIPT>
</P>
<A NAME="1791"></A>
<P><A NAME="dingp6"></A>
This Item might better be called "prefer the compiler to the preprocessor," because <CODE>#define</CODE> is often treated as if it's not part of the language <I>per se</I>. That's one of its problems. When you do something like <NOBR>this,<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>
<A NAME="1792"></A>
<UL><PRE>#define ASPECT_RATIO 1.653
</PRE>
</UL><A NAME="1793"></A>
<P><A NAME="dingp7"></A>
the symbolic name <CODE>ASPECT_RATIO</CODE> may never be seen by compilers; it may be removed by the preprocessor before the source code ever gets <A NAME="p14"></A>to a compiler. As a result, the name <CODE>ASPECT_RATIO</CODE> may not get entered into the symbol table. This can be confusing if you get an error during compilation involving the use of the constant, because the error message may refer to <CODE>1.653</CODE>, not <CODE>ASPECT_RATIO</CODE>. If <CODE>ASPECT_RATIO</CODE> was defined in a header file you didn't write, you'd then have no idea where that <CODE>1.653</CODE> came from, and you'd probably waste time tracking it down. This problem can also crop up in a symbolic debugger, because, again, the name you're programming with may not be in the symbol <NOBR>table.<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P>
<A NAME="1794"></A>
<P><A NAME="dingp8"></A>
The solution to this sorry scenario is simple and succinct. Instead of using a preprocessor macro, define a <NOBR>constant:<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="1795"></A>
<UL><PRE>const double ASPECT_RATIO = 1.653;
</PRE>
</UL><A NAME="1796"></A>
<P><A NAME="dingp9"></A>
This approach works like a charm. There are two special cases worth mentioning, <NOBR>however.<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P>
<A NAME="12480"></A>
<P><A NAME="dingp10"></A>
First, things can get a bit tricky when defining constant pointers. Because constant definitions are typically put in header files (where many different source files will include them), it's important that the <I>pointer</I> be declared <CODE>const</CODE>, usually in addition to what the pointer points to. To define a constant <CODE>char*</CODE>-based string in a header file, for example, you have to write <CODE>const</CODE> <I>twice</I>:<SCRIPT>create_link(10);</SCRIPT>
</P>
<A NAME="12482"></A>
<UL><PRE>const char * const authorName = "Scott Meyers";
</PRE>
</UL><A NAME="12483"></A>
<P><A NAME="dingp11"></A>
For a discussion of the meanings and uses of <CODE>const</CODE>, especially in conjunction with pointers, see <A HREF="./EC4_FR.HTM#6003" TARGET="_top">Item 21</A>.<SCRIPT>create_link(11);</SCRIPT>
</P>
<A NAME="1797"></A>
<P><A NAME="dingp12"></A>
Second, it's often convenient to define class-specific constants, and that calls for a slightly different tack. To limit the scope of a constant to a class, you must make it a member, and to ensure there's at most one copy of the constant, you must make it a <I>static</I> <NOBR>member:<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<A NAME="12504"></A>
<UL><PRE>
class GamePlayer {
private:
static const int NUM_TURNS = 5; // constant declaration
int scores[NUM_TURNS]; // use of constant
...
};
</PRE>
</UL>
<A NAME="33322"></A>
<P><A NAME="dingp13"></A>
There's a minor wrinkle, however, which is that what you see above is a <I>declaration</I> for <CODE>NUM_TURNS</CODE>, not a definition. You must still define static class members in an implementation <NOBR>file:<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>
<A NAME="12506"></A>
<UL><PRE>
const int GamePlayer::NUM_TURNS; // mandatory definition;
// goes in class impl. file
</PRE>
</UL><A NAME="12510"></A>
<P><A NAME="dingp14"></A>
There's no need to lose sleep worrying about this detail. If you forget the definition, your linker should remind <NOBR>you.<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>
<A NAME="12511"></A>
<P><A NAME="dingp15"></A>
<A NAME="p15"></A>Older compilers may not accept this syntax, because it used to be illegal to provide an initial value for a static class member at its point of declaration. Furthermore, in-class initialization is allowed only for integral types (e.g., <CODE>int</CODE>s, <CODE>bool</CODE>s, <CODE>char</CODE>s, etc.), and only for constants. In cases where the above syntax can't be used, you put the initial value at the point of <NOBR>definition:<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P>
<A NAME="12519"></A>
<UL><PRE>
class EngineeringConstants { // this goes in the class
private: // header file
</PRE>
</UL><A NAME="12878"></A>
<UL><PRE>
static const double FUDGE_FACTOR;
</PRE>
</UL><A NAME="12879"></A>
<UL><PRE>
...
</PRE>
</UL><A NAME="12880"></A>
<UL><PRE>
};
</PRE>
</UL><A NAME="12520"></A>
<UL><PRE>
// this goes in the class implementation file
const double EngineeringConstants::FUDGE_FACTOR = 1.35;
</PRE>
</UL><A NAME="12495"></A>
<P><A NAME="dingp16"></A>
This is all you need almost all the time. The only exception is when you need the value of a class constant during compilation of the class, such as in the declaration of the array <CODE>GamePlayer::scores</CODE> above (where compilers insist on knowing the size of the array during compilation). Then the accepted way to compensate for compilers that (incorrectly) forbid the in-class specification of initial values for integral class constants is to use what is affectionately known as "the enum hack." This technique takes advantage of the fact that the values of an enumerated type can be used where <CODE>int</CODE>s are expected, so <CODE>GamePlayer</CODE> could just as well have been defined like <NOBR>this:<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>
<A NAME="12534"></A>
<UL><PRE>
class GamePlayer {
private:
enum { NUM_TURNS = 5 }; // "the enum hack" — makes
// NUM_TURNS a symbolic name
// for 5
</PRE>
</UL><A NAME="12649"></A>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -