C++ クラスのデータ メンバーに m_ のようなプレフィックスを使用するのはなぜですか? 質問する

C++ クラスのデータ メンバーに m_ のようなプレフィックスを使用するのはなぜですか? 質問する

多くのC++コードでは、データメンバーをマークアップするために構文規則が使用されています。一般的な例としては、

  • m_memberNameパブリック メンバーの場合 (パブリック メンバーが使用される場合)
  • _memberNameプライベートメンバーまたは全メンバー

this->memberその他は、データ メンバーが使用されるたびに使用を強制しようとします。

私の経験では、大規模なコード ベースのほとんどは、このようなルールを一貫して適用することに失敗しています。

他の言語では、これらの規則はあまり普及していません。Java や C# のコードではたまに見かける程度です。Ruby や Python のコードでは見たことがないと思います。したがって、最近の言語では、データ メンバーに特別なマークアップを使用しない傾向があるようです。

この規則は、C++ では現在でも有用でしょうか、それとも、特にライブラリ間で一貫性なく使用されていることから、単に時代錯誤なのでしょうか? 他の言語では、メンバー プレフィックスがなくても実行できることが示されていませんか?

ベストアンサー1

私は全面的に賛成です接頭辞がうまくいった

接頭辞が受ける「悪評」のほとんどは、(システム) ハンガリー記法によるものだと私は思います。

この表記は、強く型付けされた言語ではほとんど意味がありません。たとえば、C++ で「lpsz」を使用して、文字列がヌル終端文字列への long ポインターであることを示す場合、セグメント化されたアーキテクチャは古いものであり、C++ 文字列は一般的な規則によりヌル終端 char 配列へのポインターであり、「customerName」が文字列であることを知ることはそれほど難しいことではありません。

しかし、私は接頭辞を使って使用法変数(基本的には「Apps Hungarian」ですが、System Hungarianとの悪い不公平な関連があるため、Hungarianという用語を避けることを好みます)の非常に便利な時間の節約そしてバグの削減アプローチ。

私が使う:

  • メンバーのm
  • 定数/読み取り専用の場合は c
  • p はポインタ(および pp はポインタへのポインタ)
  • vは揮発性
  • sは静的
  • インデックスとイテレータの i
  • イベントのe

私が作りたいのはタイプ明確にするために、標準のサフィックス (List、ComboBox など) を使用します。

これにより、プログラマーは使用法変数を見たり使用したりするたびに、その変数の「p」を使用します。おそらく最も重要なケースはポインターの「p」です (使用法が var. から var-> に変わり、ポインター (NULL、ポインター演算など) にはより注意する必要があるため)。ただし、他のケースもすべて非常に便利です。

たとえば、1 つの関数内で同じ変数名を複数の方法で使用できます。(ここでは C++ の例ですが、多くの言語に同様に当てはまります)

MyClass::MyClass(int numItems)
{
    mNumItems = numItems;
    for (int iItem = 0; iItem < mNumItems; iItem++)
    {
        Item *pItem = new Item();
        itemList[iItem] = pItem;
    }
}

ここから見ることができます:

  • メンバーとパラメータの混同がない
  • インデックス/イテレータとアイテムの混同がない
  • 「count」、「index」などの一般的な(あいまいな)名前の多くの落とし穴を回避する、明確に関連する一連の変数(項目リスト、ポインター、インデックス)の使用。
  • プレフィックスを使用すると、「itemIndex」や「itemPtr」などの代替手段よりも入力が減ります(短くなり、自動補完との相性が良くなります)。

「iName」イテレータのもう 1 つの優れた点は、配列を間違ったインデックスでインデックス付けすることがなく、ループを別のループ内にコピーする場合に、ループ インデックス変数の 1 つをリファクタリングする必要がないことです。

この非現実的に単純な例と比較してください。

for (int i = 0; i < 100; i++)
    for (int j = 0; j < 5; j++)
        list[i].score += other[j].score;

(これは読みにくく、多くの場合、「j」を意図した箇所に「i」が使用されることになります)

と:

for (int iCompany = 0; iCompany < numCompanies; iCompany++)
    for (int iUser = 0; iUser < numUsers; iUser++)
       companyList[iCompany].score += userList[iUser].score;

(これははるかに読みやすく、インデックス作成に関する混乱をすべて排除します。最新の IDE の自動補完機能により、これもすばやく簡単に入力できます)

次の利点は、コードスニペットが文脈を必要としない理解できるはずです。2 行のコードを電子メールやドキュメントにコピーすれば、そのスニペットを読む人は誰でも、メンバー、定数、ポインタ、インデックスなどの違いを理解できます。「ああ、'data' はポインタへのポインタなので注意してください」と付け加える必要はありません。なぜなら、'ppData' という名前だからです。

同じ理由で、コードを理解するためにコードを一行外す必要がありません。コードを検索して「データ」がローカル、パラメータ、メンバー、または定数であるかどうかを調べる必要もありません。マウスに手を動かして「データ」の上にポインタを置いて、ツールチップ(表示されないこともあります)がポップアップするのを待つ必要もありません。そのため、プログラマーはコードを読んで理解することができます。大幅上下に探したり待ったりして時間を無駄にしないので、より高速になります。

(物事を解決するために上から下まで検索するのに時間を無駄にしたくないと思うなら、1 年前に書いて以来見ていないコードを見つけてください。ファイルを開いて、読まずに半分くらいまでジャンプします。この時点から、何かがメンバー、パラメーター、またはローカルであるかどうかがわからなくなるまで、どれだけ読めるかを確認します。次に、別のランダムな場所にジャンプします... これは、他の人のコードをシングル ステップで実行したり、関数の呼び出し方法を理解しようとしたりするときに、私たちが一日中行っていることです)

'm' プレフィックスは、(私見では) 見苦しく冗長な "this->" 表記と、それが保証する不一致も回避します (注意していても、名前の一貫したスペルを強制するものがないため、通常は同じクラスに 'this->data' と 'data' が混在することになります)。

「this」表記は、曖昧さ- しかし、なぜわざわざ曖昧なコードを書く人がいるのでしょうか? 曖昧さ意思遅かれ早かれバグにつながります。また、一部の言語では「this」を静的メンバーに使用できないため、コーディング スタイルに「特別なケース」を導入する必要があります。私は、どこにでも適用される、明示的、明確、一貫性のある単一のシンプルなコーディング ルールを好みます。

最後の大きなメリットは、Intellisense と自動補完です。Windows フォームで Intellisense を使用してイベントを検索してみてください。イベントを見つけるために呼び出す必要のない、何百もの謎めいた基本クラス メソッドをスクロールする必要があります。しかし、すべてのイベントに「e」プレフィックスが付いていれば、それらは自動的に「e」の下のグループにリストされます。このように、プレフィックスを付けると、メンバー、定数、イベントなどが Intellisense リストにグループ化され、必要な名前をはるかに迅速かつ簡単に見つけることができます。(通常、メソッドには、そのスコープ内でアクセス可能な値 (ローカル、パラメーター、メンバー、定数、イベント) が 20 ~ 50 個程度あります。しかし、プレフィックスを入力すると (インデックスを使用したいので、「i...」と入力します)、2 ~ 5 個の自動補完オプションしか表示されません。プレフィックスと意味のある名前に起因する「余分な入力」によって、検索領域が大幅に削減され、開発速度が大幅に向上します)

私は怠け者のプログラマーですが、上記の規則により多くの作業が省けます。すべての変数の使用方法がわかっているので、コーディングが速くなり、ミスも大幅に減ります。


反対意見

では、欠点は何でしょうか? プレフィックスに対する典型的な反論は次のとおりです。

  • 「プレフィックススキームは悪い/邪悪だ」「m_lpsz」やそれに類するものはよく考えられておらず、まったく役に立たないことに同意します。そのため、コンテキストに適さないものをコピーするのではなく、要件をサポートするように設計された、適切に設計された表記法を使用することをお勧めします。(適切なツールを使用してください)。

  • 「何かの使い方が変わったら、名前も変えなければならない」はい、もちろんです。それがリファクタリングの目的であり、IDEがこの作業を迅速かつ簡単に行うためのリファクタリングツールを備えている理由です。プレフィックスがなくても、変数の使用法を変更すると、その名前がほぼ確実に変更されます。すべき変更される。

  • 「接頭辞は私を混乱させるだけです」. As does every tool until you learn how to use it. Once your brain has become used to the naming patterns, it will filter the information out automatically and you won't really mind that the prefixes are there any more. But you have to use a scheme like this solidly for a week or two before you'll really become "fluent". And that's when a lot of people look at old code and start to wonder how they ever managed without a good prefix scheme.

  • "I can just look at the code to work this stuff out". Yes, but you don't need to waste time looking elsewhere in the code or remembering every little detail of it when the answer is right on the spot your eye is already focussed on.

  • (Some of) that information can be found by just waiting for a tooltip to pop up on my variable. Yes. Where supported, for some types of prefix, when your code compiles cleanly, after a wait, you can read through a description and find the information the prefix would have conveyed instantly. I feel that the prefix is a simpler, more reliable and more efficient approach.

  • "It's more typing". Really? One whole character more? Or is it - with IDE auto-completion tools, it will often reduce typing, because each prefix character narrows the search space significantly. Press "e" and the three events in your class pop up in intellisense. Press "c" and the five constants are listed.

  • "I can use this-> instead of m". Well, yes, you can. But that's just a much uglier and more verbose prefix! Only it carries a far greater risk (especially in teams) because to the compiler it is optional, and therefore its usage is frequently inconsistent. m on the other hand is brief, clear, explicit and not optional, so it's much harder to make mistakes using it.

おすすめ記事