How can we use a class Foo
in a header file without access to its definition?
- We can declare data members of type
Foo*
orFoo&
. - We can declare (but not define) functions with arguments, and/or return values, of type
Foo
. (One exception is if an argumentFoo
orconst Foo&
has a non-explicit
, one-argument constructor, in which case we need the full definition to support automatic type conversion.)(程序中直接用Foo做参数是不好滴) - We can declare static data members of type
Foo
. This is because static data members are defined outside the class definition.
④
Stru cts v s.
The struct
and class
keywords behave almost identically in C++. We add our own semantic meanings to each keyword, so you should use the appropriate keyword for the data-type you're defining.
structs
should be used for passive objects that carry data, and may have associated constants, but lack any functionality other than access/setting the data members. The accessing/setting of fields is done by directly accessing the fields rather than through method invocations. Methods should not provide behavior but should only be used to set up the data members, e.g., constructor, destructor, Initialize()
, Reset()
, Validate()
.
If more functionality is required, a class
is more appropriate. If in doubt, make it a class
.
For consistency with STL, you can use struct
instead of class
for functors and traits.
Note that member variables in structs and classes have different naming rules.
⑤
All inheritance should be public
. If you want to do private inheritance, you should be including an instance of the base class as a member instead.
Do not overuse implementation inheritance. Composition is often more appropriate. Try to restrict use of inheritance to the "is-a" case: Bar
subclasses Foo
if it can reasonably be said that Bar
"is a kind of" Foo
.
Make your destructor virtual
if necessary. If your class has virtual methods, its destructor should be virtual.
Limit the use of protected
to those member functions that might need to be accessed from subclasses. Note that data members should be private.
When redefining an inherited virtual function, explicitly declare it virtual
in the declaration of the derived class. Rationale: If virtual
is omitted, the reader has to check all ancestors of the class in question to determine if the function is virtual or not.
⑥
Do not put large method definitions inline in the class definition. Usually, only trivial or performance-critical, and very short, methods may be defined inline. See Inline Functions for more details.
⑦
函数不要太长
We recognize that long functions are sometimes appropriate, so no hard limit is placed on functions length.If a function exceeds about 40 lines, think about whether it can be broken up without harming the structure of the program.
Even if your long function works perfectly now, someone modifying it in a few months may add new behavior. This could result in bugs that are hard to find. Keeping your functions short and simple makes it easier for other people to read and modify your code.
Within function parameter lists all references must be const
:
void Foo(const string &in, string *out);
In fact it is a very strong convention in Google code that input arguments are values or const
references while output arguments are pointers. Input parameters may be const
pointers, but we never allow non-const
reference parameters.
However, there are some instances where using const T*
is preferable to const T&
for input parameters. For example: You want to pass in NULL. The function saves a pointer or reference to the input. Remember that most of the time input parameters are going to be specified as const T&
. Using const T*
instead communicates to the reader that the input is somehow treated differently. So if you choose const T*
rather than const T&
, do so for a concrete reason; otherwise it will likely confuse readers by making them look for an explanation that doesn't exist.
⑨
类型转换
Do not use C-style casts. Instead, use these C++-style casts.
- Use
static_cast
as the equivalent of a C-style cast that does value conversion, or when you need to explicitly up-cast a pointer from a class to its superclass. - Use
const_cast
to remove theconst
qualifier (see const). - Use
reinterpret_cast
to do unsafe conversions of pointer types to and from integer and other pointer types. Use this only if you know what you are doing and you understand the aliasing issues. - Do not use
dynamic_cast
except in test code. If you need to know type information at runtime in this way outside of a unittest, you probably have a design flaw.
const
variables, data members, methods and arguments add a level of compile-time type checking; it is better to detect errors as soon as possible. Therefore we strongly recommend that you use const
whenever it makes sense to do so:
- If a function does not modify an argument passed by reference or by pointer, that argument should be
const
. - Declare methods to be
const
whenever possible. Accessors should almost always beconst
. Other methods should be const if they do not modify any data members, do not call any non-const
methods, and do not return a non-const
pointer or non-const
reference to a data member. - Consider making data members
const
whenever they do not need to be modified after construction.
However, do not go crazy with const
. Something like const int * const * const x;
is likely overkill, even if it accurately describes how const x is. Focus on what's really useful to know: in this case, const int** x
is probably sufficient.
The mutable
keyword is allowed but is unsafe when used with threads, so thread safety should be carefully considered first.
Where to put the const
Some people favor the form int const *foo
to const int* foo
. They argue that this is more readable because it's more consistent: it keeps the rule that const
always follows the object it's describing. However, this consistency argument doesn't apply in this case, because the "don't go crazy" dictum eliminates most of the uses you'd have to be consistent with. Putting the const
first is arguably more readable, since it follows English in putting the "adjective" (const
) before the "noun" (int
).
That said, while we encourage putting const
first, we do not require it. But be consistent with the code around you!
11.整形类型的使用
<stdint.h>
defines types like int16_t
, uint32_t
, int64_t
, etc. You should always use those in preference to short
, unsigned long long
and the like, when you need a guarantee on the size of an integer. Of the C integer types, only int
should be used. When appropriate, you are welcome to use standard types like size_t
and ptrdiff_t
.
We use int
very often, for integers we know are not going to be too big, e.g., loop counters. Use plain old int
for such things. You should assume that an int
is at least 32 bits, but don't assume that it has more than 32 bits. If you need a 64-bit integer type, use int64_t
or uint64_t
.
For integers we know can be "big", use int64_t
.
You should not use the unsigned integer types such as uint32_t
, unless the quantity you are representing is really a bit pattern rather than a number, or unless you need defined twos-complement overflow. In particular, do not use unsigned types to say a number will never be negative. Instead, use assertions for this.
On Unsigned Integers
Some people, including some textbook authors, recommend using unsigned types to represent numbers that are never negative. This is intended as a form of self-documentation. However, in C, the advantages of such documentation are outweighed by the real bugs it can introduce. Consider:
for (unsigned int i = foo.Length()-1; i >= 0; --i) ...
This code will never terminate! Sometimes gcc will notice this bug and warn you, but often it will not. Equally bad bugs can occur when comparing signed and unsigned variables. Basically, C's type-promotion scheme causes unsigned types to behave differently than one might expect.
So, document that a variable is non-negative using assertions. Don't use an unsigned type.
12.
0 和 NULL
Use 0
for integers, 0.0
for reals, NULL
for pointers, and '\0'
for chars.
sizeof(varname)
instead of
sizeof(type)
whenever possible.
Use sizeof(varname)
because it will update appropriately if the type of the variable changes. sizeof(type)
may make sense in some cases, but should generally be avoided because it can fall out of sync if the variable's type changes.
Struct data; memset(&data, 0, sizeof(data));
memset(&data, 0, sizeof(Struct));
if(condition) // Bad - space missing after IF. if (condition){ // Bad - space missing before {. if(condition){ // Doubly bad.
if (condition) { // Good - proper space after IF and before {.
Short conditional statements may be written on one line if this enhances readability. You may use this only when the line is brief and the statement does not use the else
clause.
if (x == kFoo) return new Foo(); if (x == kBar) return new Bar();
If not conditional on an enumerated value, switch statements should always have a default
case (in the case of an enumerated value, the compiler will warn you if any values are not handled). If the default case should never execute, simply assert
:
switch (var) { case 0: { // 2 space indent ... // 4 space indent break; } case 1: { ... break; } default: { assert(false); } }
Empty loop bodies should use {}
or continue
, but not a single semicolon.
while (condition) { // Repeat test until it returns false. } for (int i = 0; i < kSomeNumber; ++i) {} // Good - empty body. while (condition) continue; // Good - continue indicates no logic.
while (condition); // Bad - looks like part of do/while loop.
When declaring a pointer variable or argument, you may place the asterisk adjacent to either the type or to the variable name:
// These are fine, space preceding. char *c; const string &str; // These are fine, space following. char* c; // but remember to do "char* c, *d, *e, ...;"! const string& str;
char * c; // Bad - spaces on both sides of * const string & str; // Bad - spaces on both sides of &
You should do this consistently within a single file, so, when modifying an existing file, use the style in that file.
return
expression with parentheses.
Use parentheses in return expr;
only where you would use them in x = expr;
.
return result; // No parentheses in the simple case. return (some_long_condition && // Parentheses ok to make a complex another_condition); // expression more readable.
return (value); // You wouldn't write var = (value); return(result); // return is not a function!
Even when preprocessor directives are within the body of indented code, the directives should start at the beginning of the line.
// Good - directives at beginning of line if (lopsided_score) { #if DISASTER_PENDING // Correct -- Starts at beginning of line DropEverything(); # if NOTIFY // OK but not required -- Spaces after # NotifyClient(); # endif #endif BackToNormal(); }
// Bad - indented directives if (lopsided_score) { #if DISASTER_PENDING // Wrong! The "#if" should be at beginning of line DropEverything(); #endif // Wrong! Do not indent "#endif" BackToNormal(); }
The basic format for a class declaration (lacking the comments, see Class Comments for a discussion of what comments are needed) is:
class MyClass : public OtherClass { public: // Note the 1 space indent! MyClass(); // Regular 2 space indent. explicit MyClass(int var); ~MyClass() {} void SomeFunction(); void SomeFunctionThatDoesNothing() { } void set_some_var(int var) { some_var_ = var; } int some_var() const { return some_var_; } private: bool SomeInternalFunction(); int some_var_; int some_other_var_; DISALLOW_COPY_AND_ASSIGN(MyClass); };
Things to note:
- Any base class name should be on the same line as the subclass name, subject to the 80-column limit.
- The
public:
,protected:
, andprivate:
keywords should be indented one space. - Except for the first instance, these keywords should be preceded by a blank line. This rule is optional in small classes.
- Do not leave a blank line after these keywords.
- The
public
section should be first, followed by theprotected
and finally theprivate
section. - See Declaration Order for rules on ordering declarations within each of these sections.
.h
, your project's
.h
.
All of a project's header files should be listed as descendants of the project's source directory without use of UNIX directory shortcuts .
(the current directory) or ..
(the parent directory). For example, google-awesome-project/src/base/logging.h
should be included as
#include "base/logging.h"
In dir/foo.cc
or dir/foo_test.cc
, whose main purpose is to implement or test the stuff in dir2/foo2.h
, order your includes as follows:
dir2/foo2.h
(preferred location — see details below).- C system files.
- C++ system files.
- Other libraries'
.h
files. - Your project's
.h
files.
With the preferred ordering, if dir/foo2.h
omits any necessary includes, the build of dir/foo.cc
or dir/foo_test.cc
will break. Thus, this rule ensures that build breaks show up first for the people working on these files, not for innocent people in other packages.
dir/foo.cc
and dir2/foo2.h
are often in the same directory (e.g. base/basictypes_test.cc
and base/basictypes.h
), but can be in different directories too.
Within each section it is nice to order the includes alphabetically.
For example, the includes in google-awesome-project/src/foo/internal/fooserver.cc
might look like this:
#include "foo/public/fooserver.h" // Preferred location. #include <sys/types.h> #include <unistd.h> #include <hash_map> #include <vector> #include "base/basictypes.h" #include "base/commandlineflags.h" #include "foo/public/bar.h"