web-dev-qa-db-ja.com

好きなC ++コーディングスタイルのイディオムは何ですか

好きなC++コーディングスタイルのイディオムは何ですか?スタイルやコーディングのタイポグラフィー、たとえば中括弧を付ける場所、キーワードの後に​​スペースがある、インデントのサイズなどについて質問しています。これは、delete[]

これが私のお気に入りの1つの例です。C++クラス初期化子では、セパレータを行ではなく行の前に配置しています。これにより、これを最新に保つことが容易になります。また、バージョン間のソースコード管理の相違が明確になります。

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 ] )
{
    ;
}
59
Foredecker

列挙型を作成するときは、名前空間に入れて、意味のある名前でアクセスできるようにします。

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

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

[〜#〜] edit [〜#〜]:ただし、この手法はC++ 11では廃止されました。 Scoped enumerationenum classまたはenum structで宣言)を代わりに使用する必要があります。これは、よりタイプセーフで、簡潔で、柔軟性があります。古いスタイルの列挙では、値は外側のスコープに配置されます。新しいスタイルの列挙では、enum classの名前のスコープ内に配置されます。
前の例は、スコープ付き列挙型(としても知られている)厳密に型指定された列挙型)を使用して書き換えられています。

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

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

スコープ付き列挙型を使用することには、暗黙のキャストの不在、可能な前方宣言の可能性、およびカスタムの基本型を使用できること(デフォルトのintではない)の他の重要な利点があります。

58
kshahar

RAII:リソースの取得は初期化です

RAIIは最も重要なイディオムかもしれません。リソースがオブジェクトにマッピングされ、それらのオブジェクトが宣言されているスコープに従ってそれらのライフタイムが自動的に管理されるようにする必要があるという考えです。

たとえば、ファイルハンドルがスタックで宣言されている場合、関数(またはループ、または内部で宣言されたスコープ)から戻ると、ファイルハンドルは暗黙的に閉じられます。動的メモリ割り当てがクラスのメンバーとして割り当てられた場合、そのクラスインスタンスが破棄されるときに暗黙的に解放される必要があります。等々。あらゆる種類のリソース(メモリ割り当て、ファイルハンドル、データベース接続、ソケット、および取得および解放する必要のあるその他の種類のリソース)は、そのようなRAIIクラス内にラップする必要があります。RAIIクラスの存続期間は、リソースのスコープによって決定されます。宣言した。

これの主な利点の1つは、C++は、オブジェクトがスコープから外れたときにデストラクタが呼び出されることを保証することです。コントロールがスコープを離れる方法に関係なく。例外がスローされた場合でも、すべてのローカルオブジェクトはスコープ外になるため、関連するリソースがクリーンアップされます。

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.
}

関数を離れる方法や、ファイルを開いた後に何が起こるかに関係なく、ファイルを明示的に閉じたり、その関数内で例外(たとえば、try-finally)を処理したりする必要はありません。代わりに、ファイルはスコープ外に出たときに破棄されるローカルオブジェクトに関連付けられているため、クリーンアップされます。

RAIIは、あまり一般的ではないがSBRM(Scope-Bound Resource Management)としても知られています。

以下も参照してください。

  • ScopeGuard を使用すると、コードで「例外がスローされた場合に、自動的に「元に戻す」操作を呼び出すことができます。」
63
jalf

コピースワップ

コピースワップイディオムは、例外に対して安全なコピーを提供します。正しいコピークターとスワップが実装されている必要があります。

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);
}

ADL(引数依存検索) 直接 を使用してswapメソッドを実装することもできます。

自己割り当てを処理するため、このイディオムは重要です[1]、強力な例外保証を行います[2]、多くの場合、非常に簡単に記述できます。


[1] 自己割り当ては可能な限り効率的に処理されませんが、それはrareであると想定されているため、発生しない場合、これは実際には高速です。

[2] 例外がスローされた場合、オブジェクトの状態(*this)は変更されません。

37
RC.

CRTP:不思議な繰り返しのテンプレートパターン

[〜#〜] crtp [〜#〜] は、クラスをテンプレートパラメータとしてその基本クラスに渡すと発生します。

template<class Derived>
struct BaseCRTP {};

struct Example : BaseCRTP<Example> {};

基本クラス内で、(をキャストするだけで、派生型で派生インスタンスを完全に取得できます=)static_castまたはdynamic_castwork):

template<class Derived>
struct BaseCRTP {
  void call_foo() {
    Derived& self = *static_cast<Derived*>(this);
    self.foo();
  }
};

struct Example : BaseCRTP<Example> {
  void foo() { cout << "foo()\n"; }
};

実質的に、call_fooinjectedフルアクセスで派生クラスに派生クラスのメンバーに。

other SO posts に、具体的な使用例を編集して追加してください。

34
Roger Pate

pImpl:実装へのポインタ

PImplイディオムは、クラスのインターフェースをその実装から切り離すための非常に便利な方法です。

通常、クラス定義にはメソッドだけでなくメンバー変数も含まれている必要があり、情報が多すぎる可能性があります。たとえば、メンバー変数は、どこにも含めたくないヘッダーで定義された型である場合があります。

windows.hヘッダーは、ここでの主要な例です。 HANDLEまたは別のWin32型をクラス内でラップすることもできますが、クラスのどこにでもwindows.hを含めずにHANDLEをクラス定義に含めることはできません。中古。

次に、ソリューションは P追い払う IMPL共生または Pointer-to-IMPLクラスの連携、およびパブリック実装にプライベートポインタへのポインタのみを格納させ、すべてのメンバーメソッドを転送します。

例えば:

class private_foo; // a forward declaration a pointer may be used

// foo.h
class foo {
public:
  foo();
  ~foo();
  void bar();
private:
  private_foo* pImpl;
};

// foo.cpp
#include whichever header defines the types T and U

// define the private implementation class
class private_foo {
public:
  void bar() { /*...*/ }

private:
  T member1;
  U member2;
};

// fill in the public interface function definitions:
foo::foo() : pImpl(new private_foo()) {}
foo::~foo() { delete pImpl; }
void foo::bar() { pImpl->bar(); }

fooの実装がパブリックインターフェイスから分離されたため、

  • クラスの使用時にこれらの依存関係が存在している必要なく、他のヘッダーのメンバーと型を使用できます。
  • クラスを使用するコードの再コンパイルを強制せずに実装を変更できます。

クラスのユーザーは、ヘッダーを含めるだけです。ヘッダーには、クラスの実装に関する特定のものが含まれていません。実装の詳細はすべてfoo.cppに含まれています。

27
jalf

'columns'でコード/初期化を並べるのが好きです... 'column'モード対応のエディターで編集するときに非常に便利であることがわかります。

int myVar        = 1;    // comment 1
int myLongerVar  = 200;  // comment 2

MyStruct arrayOfMyStruct[] = 
{   
    // Name,                 timeout,   valid
    {"A string",             1000,      true    },   // Comment 1
    {"Another string",       2000,      false   },   // Comment 2 
    {"Yet another string",   11111000,  false   },   // Comment 3
    {NULL,                   5,         true    },   // Comment 4
};

対照的に、上記のようにインデントされておらず、フォーマットされていない同じコードが表示されます...(私の目には少し読みにくい)

int myVar = 1; // comment 1
int myLongerVar = 200; // comment 2

MyStruct arrayOfMyStruct[] = 
{   
    // Name, timeout, valid
    {"A string", 1000, true},// Comment 1
    {"Another string", 2000, false }, // Comment 2 
    {"Yet another string", 11111000,false}, // Comment 3
    {NULL, 5, true }, // Comment 4
};
22
Prembo

パブリックトップ-プライベートダウン

小さな最適化のように見えますが、この規則に切り替えて以来、特に42年間クラスを見ていないので、クラスを理解するのにもっと楽しい時間があります。

頻繁に関心のあるポイントから退屈なものまで一貫したメンバーの可視性を持つことは、特にコードが自己文書化されるべきである場合に非常に役立ちます。

(qt-usersの注:スロットはシグナルの前に来ます。スロットは非スロットメンバー関数のように呼び出し可能でなければならず、スロット性以外は非スロットと区別できないためです)

  • パブリック、保護、プライベート
  • 次に、Factory、ctor、dtor、コピー、スワッピング
  • その後、クラスのインターフェイス最後に、別のprivate:セクションには、データが含まれます(理想的にはimpl-pointerのみ)。

このルールは、クラス宣言を整理しておくことに問題がある場合にも役立ちます。

class Widget : public Purple {
public:
    // Factory methods.
    Widget FromRadians (float);
    Widget FromDegrees (float);

    // Ctors, rule of three, swap
    Widget();
    Widget (Widget const&);
    Widget &operator = (Widget const &);
    void swap (Widget &) throw();

    // Member methods.
    float area() const;

    // in case of qt {{ 
public slots:
    void invalidateBlackHole();

signals:
    void areaChanged (float);
    // }}

protected:    
    // same as public, but for protected members


private:    
    // same as public, but for private members

private:
    // data
    float widgetness_;
    bool  isMale_;
};
14
Sebastian Mach

ifステートメントでは、難しい条件がある場合に、各条件がインデントを使用しているレベルを明確に示すことができます。

if (  (  (var1A == var2A)
      || (var1B == var2B))
   && (  (var1C == var2C)
      || (var1D == var2D)))
{
   // do something
}
11
zerbp

お気に入り以外はありませんwill以下のコードを修正します:

  1. tabs-多くのIDEとコードレビューツールで不整合を引き起こします。なぜなら、それらはmod 8スペースでのタブに常に同意するわけではないためです。
  2. 80列より長い行-正直言って、行が短いほど読みやすくなります。行が短い限り、私の脳はほとんどのコーディング規約を解析できます。
  3. 末尾に空白がある行-gitは空白について文句を言うerrors、これはdiffで赤いblobとして表示されるので、迷惑です。

次に、問題のあるファイルを見つけるためのワンライナーを示します。

git grep -I -E '<tab>|.{81,}|  *$' | cut -f1 -d: | sort -u

どこ <tab>はタブ文字です(POSIX正規表現は\ tを行いません)

7
ididak

re:ididak

長いステートメントを多くの短い行に分割するコードを修正します。

それに直面しよう:それはもはや90年代ではありません。あなたの会社がそのコーダーのためのワイドスクリーンLCDを買う余裕がないなら、あなたはより良い仕事を得る必要があります:)

7
Buddy

コンパイル時のポリモーフィズム

(ランタイム多態性とは対照的に、構文多態性および静的多態性とも呼ばれます。)

テンプレート関数を使用すると、共通の基本クラスを導入しなくても、型コンストラクターに依存し、パラメーター化された型のファミリーのシグニチャーを呼び出すコードを記述できます。

Elements of Programming では、著者はこの型の扱いを abstract genera と呼んでいます。 concepts を使用すると、そのような型パラメーターの要件を指定できますが、C++ではそのような指定は必須ではありません。

2つの簡単な例:

_#include <stdexcept>

template <typename T>
T twice(T n) {
  return 2 * n;
}

InIt find(InIt f, InIt l,
          typename std::iterator_traits<InIt>::reference v)
{
  while (f != l && *f != v)
    ++f;
  return f;
}   

int main(int argc, char* argv[]) {
  if (6 != twice(3))
    throw std::logic_error("3 x 2 = 6");

  int const nums[] = { 1, 2, 3 };
  if (nums + 4 != find(nums, nums + 4, 42))
    throw std::logic_error("42 should not have been found.");

  return 0;
}
_

バイナリの_*_演算子が定義されている通常の型でtwiceを呼び出すことができます。同様に、比較可能な任意の型とそのモデル Input Iterator find()を呼び出すことができます。 1セットのコードは、さまざまなタイプで同様に動作し、共有基本クラスは見えません。

もちろん、ここで実際に行われているのは、同じソースコードが、テンプレートのインスタンス化時に expanded でさまざまな型固有の関数に展開され、それぞれが個別に生成されたマシンコードを持つということです。テンプレートなしで同じタイプのセットに対応するには、1)特定のシグネチャを持つ個別の手書き関数、または2)仮想関数による実行時の多態性が必要です。

7
seh

if/while/for括弧で囲まれた式とスペース区切り

if (expression)  // preferred - if keyword sticks out more

vs.

if(expression)  // looks too much like a void function call

私はこれが私の関数呼び出しがスペースセパレータを持たないことを好むことを意味していると思います

foo(parm1, parm2);
5
franji1

テンプレートとフック

これは、フレームワークで可能な限り多くを処理し、doorまたはhookフレームワークのユーザーによるカスタマイズ用。ホットスポットおよび テンプレートメソッド とも呼ばれます。

class Class {
  void PrintInvoice();     // Called Template (boilerplate) which uses CalcRate()
  virtual void CalcRate() = 0;  // Called Hook
}

class SubClass : public Class {
  virtual void CalcRate();      // Customized method
}

Wolfgang Pree 著の本 オブジェクト指向ソフトウェア開発の設計パターン で説明されています。

5
epatel

部分的に盲目だった人と一緒に働いた後、そして彼の要請により、私はもっと多くのスペースを使うように切り替えました。当時は嫌いでしたが、今は好きです。頭の上の、識別子とキーワードの間に空白がない唯一の場所はafter関数名で、次の括弧の前です。

void foo( int a, int b )
{
  int c = a + ( a * ( a * b ) );
  if ( c > 12 )
    c += 9;
  return foo( 2, c );
}
4
Jamie Hale

それがイディオムとして適格かどうかは正確にはわかりませんが、かなりの負荷のかかるテンプレートプログラミングは(多くの場合)SFINAEに依存します(置換の失敗はエラーではありません)。 前の質問 に対するいくつかの回答には例があります。

4
Jerry Coffin

Ifと同じ行に小さなステートメントを書くのが本当に好きです

int myFunc(int x) {
   if(x >20) return -1;
   //do other stuff ....
}
4
mempko

これが慣用句として数えられるかどうかはわかりませんが、プロジェクトが-detoxygenをまだ使用していない場合でも doxygen -styleインラインコメントを使用する傾向があります...

bool MyObjects::isUpToSomething() ///< Is my object up to something 

(余談ですが、私のコメントは通常、それほど不完全ではありません。)

3
Roddy

関数名を新しい行に置くと便利なので、次のようにgrepできます

grep -R '^fun_name' .

彼らのために。 GNUプロジェクトのロードに使用されるそのスタイルが好きで、それが好きです:

static void
fun_name (int a, int b) {
    /* ... */
}

戻り値を関数行に文書化すると、非常に簡単に見つけることができます。

int function(void) /* return 1 on success, 0 on failure */ 
{
    return 1;
};
2
EvilTeach

PIMPLまたはJames Coplienが元々それを「ハンドルボディ」と呼んでいたように私はお勧めします。

このイディオムを使用すると、インターフェースを実装から完全に切り離すことができます。主要なCORBAミドルウェアコンポーネントの書き換えと再リリースに取り組む場合、このイディオムを使用して、APIを実装から完全に切り離しました。

これにより、リバースエンジニアリングの可能性がほとんどなくなりました。

C++イディオムの優れたリソースは、James Coplienの優れた書籍「 Advanced C++ Programming Styles and Idioms 」です。強くお勧めします!

編集:Neilが以下に指摘するように、この本はかなり古く、彼の推奨事項の多くは実際にC++標準自体に組み込まれています。しかし、私はそれが有用な情報の源であることに気付いています。彼の C++イディオムに関するPLoP論文 の形で、多くのイディオムがpatterm形式にリキャストされました。

1
Rob Wells

簡単にコメントできるように、各メソッドまたは関数の引数を別々の行に記述します。

int ReturnMaxValue(
    int* inputList,   /* the list of integer values from which to get the maximum */
    long size,        /* count of the number of integer values in inputList */
    char* extraArgs   /* additional arguments that a caller can provide.    */
)
1
Ather

私は常に次のようにひっくり返して編集します:

  • 余分な改行
  • EOFに改行なし
0
korona

私は通常、* BSD STYLE(9) で説明されているKNFを使用します

0
Terminus