web-dev-qa-db-ja.com

C ++クラスのメンバーをどのように注文すればよいですか?

すべてのプライベートメンバー、次にすべての保護メンバー、すべてのパブリックメンバーの方が良いですか?またはその逆?または、操作をコンストラクターなどから分離しておくことができるように、複数のプライベート、保護、パブリックラベルが必要ですか?この決定を行う際に考慮すべき問題は何ですか?

56
Tommy Herbert

私はパブリックインターフェイスを最初に配置しましたが、常にこれを行うとは限りませんでした。私はこれまで逆に、プライベート、保護、パブリックの順で物事を行っていました。振り返ってみると、あまり意味がありませんでした。

クラスの開発者は、その「内部」についてよく知っているはずですが、クラスのユーザーはあまり気にしないか、少なくとも気にする必要はありません。彼らは主にクラスが彼らのために何ができるか興味がありますよね?

したがって、私は最初に一般公開し、通常は機能/ユーティリティごとに整理します。私は、Xに関連するすべてのメソッドを見つけるためにインターフェースをわざわざ調べなくて済むようにしたくはありません。それらすべてを整理された方法で一緒に見られるようにしたいと思います。

私は複数のパブリック/プロテクト/プライベートセクションを使用していません。

50
itsmatt

Googleは この順序 を支持します: "TypedefsおよびEnums、定数、コンストラクター、デストラクタ、静的メソッドを含むメソッド、静的データメンバーを含むデータメンバー。"

Matthew Wilson (Safariサブスクリプションが必要)は、「構造、操作、属性、反復、状態、実装、メンバー、および私のお気に入り、実装しない」の順序を推奨しています。

それらは正当な理由を提供し、この種のアプローチはかなり標準的であるように見えますが、あなたが何をするにせよ、それについて一貫しています。

37
Josh Kelley

それは私の意見であり、私はほとんどの人が同意するだろうと推測して、パブリックメソッドを最初にすべきだと思っています。 OOのコア原則の1つは、実装を気にする必要がないということです。パブリックメソッドを見るだけで、クラスの使用に必要なすべてを知ることができます。

8
korona

いつものように、hum​​ans firstのコードを記述します。クラスを使用する人を考慮し、最も重要なメンバー/列挙型/タイプ定義/何でもそれらへを上部に配置します。

通常これは、クラスのほとんどのコンシューマーが最も関心があるので、パブリックメンバーがトップであることを意味します。次に、プロテクトが続き、プライベートが続きます。通常。

いくつかの例外があります。

時折、初期化の順序が重要であり、場合によっては、パブリックの前にプライベートを宣言する必要があります。場合によっては、クラスを継承して拡張することがより重要であり、その場合、保護されたメンバーが上位に配置されることがあります。ユニットテストをレガシーコードにハッキングするときは、パブリックメソッドを公開する方が簡単な場合があります。このほぼ罪を犯さなければならない場合は、これらをクラス定義の下部に配置します。

しかし、それらは比較的まれな状況です。

ほとんどの場合、「パブリック、保護、プライベート」がクラスの消費者にとって最も有用であることがわかります。それは固執するのにまともな基本的なルールです。

しかし、それはアクセスによる順序付けではなく、消費者への関心による順序付けについてです。

5
MattyT

私は通常、最初に(読み取られる)インターフェースを定義します。それはパブリックであり、次に保護され、次にプライベートなものです。今、多くの場合、私は一歩前進し、(それを処理できる場合は)PIMPLパターンを使用して、すべてのプライベートなものを実際のクラスのインターフェースから完全に隠します。

class Example1 {
public:
   void publicOperation();
private:
   void privateOperation1_();
   void privateOperation2_();

   Type1 data1_;
   Type2 data2_;
};
// example 2 header:
class Example2 {
   class Impl;
public:
   void publicOperation();
private:
   std::auto_ptr<Example2Impl> impl_;
};
// example2 cpp:
class Example2::Impl
{
public:
   void privateOperation1();
   void privateOperation2();
private: // or public if Example2 needs access, or private + friendship:
   Type1 data1_;
   Type2 data2_;
};

プライベート(および保護)メンバーをアンダースコアで後置していることに気付くでしょう。 PIMPLバージョンには、外部クラスが操作を見ることさえできない内部クラスがあります。これにより、クラスインターフェイスが完全にクリーンな状態に保たれます。実際のインターフェイスのみが公開されます。秩序について議論する必要はありません。

動的に割り当てられたオブジェクトを構築する必要があるため、クラスの構築中に関連コストが発生します。また、これは、拡張することを意図していないクラスでも非常にうまく機能しますが、階層を持ついくつかの短所があります。プロテクトメソッドは外部クラスの一部である必要があるため、内部クラスに実際にプッシュすることはできません。

コーディングスタイルは驚くほど白熱した会話の源です。そのことを念頭に置いて、私は別の意見を提示するリスクがあります。

コードは、人間が最も読みやすいように作成する必要があります。私はここで何度か与えられたこの声明に完全に同意します。

偏差は、どのロールを取っているかです。

クラスのserがその使用方法を理解できるように、適切なdocumentationを記述して維持する必要があります。ユーザーは、クラスを使用できるようにするためにソースコードを読む必要はありません。これが(手動で、またはソース内のドキュメントツールを使用して)行われる場合、パブリッククラスメンバーとプライベートクラスメンバーがソースで定義される順序は、ユーザーにとって重要ではありません。

ただし、コードを理解にする必要がある人にとっては、コードレビュー、プルリクエスト、またはメンテナンスの際に、順序が非常に重要になります。ルールは単純です。

アイテムは使用する前に定義する必要があります

これはコンパイラのルールではなく、厳密にパブリックなバージョンではありません。私的ルール、しかし常識-人間の可読性ルール。コードを順番に読み取ります。たとえば、クラスメンバーが使用されているのを確認するたびに「ジャグリング」する必要があるが、たとえばその型がわからない場合、コードの読みやすさに悪影響を及ぼします。

厳密にプライベートVSで分割を行うプライベートクラスメンバーはパブリックメソッドで使用された後に表示されるため、パブリックはこのルールに違反します。

3
PolarBear2015

私は POCO C++コーディングスタイルガイド に従う傾向があります。

2
ayaz

実際には、それが問題になることはめったにありません。それは主に個人的な好みの問題です。

クラスのユーザーがそれらをより簡単に見つけられるように、表面上ではパブリックメソッドを最初に置くことは非常に人気があります。しかし、ヘッダーがドキュメントの主要なソースになることはありません。そのため、ユーザーがヘッダーを見るという考えに基づいた「ベストプラクティス」を基にすると、私にとってマークが見落とされるようです。

彼らがクラスをmodifyしている場合、人々はあなたのヘッダーにいる可能性が高く、その場合、彼らはプライベートインターフェイスに注意してください。

どちらを選択する場合でも、ヘッダーを簡潔で読みやすくします。私がクラスのユーザーであろうと、クラスのメンテナーであろうと、たまたま私が探しているあらゆる情報を簡単に見つけることができることは、最も重要なことです。

2
Dan Olson

読みやすさの問題だと思います。

一部の人々は、それらを固定された順序でグループ化することを好むので、クラス宣言を開くときはいつでも、どこを探すべきかすぐにわかります。パブリックデータメンバー。

一般的に、最も重要なことを最初にすべきだと思います。すべてのクラスの99.6%については、おおよそ、パブリックメソッド、特にコンストラクタを意味します。次に、パブリックデータメンバー(ある場合)が続きます(覚えておいてください:カプセル化は良いアイデアです)。その後に、保護されたメソッドやプライベートメソッド、データメンバーが続きます。

これは、大規模プロジェクトのコーディング標準でカバーされる可能性があるものです。確認することをお勧めします。

2
unwind

全体として、パブリックインターフェイスは何よりも優先される必要があります。これは、クラスのユーザーが関心を持つ必要のある主要な/唯一のことです(もちろん、実際には常に成立するとは限りませんが、良いスタートです)。

その中で、メンバーのタイプと定数が最初に最適で、その後に建設演算子、演算、メンバー変数が続きます。

1
dcw

(コンパイラとダイナミックリンカーに応じて)クラスの最後(つまり、インターフェイスの最後)に追加するだけで、以前のバージョンの共有ライブラリとの互換性を維持でき、その他のものを削除または変更しないことに注意してください。 (これはG ++とlibtoolに当てはまり、GNU/Linux共有ライブラリの3つの部分のバージョン管理スキームはこれを反映しています。)

また、メモリの整列によるスペースの無駄を避けるために、クラスのメンバーを順序付ける必要があるという考えもあります。 1つの戦略は、メンバーを最小サイズから最大サイズの順に並べることです。 C++でもCでもこれを行ったことはありません。

1
Reed Hedges

私たちのプロジェクトでは、アクセス順ではなく、使用順にメンバーを並べ替えます。つまり、メンバーが使用されるときにメンバーを注文します。パブリックメンバーが同じクラスのプライベートメンバーを使用する場合、次の(単純な)例のように、そのプライベートメンバーは通常、どこかでパブリックメンバーの前に配置されます。

class Foo
{
private:
  int bar;

public:
  int GetBar() const
  {
    return bar;
  }
};

ここでは、メンバーbarがメンバーGetBar()の前に配置されています。これは、前者が後者によって使用されるためです。これにより、次の例のように、複数のアクセスセクションが生じる可能性があります。

class Foo
{
public:
  typedef int bar_type;

private:
  bar_type bar;

public:
  bar_type GetBar() const
  {
    return bar;
  }
};

bar_typeメンバーはbarメンバーによって使用されています。

どうしてこれなの?わからないけど、実装のどこかでメンバーに出会い、さらに詳細が必要な場合(そしてIntelliSenseが再び台無しになっている場合)は、作業している場所の上のどこかで見つけることができるのは自然なことのようです。

1

クラスを使用してパブリックインターフェイスを最初にリストする人にとっては、非常に役立ちます。それは彼らが気にし、使用できる部分です。保護されたプライベートはその後に続くことができます。

パブリックインターフェイス内では、コンストラクター、プロパティアクセサーとミューテーター、および演算子を別々のグループにグループ化すると便利です。

1
Todd