C++のクラスがプライベート関数を宣言しなければならないのはなぜですか?それは実際の技術的な理由(コンパイル時の役割は何ですか)ですか、それとも単に一貫性のためですか?
他の変換ユニットが知るために何も(オブジェクトサイズもvtableエントリも)追加しないので、プライベート関数を宣言する必要がある理由を尋ねました
あなたがそれについて考えるならば、これはファイルでいくつかの関数static
を宣言することに似ています。外部からは見えませんが、コンパイラ自体にとって重要です。コンパイラは、関数を使用する前に、関数のシグネチャを知りたいと考えています。そもそも関数を宣言するのはそのためです。 C++コンパイラは1つのパスであることに注意してください。つまり、使用する前にすべてを宣言する必要があります。1
プログラマーの観点からは、プライベート関数を宣言することはまだ完全に役に立たないわけではありません。 2つのクラスを想像してみてください。そのうちの1つはもう1つのfriend
です。フレンドゾーンクラス2 そのクラスのプライベートがどのように見えるかを知る必要があります(この議論は奇妙になっています)そうでなければ、彼らはそれを使用できません。
正確にC++がこのように設計された理由については、最初に歴史的な理由があると思います。Cで構造体をスライスできないという事実は、C++で採用されたため、クラスをスライスできません(他の言語もC++から分岐しています)。また、単純さについても推測します。クラスを異なるヘッダーファイルに分割し、ソースファイルに通知し、他のユーザーが自分に何かを追加できないようにするコンパイル方法を考案するのがどれほど難しいか想像してみてください。クラス。
最後に、private
関数はvtableのサイズに影響を与える可能性があります。つまり、それらがvirtual
の場合です。
1実際には完全ではありません。クラスにインライン関数がある場合、それらは後で同じクラスで定義された関数を参照できます。しかし、おそらくアイデアはシングルパスから始まり、この例外が後で追加されました。
2特にインライン化されたメンバー関数です。
クラス自体の定義ですべてのメンバーを宣言して、コンパイラがどの関数が許可メンバーであるかを認識できるようにする必要があります。そうしないと、2人目のプログラマーが(偶然に?)やって来てメンバーを追加したり、間違いを犯したり、オブジェクトの保証に違反したりして、未定義の動作やランダムなクラッシュを引き起こす可能性があります。
懸念事項の組み合わせがありますが、次のとおりです。
したがって:
実用的なバイナリ互換性のPOVから:Davidがコメントで述べているように、プライベートvirtual
関数は、このクラスとそれをベースとして使用するクラスのvtableのサイズとレイアウトに影響を与えます。したがって、コンパイラは、それらを呼び出すことができないコードをコンパイルする場合でも、それらについて知る必要があります。
.cppファイルがクラスを再度開き、特定の種類の追加メンバー関数を追加できるようにするために、C++を別の方法で発明し、バイナリ互換性を損なわないように実装する必要がありますか?特定の方法で異なる定義を許可するために、1つの定義ルールを緩和できますか?たとえば、静的メンバー関数と非仮想非静的メンバー関数。
おそらく両方にそうです。現在のODRは非常に定義を「異なる」ものにするものについて厳密ですが、技術的な障害はないと思います(したがって、非常によく似た定義間のバイナリの非互換性を許可する実装には非常に寛大です)。この種の例外をルールに導入するためのテキストは複雑になると思います。
最終的には、「デザイナーがそのように望んでいた」ということになるかもしれませんし、誰かがそれを試し、私が考えもしなかった障害に遭遇したかもしれません。
アクセスレベルは可視性に影響しません。プライベート関数は外部コードに表示され、過負荷解決によって選択される場合があります(これにより、アクセス違反エラーが発生します)。
class A {
void F(int i) {}
public:
void F(unsigned i) {}
};
int main() {
A a;
a.F(1); // error, void A::F(int) is private
}
これが機能するときの混乱を想像してみてください。
class A {
public:
void F(unsigned i) {}
};
int main() {
A a;
a.F(1);
}
// add private F overload to A
void A::F(int i) {}
ただし、最初のコードに変更すると、過負荷解決によって別の関数が選択されます。そして、次の例はどうですか?
class A {
public:
void F(unsigned i) {}
};
// add private F overload to A
void A::F(int i) {}
int main() {
A a;
a.F(1);
}
または、これがうまくいかない別の例を次に示します。
// A.h
class A {
public:
void g() { f(1); }
void f(unsigned);
};
// A_private_interface.h
class A;
void A::f(int);
// A.cpp
#include "A_private_interface.h"
#include "A.h"
void A::f(int) {}
void A::f(unsigned) {}
// main.cpp
#include "A.h"
int main() {
A().g();
}
1つの理由は、C++では友達があなたのプライベートにアクセスできることです。友達がそれらにアクセスするには、友達はそれらについて知っている必要があります。
クラスのプライベートメンバーは引き続きクラスのメンバーであるため、他のパブリックメンバーの実装はそのプライベートメソッドに依存する可能性があるため、宣言する必要があります。それらを宣言すると、コンパイラーはその関数の呼び出しをメンバー関数呼び出しとして理解できるようになります。
.cpp
ファイルでのみ使用され、クラスの他のプライベートメンバーへの直接アクセスに依存しないメソッドがある場合は、匿名の名前空間に移動することを検討してください。その場合、ヘッダーファイルで宣言する必要はありません。
プライベート関数を宣言する必要がある理由はいくつかあります。
最初のコンパイル時エラーチェック
ポイントオブアクセス修飾子は、コンパイル時にプログラミングエラーの特定のクラス(しゃれは意図されていません)をキャッチすることです。プライベート関数は、誰かがクラスの外部からそれらを呼び出した場合、それはバグであり、できるだけ早くそれについて知りたい関数です。
2番目のキャストと継承
C++標準から取得:
3 [注:プライベート基本クラスのメンバーは、継承されたメンバー名としてアクセスできない場合がありますが、直接アクセスできます。ポインター変換(4.10)と明示的キャスト(5.4)の規則により、暗黙的変換が使用されている場合、派生クラスへのポインターからアクセスできない基本クラスへのポインターへの変換は形式が正しくない可能性がありますが、形式は適切です。明示的なキャストが使用されている場合。
人目の友達
友達はそこでプライベートをお互いに見せます。プライベートメソッドは、友達である別のクラスから呼び出すことができます。
第4回一般正気と優れたデザイン
これまでに別の100人の開発者とプロジェクトに取り組んだことがあります。標準と一般的なルールのセットがあると、保守可能性を維持するのに役立ちます。プライベートなものを宣言することは、グループ内の他のすべての人にとって特定の意味を持ちます。
また、これは良いOO設計原則に流れ込みます。何を公開し、何を公開しないか