Google C++ Style Guide

Google C++ Style Guide
I found a very good C++ Style guide from google. I would try to code new c++ following that guide and see how it works or not.

http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml


Couple things I need to remember.

1. Sequence of Includes.
2. Unnamed namespaces for cpp file.
3. Don't use using-directive.
4. Initialization should be used instead of declaration and assignment for local variables.
5. Objects with static storage duration, including global variables, static variables, static class member variables, and function static variables, must be Plain Old Data (POD): only ints, chars, floats, or pointers, or arrays/structs of POD. string (use const char []).
6. Complex initialization should go in an explicit Init() method.
7. Use the C++ keyword explicit for constructors with one argument.
8. Provide both a copy constructor and assignment operator if the class needs to be stored by value in an STL container
9. 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.
10. Make your destructor virtual if necessary. If your class has virtual methods, its destructor should be virtual.
11. 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.
12. Do not overload operator==  or operator< just so that your class can be used as a key in an STL container; instead, you should create equality and comparison functor types when declaring the container.
13. Declaration Order
14. Smart Pointers. scoped_ptr is great. You should only use std::tr1::shared_ptr  under very specific conditions, such as when objects need to be held by STL containers. You should never use auto_ptr.
15. Within function parameter lists all references must be const.  const is viral: if you pass a const  variable to a function, that function must have const  in its prototype (or the variable will need a const_cast). This can be a particular problem when calling library functions.
16. Use C++ casts like static_cast<>(). Do not use other cast formats like int y = (int)x; or int y = int(x);
17. Do not use streams, except where required by a logging interface. Use printf-like routines instead.
18. Be very cautious with macros. Prefer inline functions, enums, and const variables to macros.
19. Use 0 for integers, 0.0 for reals, NULL for pointers, and '\0' for chars.
20. Use sizeof(varname) instead of sizeof(type) whenever possible.
21. Non-ASCII characters should be rare, and must use UTF-8 formatting.
 

In dir/foo.cc, whose main purpose is to implement or test the stuff in dir2/foo2.h, order your includes as follows:

   1. dir2/foo2.h (preferred location — see details below).
   2. C system files.
   3. C++ system files.
   4. Other libraries' .h files.
   5. Your project's .h files.

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"

Namespaces

Unnamed namespaces in .cc files are encouraged. With named namespaces, choose the name based on the project, and possibly its path. Do not use a using-directive.
link

Definition: Namespaces subdivide the global scope into distinct, named scopes, and so are useful for preventing name collisions in the global scope.

Pros:

Namespaces provide a (hierarchical) axis of naming, in addition to the (also hierarchical) name axis provided by classes.

For example, if two different projects have a class Foo in the global scope, these symbols may collide at compile time or at runtime. If each project places their code in a namespace, project1::Foo and project2::Foo are now distinct symbols that do not collide.

Cons:

Namespaces can be confusing, because they provide an additional (hierarchical) axis of naming, in addition to the (also hierarchical) name axis provided by classes.

Use of unnamed spaces in header files can easily cause violations of the C++ One Definition Rule (ODR).

Decision:

Use namespaces according to the policy described below.

Unnamed Namespaces

    * Unnamed namespaces are allowed and even encouraged in .cc files, to avoid runtime naming conflicts:

      namespace {                           // This is in a .cc file.

      // The content of a namespace is not indented
      enum { kUnused, kEOF, kError };       // Commonly used tokens.
      bool AtEof() { return pos_ == kEOF; }  // Uses our namespace's EOF.

      }  // namespace

      However, file-scope declarations that are associated with a particular class may be declared in that class as types, static data members or static member functions rather than as members of an unnamed namespace. Terminate the unnamed namespace as shown, with a comment // namespace.
    * Do not use unnamed namespaces in .h files.

Named Namespaces

Named namespaces should be used as follows:

    * Namespaces wrap the entire source file after includes, gflags definitions/declarations, and forward declarations of classes from other namespaces:

      // In the .h file
      namespace mynamespace {

      // All declarations are within the namespace scope.
      // Notice the lack of indentation.
      class MyClass {
       public:
        ...
        void Foo();
      };

      }  // namespace mynamespace

      // In the .cc file
      namespace mynamespace {

      // Definition of functions is within scope of the namespace.
      void MyClass::Foo() {
        ...
      }

      }  // namespace mynamespace

      The typical .cc file might have more complex detail, including the need to reference classes in other namespaces.

      #include "a.h"

      DEFINE_bool(someflag, false, "dummy flag");

      class C;  // Forward declaration of class C in the global namespace.
      namespace a { class A; }  // Forward declaration of a::A.

      namespace b {

      ...code for b...         // Code goes against the left margin.

      }  // namespace b

    * Do not declare anything in namespace std, not even forward declarations of standard library classes. Declaring entities in namespace std is undefined behavior, i.e., not portable. To declare entities from the standard library, include the appropriate header file.
    * You may not use a using-directive to make all names from a namespace available.

      // Forbidden -- This pollutes the namespace.
      using namespace foo;

    * You may use a using-declaration anywhere in a .cc file, and in functions, methods or classes in .h files.

      // OK in .cc files.
      // Must be in a function, method or class in .h files.
      using ::foo::bar;

    * Namespace aliases are allowed anywhere in a .cc file, and in functions and methods in .h files.

      // OK in .cc files.
      // Must be in a function or method in .h files.
      namespace fbz = ::foo::bar::baz;


Local Variables
?
Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.
link

C++ allows you to declare variables anywhere in a function. We encourage you to declare them in as local a scope as possible, and as close to the first use as possible. This makes it easier for the reader to find the declaration and see what type the variable is and what it was initialized to. In particular, initialization should be used instead of declaration and assignment, e.g.

int i;
i = f();      // Bad -- initialization separate from declaration.
int j = g();  // Good -- declaration has initialization.

Note that gcc implements for (int i = 0; i < 10; ++i) correctly (the scope of i is only the scope of the for loop), so you can then reuse i in another for loop in the same scope. It also correctly scopes declarations in if and while statements, e.g.

while (const char* p = strchr(str, '/')) str = p + 1;

There is one caveat: if the variable is an object, its constructor is invoked every time it enters scope and is created, and its destructor is invoked every time it goes out of scope.

// Inefficient implementation:
for (int i = 0; i < 1000000; ++i) {
  Foo f;  // My ctor and dtor get called 1000000 times each.
  f.DoSomething(i);
}

It may be more efficient to declare such a variable used in a loop outside that loop:

Foo f;  // My ctor and dtor get called once each.
for (int i = 0; i < 1000000; ++i) {
  f.DoSomething(i);
}

Static and Global Variables
?
Static or global variables of class type are forbidden: they cause hard-to-find bugs due to indeterminate order of construction and destruction.
link

Objects with static storage duration, including global variables, static variables, static class member variables, and function static variables, must be Plain Old Data (POD): only ints, chars, floats, or pointers, or arrays/structs of POD.

The order in which class constructors and initializers for static variables are called is only partially specified in C++ and can even change from build to build, which can cause bugs that are difficult to find. Therefore in addition to banning globals of class type, we do not allow static POD variables to be initialized with the result of a function, unless that function (such as getenv(), or getpid()) does not itself depend on any other globals.

Likewise, the order in which destructors are called is defined to be the reverse of the order in which the constructors were called. Since constructor order is indeterminate, so is destructor order. For example, at program-end time a static variable might have been destroyed, but code still running -- perhaps in another thread -- tries to access it and fails. Or the destructor for a static 'string' variable might be run prior to the destructor for another variable that contains a reference to that string.

As a result we only allow static variables to contain POD data. This rule completely disallows vector (use C arrays instead), or string (use const char []).

If you need a static or global variable of a class type, consider initializing a pointer (which will never be freed), from either your main() function or from pthread_once(). Note that this must be a raw pointer, not a "smart" pointer, since the smart pointer's destructor will have the order-of-destructor issue that we are trying to avoid.

Doing Work in Constructors
?
In general, constructors should merely set member variables to their initial values. Any complex initialization should go in an explicit Init() method.
link

Definition: It is possible to perform initialization in the body of the constructor.

Pros: Convenience in typing. No need to worry about whether the class has been initialized or not.

Cons: The problems with doing work in constructors are:

    * There is no easy way for constructors to signal errors, short of using exceptions (which are forbidden).
    * If the work fails, we now have an object whose initialization code failed, so it may be an indeterminate state.
    * If the work calls virtual functions, these calls will not get dispatched to the subclass implementations. Future modification to your class can quietly introduce this problem even if your class is not currently subclassed, causing much confusion.
    * If someone creates a global variable of this type (which is against the rules, but still), the constructor code will be called before main(), possibly breaking some implicit assumptions in the constructor code. For instance, gflags will not yet have been initialized.

Decision: If your object requires non-trivial initialization, consider having an explicit Init() method. In particular, constructors should not call virtual functions, attempt to raise errors, access potentially uninitialized global variables, etc.

Explicit Constructors
?
Use the C++ keyword explicit for constructors with one argument.
link

Definition: Normally, if a constructor takes one argument, it can be used as a conversion. For instance, if you define Foo::Foo(string name) and then pass a string to a function that expects a Foo, the constructor will be called to convert the string into a Foo and will pass the Foo to your function for you. This can be convenient but is also a source of trouble when things get converted and new objects created without you meaning them to. Declaring a constructor explicit prevents it from being invoked implicitly as a conversion.

Pros: Avoids undesirable conversions.

Cons: None.

Decision:

We require all single argument constructors to be explicit. Always put explicit in front of one-argument constructors in the class definition: explicit Foo(string name);

The exception is copy constructors, which, in the rare cases when we allow them, should probably not be explicit. Classes that are intended to be transparent wrappers around other classes are also exceptions. Such exceptions should be clearly marked with comments.

Copy Constructors
?
Provide a copy constructor and assignment operator only when necessary. Otherwise, disable them with DISALLOW_COPY_AND_ASSIGN.
link

Definition: The copy constructor and assignment operator are used to create copies of objects. The copy constructor is implicitly invoked by the compiler in some situations, e.g. passing objects by value.

Pros: Copy constructors make it easy to copy objects. STL containers require that all contents be copyable and assignable. Copy constructors can be more efficient than CopyFrom()-style workarounds because they combine construction with copying, the compiler can elide them in some contexts, and they make it easier to avoid heap allocation.

Cons: Implicit copying of objects in C++ is a rich source of bugs and of performance problems. It also reduces readability, as it becomes hard to track which objects are being passed around by value as opposed to by reference, and therefore where changes to an object are reflected.

Decision:

Few classes need to be copyable. Most should have neither a copy constructor nor an assignment operator. In many situations, a pointer or reference will work just as well as a copied value, with better performance. For example, you can pass function parameters by reference or pointer instead of by value, and you can store pointers rather than objects in an STL container.

If your class needs to be copyable, prefer providing a copy method, such as CopyFrom() or Clone(), rather than a copy constructor, because such methods cannot be invoked implicitly. If a copy method is insufficient in your situation (e.g. for performance reasons, or because your class needs to be stored by value in an STL container), provide both a copy constructor and assignment operator.

If your class does not need a copy constructor or assignment operator, you must explicitly disable them. To do so, add dummy declarations for the copy constructor and assignment operator in the private: section of your class, but do not provide any corresponding definition (so that any attempt to use them results in a link error).

For convenience, a DISALLOW_COPY_AND_ASSIGN macro can be used:

// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&);               \
  void operator=(const TypeName&)

Then, in class Foo:

class Foo {
 public:
  Foo(int f);
  ~Foo();

 private:
  DISALLOW_COPY_AND_ASSIGN(Foo);

Declaration Order
?
Use the specified order of declarations within a class: public: before private:, methods before data members (variables), etc.
link

Your class definition should start with its public: section, followed by its protected: section and then its private: section. If any of these sections are empty, omit them.

Within each section, the declarations generally should be in the following order:

    * Typedefs and Enums
    * Constants
    * Constructors
    * Destructor
    * Methods, including static methods
    * Data Members, including static data members

The DISALLOW_COPY_AND_ASSIGN macro invocation should be at the end of the private: section. It should be the last thing in the class. See Copy Constructors.

Method definitions in the corresponding .cc file should be the same as the declaration order, as much as possible.

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.

Casting
?
Use C++ casts like static_cast<>(). Do not use other cast formats like int y = (int)x; or int y = int(x);.
link

Definition: C++ introduced a different cast system from C that distinguishes the types of cast operations.

Pros: The problem with C casts is the ambiguity of the operation; sometimes you are doing a conversion (e.g., (int)3.5) and sometimes you are doing a cast (e.g., (int)"hello"); C++ casts avoid this. Additionally C++ casts are more visible when searching for them.

Cons: The syntax is nasty.

Decision:

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 the const 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.

Preprocessor Macros
?
Be very cautious with macros. Prefer inline functions, enums, and const variables to macros.
link

Macros mean that the code you see is not the same as the code the compiler sees. This can introduce unexpected behavior, especially since macros have global scope.

Luckily, macros are not nearly as necessary in C++ as they are in C. Instead of using a macro to inline performance-critical code, use an inline function. Instead of using a macro to store a constant, use a const variable. Instead of using a macro to "abbreviate" a long variable name, use a reference. Instead of using a macro to conditionally compile code ... well, don't do that at all (except, of course, for the #define guards to prevent double inclusion of header files). It makes testing much more difficult.

Macros can do things these other techniques cannot, and you do see them in the codebase, especially in the lower-level libraries. And some of their special features (like stringifying, concatenation, and so forth) are not available through the language proper. But before using a macro, consider carefully whether there's a non-macro way to achieve the same result.

The following usage pattern will avoid many problems with macros; if you use macros, follow it whenever possible:

    * Don't define macros in a .h file.
    * #define macros right before you use them, and #undef them right after.
    * Do not just #undef an existing macro before replacing it with your own; instead, pick a name that's likely to be unique.
    * Try not to use macros that expand to unbalanced C++ constructs, or at least document that behavior well.
    * Prefer not using ## to generate function/class/variable names.

 
posted @ 2010-05-11 17:30  Jake Lin  阅读(2008)  评论(0编辑  收藏  举报