クライアント コードから隠したい内部構造を持つライブラリに複数のクラスがあります。クライアントの観点からは、各クラスはライブラリ クラスからクエリされ、不透明なポインターとしてのみ使用されます。例は次のとおりです。
struct SomeSystem;
void doSomethingToSomeSystem(SomeSystem* system, Parameters params);
void doSomethingElseToSomeSystem(SomeSystem* system, Parameters params);
実装側では、 SomeSystem には、呼び出し元には表示されない複数のメンバーがあります。これはすべて問題ありませんが、不格好な使用構文はあまり好きではありません。
SomeSystem* system = lib->getSomeSystem();
doSomethingToSomeSystem(system, params);
doSomethingElseToSomeSystem(system, params);
別のアプローチはこれです:
struct SomeSystem;
namespace somesystem {
void doSomething(SomeSystem* system, Parameters params);
void doSomethingElse(SomeSystem* system, Parameters params);
}
使用法コード:
SomeSystem* system = lib->getSomeSystem();
somesystem::doSomething(system, params);
somesystem::doSomethingElse(system, params);
doSomething
と呼ばれるグローバル メソッドを使用doSomethingElse
して、別の型でも が定義されている場合は、関数のオーバーロードに依存することもできますdoSomething
。ただし、この場合、IDE で SomeSystem のすべての「メンバー」を見つけるのは困難です。
私は実際にメンバー関数を使用したくなる:
struct SomeSystem {
void doSomething(Parameters params);
void doSomethingElse(Parameters params);
};
使用法コード:
SomeSystem* system = lib->getSomeSystem();
system->doSomething(params);
system->doSomethingElse(params);
最後のスニペットは私には良さそうに見えますが、 SomeSystem はもはや不透明なポインターではなく、実際にメンバーを定義しています。私はこれを少し警戒しています。潜在的な問題の 1 つは、1 つの定義ルールです。ただし、クラスの「パブリック」定義と「プライベート」定義は、異なる翻訳単位からしか見えません。ここに隠された他の悪いことはありますか?クライアント コードがスタックまたは new を使用して SomeSystem をインスタンス化しようとすると、明らかにプログラムがクラッシュします。しかし、私はそれを喜んで受け入れます。おそらく、パブリック インターフェイスでプライベート コンストラクターを提供することで、これを回避できます。
もちろん、別のアプローチは、純粋仮想メソッドを使用して抽象クラスを定義することです。ただし、絶対に必要でない場合に備えて、このオーバーヘッドは避けたいと思います。
編集:
明確にするために、クライアントがクラスをインスタンス化しないため、実装が使用するものとは異なるクラスの定義 (一部のメンバーが欠落している) を含むパブリックヘッダーをクライアントに含めることが合法であるかどうか疑問に思っています。
公開ヘッダー:
struct SomeSystem {
void doSomething(Parameters params);
void doSomethingElse(Parameters params);
};
プライベート ヘッダー:
struct SomeSystem {
Member member;
void doSomething(Parameters params);
void doSomethingElse(Parameters params);
};
プライベート ソース (プライベート ヘッダーを含む):
void SomeSystem::doSomething(Parameters params) {
...
}
void SomeSystem::doSomethingElse(Parameters params) {
...
}
これはテスト時に機能しますが、何らかの形で標準に違反しているかどうかはわかりません。2 つのヘッダーが同じ翻訳単位に含まれることはありません。