好きな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 ] )
{
;
}
列挙型を作成するときは、名前空間に入れて、意味のある名前でアクセスできるようにします。
namespace EntityType {
enum Enum {
Ground = 0,
Human,
Aerial,
Total
};
}
void foo(EntityType::Enum entityType)
{
if (entityType == EntityType::Ground) {
/*code*/
}
}
[〜#〜] edit [〜#〜]:ただし、この手法はC++ 11では廃止されました。 Scoped enumeration(enum class
またはenum struct
で宣言)を代わりに使用する必要があります。これは、よりタイプセーフで、簡潔で、柔軟性があります。古いスタイルの列挙では、値は外側のスコープに配置されます。新しいスタイルの列挙では、enum class
の名前のスコープ内に配置されます。
前の例は、スコープ付き列挙型(としても知られている)厳密に型指定された列挙型)を使用して書き換えられています。
enum class EntityType {
Ground = 0,
Human,
Aerial,
Total
};
void foo(EntityType entityType)
{
if (entityType == EntityType::Ground) {
/*code*/
}
}
スコープ付き列挙型を使用することには、暗黙のキャストの不在、可能な前方宣言の可能性、およびカスタムの基本型を使用できること(デフォルトのint
ではない)の他の重要な利点があります。
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)としても知られています。
以下も参照してください。
コピースワップイディオムは、例外に対して安全なコピーを提供します。正しいコピークターとスワップが実装されている必要があります。
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
)は変更されません。
[〜#〜] 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_fooがinjectedフルアクセスで派生クラスに派生クラスのメンバーに。
other SO posts に、具体的な使用例を編集して追加してください。
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
に含まれています。
'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
};
パブリックトップ-プライベートダウン
小さな最適化のように見えますが、この規則に切り替えて以来、特に42年間クラスを見ていないので、クラスを理解するのにもっと楽しい時間があります。
頻繁に関心のあるポイントから退屈なものまで一貫したメンバーの可視性を持つことは、特にコードが自己文書化されるべきである場合に非常に役立ちます。
(qt-usersの注:スロットはシグナルの前に来ます。スロットは非スロットメンバー関数のように呼び出し可能でなければならず、スロット性以外は非スロットと区別できないためです)
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_;
};
if
ステートメントでは、難しい条件がある場合に、各条件がインデントを使用しているレベルを明確に示すことができます。
if ( ( (var1A == var2A)
|| (var1B == var2B))
&& ( (var1C == var2C)
|| (var1D == var2D)))
{
// do something
}
お気に入り以外はありませんwill以下のコードを修正します:
次に、問題のあるファイルを見つけるためのワンライナーを示します。
git grep -I -E '<tab>|.{81,}| *$' | cut -f1 -d: | sort -u
どこ <tab>
はタブ文字です(POSIX正規表現は\ tを行いません)
re:ididak
長いステートメントを多くの短い行に分割するコードを修正します。
それに直面しよう:それはもはや90年代ではありません。あなたの会社がそのコーダーのためのワイドスクリーンLCDを買う余裕がないなら、あなたはより良い仕事を得る必要があります:)
(ランタイム多態性とは対照的に、構文多態性および静的多態性とも呼ばれます。)
テンプレート関数を使用すると、共通の基本クラスを導入しなくても、型コンストラクターに依存し、パラメーター化された型のファミリーのシグニチャーを呼び出すコードを記述できます。
本 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)仮想関数による実行時の多態性が必要です。
if (expression) // preferred - if keyword sticks out more
vs.
if(expression) // looks too much like a void function call
私はこれが私の関数呼び出しがスペースセパレータを持たないことを好むことを意味していると思います
foo(parm1, parm2);
これは、フレームワークで可能な限り多くを処理し、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 著の本 オブジェクト指向ソフトウェア開発の設計パターン で説明されています。
部分的に盲目だった人と一緒に働いた後、そして彼の要請により、私はもっと多くのスペースを使うように切り替えました。当時は嫌いでしたが、今は好きです。頭の上の、識別子とキーワードの間に空白がない唯一の場所はafter関数名で、次の括弧の前です。
void foo( int a, int b )
{
int c = a + ( a * ( a * b ) );
if ( c > 12 )
c += 9;
return foo( 2, c );
}
それがイディオムとして適格かどうかは正確にはわかりませんが、かなりの負荷のかかるテンプレートプログラミングは(多くの場合)SFINAEに依存します(置換の失敗はエラーではありません)。 前の質問 に対するいくつかの回答には例があります。
Ifと同じ行に小さなステートメントを書くのが本当に好きです
int myFunc(int x) {
if(x >20) return -1;
//do other stuff ....
}
これが慣用句として数えられるかどうかはわかりませんが、プロジェクトが-detoxygenをまだ使用していない場合でも doxygen -styleインラインコメントを使用する傾向があります...
bool MyObjects::isUpToSomething() ///< Is my object up to something
(余談ですが、私のコメントは通常、それほど不完全ではありません。)
関数名を新しい行に置くと便利なので、次のように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;
};
PIMPLまたはJames Coplienが元々それを「ハンドルボディ」と呼んでいたように私はお勧めします。
このイディオムを使用すると、インターフェースを実装から完全に切り離すことができます。主要なCORBAミドルウェアコンポーネントの書き換えと再リリースに取り組む場合、このイディオムを使用して、APIを実装から完全に切り離しました。
これにより、リバースエンジニアリングの可能性がほとんどなくなりました。
C++イディオムの優れたリソースは、James Coplienの優れた書籍「 Advanced C++ Programming Styles and Idioms 」です。強くお勧めします!
編集:Neilが以下に指摘するように、この本はかなり古く、彼の推奨事項の多くは実際にC++標準自体に組み込まれています。しかし、私はそれが有用な情報の源であることに気付いています。彼の C++イディオムに関するPLoP論文 の形で、多くのイディオムがpatterm形式にリキャストされました。
簡単にコメントできるように、各メソッドまたは関数の引数を別々の行に記述します。
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. */
)
私は常に次のようにひっくり返して編集します:
私は通常、* BSD STYLE(9) で説明されているKNFを使用します