プライベートメンバー変数をC ++ヘッダーに配置する理由の答えは、コンパイラがスタック内を適切に移動するコードを生成できるように、インスタンスが宣言されたポイントでクラスのサイズを知る必要があるということです。
プライベートメンバーをヘッダーに配置する必要があるのはなぜですか?
しかし、クラス定義でプライベート関数を宣言する理由はありますか?
代替案は、本質的にはくぼみのイディオムですが、余分な間接化はありません。
この言語機能は歴史的なエラー以上のものですか?
プライベートメンバー変数をC ++ヘッダーに配置する理由の答えは、コンパイラがスタック内を適切に移動するコードを生成できるように、インスタンスが宣言されたポイントでクラスのサイズを知る必要があるということです。
プライベートメンバーをヘッダーに配置する必要があるのはなぜですか?
しかし、クラス定義でプライベート関数を宣言する理由はありますか?
代替案は、本質的にはくぼみのイディオムですが、余分な間接化はありません。
この言語機能は歴史的なエラー以上のものですか?
回答:
プライベートメンバー関数はでありvirtual
、C ++(vtableを使用する)の一般的な実装では、クラスのすべてのクライアントが仮想関数の特定の順序と数を知る必要があります。これは、1つ以上の仮想メンバー関数がであっても適用されますprivate
。
コンパイラーの実装の選択は言語仕様に影響を与えるべきではないため、これは「馬の前にカートを置く」ように見えるかもしれません。ただし、実際には、C ++言語自体は、vtablesを使用する実際の実装(Cfront)と同時に開発されました。
定義以外のクラスにメソッドを追加できるようにした場合、任意のファイルの任意の場所に追加できます。
これにより、すべてのクライアントコードが、プライベートデータおよび保護されたデータメンバーに簡単にアクセスできるようになります。
クラスの定義が完了すると、一部のファイルを作成者によって特別に祝福されているとマークして、それを拡張する方法はありません。フラットな翻訳単位があります。そのため、特定のメソッドセットが公式であるか、クラス作成者によって祝福されていることをコンパイラに伝える唯一の合理的な方法は、クラス内で宣言することです。
C ++のメモリに直接アクセスできることに注意してください。つまり、通常、クラスと同じメモリレイアウトでシャドウタイプを作成し、独自のメソッドを追加する(またはすべてのデータをパブリックにする)ことは簡単reinterpret_cast
です。または、私はあなたのプライベート関数のコードを見つけるか、それを逆アセンブルすることができます。または、シンボルテーブルで関数アドレスを検索し、または直接呼び出します。
これらのアクセス指定子は、これらの攻撃を防ぐことはできません。それは不可能だからです。クラスの使用方法のみを示します。
受け入れられた答えは、仮想プライベート機能についてこれを説明していますが、それは質問の1つの特定の側面だけに答えます。それはOPが尋ねたものよりかなり制限されています。したがって、言い換える必要があります:なぜ非仮想プライベート関数をヘッダーで宣言する必要があるのですか?
別の答えは、クラスを1つのブロックで宣言する必要があるという事実を呼び出します。その後、クラスは封印されて追加できません。それは、ヘッダーでプライベートメソッドを宣言することを省略し、それを他の場所で定義しようとすることによって行うことです。ナイスポイント。クラスの一部のユーザーが他のユーザーが観察できない方法でそれを増強できるのはなぜですか?プライベートメソッドはその一部であり、これから除外されません。しかし、その後、なぜそれらが含まれているのかを尋ねると、少しトートロジカルに見えます。クラスのユーザーがそれらについて知っている必要があるのはなぜですか?それらが表示されていない場合、ユーザーは追加できませんでした。
そのため、デフォルトでプライベートメソッドを含めるだけでなく、ユーザーに見えるようにするための特定のポイントを提供する答えを提供したかったのです。パブリック宣言を必要とする非仮想プライベート関数の機構的理由は、その根拠の一部としてのPimplイディオムについてのHerb SutterのGotW#100に記載されています。私たちは皆、それについて知っていると確信しているので、ここではPimplについては説明しません。ただし、関連するビットは次のとおりです。
C ++では、ヘッダーファイルのクラス定義のいずれかが変更された場合、そのクラスのすべてのユーザーを再コンパイルする必要があります。クラスのユーザーがアクセスすることさえできないプライベートクラスメンバだけが変更された場合でも。これは、C ++のビルドモデルがテキストのインクルードに基づいているため、およびC ++が、呼び出し元がプライベートメンバーによって影響を受ける可能性のあるクラスに関する2つの主なことを知っていると想定しているためです。
- サイズとレイアウト:[メンバーと仮想関数の-自明であり、パフォーマンスに優れていますが、ここにいる理由ではありません]
- 関数:呼び出しコードは、非プライベート関数でオーバーロードするアクセスできないプライベート関数を含む、クラスのメンバー関数への呼び出しを解決できる必要があります。プライベート関数がより一致する場合、呼び出しコードはコンパイルに失敗します。(C ++は、安全上の理由からアクセシビリティチェックの前にオーバーロード解決を実行するように意図的な設計決定を行いました。たとえば、関数のアクセシビリティをプライベートからパブリックに変更しても、正当な呼び出しコードの意味は変わらないと考えられました。)
もちろん、Sutterは委員会のメンバーとして非常に信頼できる情報源であるため、彼は「意図的な設計上の決定」を知っています。そして、セマンティクスの変更や後で誤ってアクセス可能性が損なわれることを回避する方法として、プライベートメソッドのパブリック宣言を要求するという考えは、おそらく最も説得力のある理由です。ありがたいことに、以前は全体がかなり無意味に見えたので!
これには2つの理由があります。
まず、アクセス指定子がコンパイラ用であり、実行時には関係ないことを理解してください。スコープ外のプライベートメンバーにアクセスすると、コンパイルエラーになります。
短い、1行または2行の関数を考えます。他の場所でのコードの複製を減らすために存在します。これは、多くの代わりに1つの場所でアルゴリズムまたはその他の動作を変更できるという利点もあります(ソートアルゴリズムの変更など)。
ヘッダーに1行または2行の短い行を挿入するか、そこに関数のプロトタイプと実装を追加しますか?ヘッダーで簡単に見つけることができます。また、短い関数の場合、個別の実装を使用する方がはるかに冗長です。
もう1つの大きな利点があります。
プライベート関数はインライン化できる場合があり、これには必然的にヘッダー内にあることが必要です。このことを考慮:
class A {
private:
inline void myPrivateFunction() {
...
}
public:
inline void somePublicFunction() {
myPrivateFunction();
...
}
};
プライベート関数は、パブリック関数とともにインライン化できる場合があります。inline
キーワードは技術的には提案であり、要件ではないため、これはコンパイラの裁量で行われます。
inline
れます。そこでキーワードを使用する理由はありません。
.cpp
クラス定義の外部で定義されているメンバー関数によってインライン化されている非メンバー関数をファイルに含めることは可能ですが、そのような関数はプライベートではありません。
ヘッダーファイルにプライベートメソッドを含めるもう1つの理由:パブリックインラインメソッドは、1つまたは複数のプライベートメソッドを呼び出すだけではない場合があります。ヘッダーにプライベートメソッドがあることは、パブリックメソッドの呼び出しをプライベートメソッドの実際のコードに対して完全にインライン化できることを意味し、インライン化はプライベートメソッドの呼び出しで停止しません。別のコンパイルユニットからでも(およびパブリックメソッドは通常、異なるコンパイルユニットから呼び出されます)。
もちろん、プライベートメソッドを含むすべてのメソッドを知らない場合、コンパイラがオーバーロード解決の問題を検出できないという理由もあります。