What are your favorite C++ Coding Style idioms

What are your favorite C++ coding style idioms? I'm asking about style or coding typography such as where you put curly braces, are there spaces after keywords, the size of indents, etc. This is opposed to best-practices or requirements such as always deleting arrays with delete[] .

Here is an example of one of my favorites: In C++ Class initializers, we put the separators at the front of the line, rather than the back. This makes it easier to keep this up to date. It also means that source code control diffs between versions are cleaner.

TextFileProcessor::
TextFileProcessor( class ConstStringFinder& theConstStringFinder ) 

    : TextFileProcessor_Base( theConstStringFinder )

    , m_ThreadHandle  ( NULL )
    , m_startNLSearch (    0 )
    , m_endNLSearch   (    0 )
    , m_LineEndGetIdx (    0 )
    , m_LineEndPutIdx (    0 )
    , m_LineEnds      ( new const void*[ sc_LineEndSize ] )
{
    ;
}

When creating enumerations, put them in a namespace so that you can access them with a meaningful name:

namespace EntityType {
    enum Enum {
        Ground = 0,
        Human,
        Aerial,
        Total
    };
}

void foo(EntityType::Enum entityType)
{
    if (entityType == EntityType::Ground) {
        /*code*/
    }
}

EDIT : However, this technique has become obsolete in C++11. Scoped enumeration (declared with enum class or enum struct ) should be used instead: it is more type-safe, concise, and flexible. With old-style enumerations the values are placed in the outer scope. With new-style enumeration they are placed within the scope of the enum class name.
Previous example rewritten using scoped enumeration (also known as strongly typed enumeration):

enum class EntityType {
    Ground = 0,
    Human,
    Aerial,
    Total
};

void foo(EntityType entityType)
{
    if (entityType == EntityType::Ground) {
        /*code*/
    }
}

There are other significant benefits from using scoped enumerations: absence of implicit cast, possible forward declaration, and ability to use custom underlying type (not the default int ).


RAII: Resource Acquisition Is Initialization

RAII may be the most important idiom. It is the idea that resources should be mapped to objects, so that their lifetimes are managed automatically according to the scope in which those objects are declared.

For example, if a file handle was declared on the stack, it should be implicitly closed once we return from the function (or loop, or whichever scope it was declared inside). If a dynamic memory allocation was allocated as a member of a class, it should be implicitly freed when that class instance is destroyed. And so on. Every kind of resource—memory allocations, file handles, database connections, sockets, and any other kind of resource that has to be acquired and released—should be wrapped inside such a RAII class, whose lifetime is determined by the scope in which it was declared.

One major advantage of this is that C++ guarantees that destructors are called when an object goes out of scope, regardless of how control is leaving that scope. Even if an exception is thrown, all local objects will go out of scope, and so their associated resources will get cleaned up.

void foo() {
  std::fstream file("bar.txt"); // open a file "bar.txt"
  if (rand() % 2) {
    // if this exception is thrown, we leave the function, and so
    // file's destructor is called, which closes the file handle.
    throw std::exception();
  }
  // if the exception is not called, we leave the function normally, and so
  // again, file's destructor is called, which closes the file handle.
}

Regardless of how we leave the function, and of what happens after the file is opened, we don't need to explicitly close the file, or handle exceptions (eg try-finally) within that function. Instead, the file gets cleaned up because it is tied to a local object that gets destroyed when it goes out of scope.

RAII is also less-commonly known as SBRM (Scope-Bound Resource Management).

See also:

  • ScopeGuard allows code to "automatically invoke an 'undo' operation .. in the event that an exception is thrown."

  • Copy-swap

    The copy-swap idiom provides exception-safe copying. It requires that a correct copy ctor and swap are implemented.

    struct String {
      String(String const& other);
    
      String& operator=(String copy) { // passed by value
        copy.swap(*this); // nothrow swap
        return *this; // old resources now in copy, released in its dtor
      }
    
      void swap(String& other) throw() {
        using std::swap; // enable ADL, defaulting to std::swap
        swap(data_members, other.data_members);
      }
    
    private:
      Various data_members;
    };
    void swap(String& a, String& b) { // provide non-member for ADL
      a.swap(b);
    }
    

    You can also implement the swap method with ADL (Argument Dependent Lookup) directly.

    This idiom is important because it handles self-assignment[1], makes the strong exception guarantee[2], and is often very easy to write.


    [1] Even though self-assignment isn't handled as efficiently as possible, it's supposed to be rare, so if it never happens, this is actually faster.

    [2] If any exception is thrown, the state of the object ( *this ) is not modified.

    链接地址: http://www.djcxy.com/p/12668.html

    上一篇: 在C ++中复制构造函数和=运算符重载:是一个常用函数吗?

    下一篇: 你最喜欢的C ++编码风格成语是什么?