📄 csdn_文档中心_微软office的源代码样式规范(上) —— 绝对机密文档!!!.htm
字号:
etc). As a bonus, the debugger will even know about this
symbol. For example,<BR>// C++ solution:<BR>const DX dxMin =
0; // type safe, optimized, and debug symbol</P>
<P>So true C++ constants are preferred to the traditional C++
#define. Note that they cannot be used in shared C/C++ header
files, though, because a C compiler will just allocate memory for
them.<BR>C++ also makes the existing C concept of an enum type
safe. An enum in C++ defines a type and declares constants of
that type. You can then use that type as, say, a parameter to
a function, and the compiler can then enforce that you pass one of
the symbols defined in the enumeration (or you can type cast to get
around this if you need to). An enum can also be made local to
a class so that its scope is limited. For example,<BR>class
Foo<BR> {<BR>public:<BR> enum GMODE { gmNo = 0, gmYes = 1,
gmMaybe = -1 };<BR> void InsertGNode(GMODE gm);<BR> };</P>
<P>Summary:<BR> Use const or enum instead of #define for constants
that are only used in C++.<BR>3.2 References<BR>C++ adds the ability
to express references to objects, and the primary use of them is to
pass classes as parameters without the overhead of the copy
constructor being called. This is a worthy goal, but a more
straightforward method to do this is to just to pass classes by
pointer, which is what we抮e all used to from C. For someone
used to C, seeing something being passed by reference looks like it抯
being passed by value, so you might wonder if the constructor is
being called, or whatever. Furthermore, when using a
reference, the illusion is that you have a local copy of the object
that you can reference cheaply, but in fact you just have a pointer
to the object (this is how the compiler does it), and every access
is an indirection. We should just make this indirection
explicit by actually using pointers. Typing “*” or “->“
instead of “.” is not a big deal, and it makes it more clear what is
going on. The one real advantage of references over pointers
is that they are guaranteed to be initialized (they cannot be NULL
or point to garbage). But this advantage is not worth the
above problems for us.<BR>Also note that when you do pass objects by
pointer, use const to mark formal parameters that are read-only (see
the "Const" section below). This is related to references
because some C++ programmers will use the convention that read-only
objects are passed by references and other objects are passed by
pointer (to make this safe you still need to the declare the
reference const because C++ will let you change a parameter through
a reference). This is a reasonable convention, but it still
has the problem of looking foreign and confusing to programmers with
a C background. So, we will use const to get the safety but
pass every object by pointer.<BR>There are other more exotic uses of
references, such as being able to return an lvalue from an operator
function, and sometimes this is necessary if you've defined such an
operator. But since we don抰 plan to use operators much if at
all (because we won抰 use stack-based classes), we should be able to
avoid references in most cases.<BR>Summary:<BR> Avoid
references. Pass objects that are larger than an 搃nt” by
pointer.<BR>3.3 Const Parameters and Functions<BR>As mentioned
above, you should use const to mark formal parameters that are
read-only. This allows the compiler to check that you actually
obey this, serves as a form of documentation to users of your
function, and also allows the compiler to produce better code in
some cases. For example:<BR>/* Copy the contents of 'fooSrc'
into 'fooDst'. */<BR>void CopyFoo(const FOO *fooSrc, FOO
*fooDst);</P>
<P>You can also declare non-pointer formal parameters as const (as
well as the actual pointer portion of a pointer parameter, rather
than what it points at, in which case the word "const" may appear
twice for that parameter), but this is not as much of a win and it
can make the prototype harder to read, so it's optional. This
just makes sure that you don't reuse the parameter itself as a local
variable and change its value. Of course, sometimes a function
will do this as a way of avoiding declaring and setting up a local
variable, so in this case you can't use const (not that this is not
great programming style, but we're not going to disallow it).
On the other hand, if you don't change the value of the parameter
within the function, declaring it as const may allow the compiler to
generate better code. Note that doing this does not give any
useful documentation to the caller, though. For example:<BR>/*
Copy 'cb' bytes of the contents of 'fooSrc' into
'fooDst'.<BR> In addition to not changing what 'fooSrc'
points at, my implementation<BR> promises not to change
the values of any of the local parameters<BR> within the
function (like you care...). */<BR>void CopyFooCb(const FOO *const
fooSrc, FOO *const fooDst, const int cb);</P>
<P>In addition to declaring parameters const, you can also declare a
member function const to indicate that the function does not modify
the object. Again, this allows compiler checks and possible
optimization as well as a form of documentation. For
example:<BR>class Counter<BR> {<BR>public:<BR> int
CItems() const { return m_cItems; }<BR> void SetCItems(int
cItems) { m_cItems = cItems; }<BR>private:<BR> int
m_cItems;<BR> };</P>
<P>Summary:<BR> Use const to mark read-only pointer parameters
(what the pointer points at, not the pointer itself).<BR> Use const
to mark member functions that don't change the object<BR> Use const
to mark parameters themselves only if you care about the possible
performance gains in the implementation.<BR>3.4 Default
Arguments<BR>Having default arguments seems like a cool
feature. It seems like a way to add a parameter which only
some calls to a function will need to pass, so that the simple cases
will be kept simple. Well unfortunately, there is no
efficiency gain here, and instead the compiler is just hiding work
from you. If you have a function with one required argument
and four optional arguments, every call to this function will push
all five arguments, so you are getting the code size and time hit in
every case. Furthermore, you can抰 even use default arguments
just to try something new without bothering with the old calls
because you still have to find all the old calls in order to rebuild
those files (if you do an incremental build of just one use after
adding a default argument, the other calls will screw up).
Finally, default arguments can be easily confused with overloading
(which we抣l also avoid).<BR>There are cases, however, where a
certain parameter is totally irrelevant in a call (because, for
example, the value of another parameter tells you all you need to
know). Note that this is somewhat different from a default
argument because there is no real default value for this
parameter. In these cases, it is nice to have the untyped
constant "NA" , #defined to be 0, to stand for "Not applicable"
which can be passed to indicate this for any actual parameter.
This is better than passing, say, NULL for a pointer or FALSE for a
Boolean because it makes it clear that the value is not important at
all. For example:<BR>#define NA 0 //
universal "not applicable" parameter value</P>
<P>/* If fShow then show the object, else hide it. If showing
then<BR> redraw it only if fRedraw.<BR>void Show(BOOL
fShow, BOOL fRedraw);</P>
<P>void Ack()<BR>{<BR> Show(TRUE,
TRUE);<BR> ...<BR> Show(FALSE, NA);<BR>}</P>
<P>Summary:<BR> Don't use default arguments.<BR> Use "NA"
(#defined to be 0) for "not applicable" parameters in a call.<BR>3.5
Function Overloading<BR>Overloading functions is just a lazy naming
scheme. It seems like a form of polymorphism, but it shouldn抰
be confused with real polymorphism because all the decisions must be
made staticly at compile time. It抯 just a way to keep 搒imple”
function names and reuse them for different cases. Such a lazy
naming scheme just causes more confusion than it抯 worth (trying to
determine which function is relevant, changing the wrong one by
accident, etc.) and can also interfere with proper use of Hungarian
in some cases. Finally, the combination of function
overloading and type coercion can be quite confusing
indeed.<BR>Summary:<BR> Don抰 overload functions<BR>3.6 Operator
Overloading<BR>The main use of operators is in classes. This
is discussed in a previous section. Operators can also be
overloaded at global scope. For example, you can define what
operator+ should do when it finds a Foo on the left side and a Bar
on the right side. Unlike the use within a class, this allows
control over the left-hand side operand. Anyway, all the same
problems apply and more (due to the larger scope). Functionality is
hidden (a possible efficiency problem) and confusion can result, so
we will avoid this.<BR>Summary:<BR> Don抰 overload operators,
especially at global scope.<BR>4. Common C/C++ Issues<BR>The
following sections comment of features of regular C that we also try
to maintain a consistent use of.<BR>4.1 #ifdefs<BR>First and
foremost, everyone should try really hard to minimize the use of
#ifdefs. Programs with lots of #ifdefs are really hard to
read. It is often possible to either invent the right
abstraction or to isolate the #ifdefs to small places in header
files, or make the right definitions in target-specific headers so
as to make #ifdefs unnecessary in the main code. The main
argument for #ifdefs (over, say, a regular 搃f”) is to minimize the
code size. However, everyone should be aware that the
optimizer is perfectly capable of simplifying statements that
evaluate to a constant. For example,<BR>// Wrong:<BR>#ifdef
MAC<BR>if (x == 3 || Foo() || FSomeMacMode())<BR>#else<BR>if (x == 3
|| Foo())<BR>#endif</P>
<P>// Right:<BR>// In a header file for each non-Mac platform, there
is<BR>#define FSomeMacMode() FALSE</P>
<P>// Then the using code can just be <BR>if (x == 3 || Foo() ||
FSomeMacMode())</P>
<P>In this example, the compiler is perfectly capable of eliminating
what amounts to (搢| FALSE”) at compile-time. Furthermore, if
the entire "if" were to always evaluate to FALSE at compile time,
then the optimizer will also remove all of the code inside the "if"
and remove the test.<BR>If you must use an #ifdef, we prefer to use
#if instead because it's shorter and allows logical operations, as
in:<BR>int Foo()<BR>{<BR> int x = 3<BR> #if MAC &&
!DEBUG<BR> x = 0;<BR> #endif<BR> return
x;<BR>}</P>
<P>Note that we will still leave flags such as DEBUG undefined when
false, but the compiler does the right thing here (treats it the
same as being defined to be 0). Leaving these flags undefined
means that #ifdef will also work, in case this is used by accident
anywhere.<BR>Also, as this example shows, #ifs should be properly
indented so that they read easily when nested. Yes, this
works; C Compilers have accepted this for years.<BR>Aside from
the standard identifiers defined by our build process (e.g. DEBUG,
MAC), we will also use the identifiers UNUSED and LATER, which are
never defined, to mark code which is currently unused but kept in
for some reason, and code which cannot be activated yet but will
eventually, respectively.<BR>Summary:<BR> Minimize #ifdefs by
defining good abstractions, partitioning fil
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -