クラスBによってのみ操作されるべきクラスAを適切にカプセル化しようとしています.
ただし、クラス B から継承したい。
友達がいる B はうまくいきません。友情は継承されません。
私が望むものを達成するための一般的に受け入れられている方法は何ですか、それとも私は間違いを犯していますか?
もう少しわかりやすいように、クラス A は複雑なシステムの状態を表します。これは、クラス A の状態を変更するために適用できるアクションである B によってのみ変更される必要があります。
クラスBによってのみ操作されるべきクラスAを適切にカプセル化しようとしています.
ただし、クラス B から継承したい。
友達がいる B はうまくいきません。友情は継承されません。
私が望むものを達成するための一般的に受け入れられている方法は何ですか、それとも私は間違いを犯していますか?
もう少しわかりやすいように、クラス A は複雑なシステムの状態を表します。これは、クラス A の状態を変更するために適用できるアクションである B によってのみ変更される必要があります。
再設計が必要なようです。クラス A は状態を表しますが、クラス B は一連のアクションを表します。そこには関係がありますが、継承関係ではありません。構成をお勧めします。私が知る限り、あなたは ISA 関係よりも HASA 関係を望んでいます。
私があなたを正しく理解しているなら、あなたはBを持ちたいと思っています。クラスAの内部実装にアクセスするには派生物ですよね?
残念ながら、C++ には、C# や Java などの言語が持つ「内部」保護レベルの概念がありません。
プライベート実装パラダイム(pimpl) (不透明ポインターとも呼ばれる) を使用して、A と B のコンシューマーが表示しないパブリック アクセス レベルを使用してシステム内の機能を公開することを検討できます。
B の子孫が A に直接アクセスできるようにしたいと思いますか? A と B が密結合している場合、独立した定義ではなく、A を B 自体内で保護されたクラス定義にすることができます。例えば
class B
{
protected:
class A
{
};
};
もう 1 つのアイデアは、アクションを AEG に委譲する保護されたメソッドを B に作成することです。
class A
{
friend class B;
private:
void DoSomething();
};
class B
{
protected:
void DoSomething(A& a) { a.DoSomething(); }
};
すべてをそのままにしておくと、最も簡単な方法は、保護されたメソッドを B に追加して、必要な A の同等の機能にアクセスできるようにすることです。これにより、カプセル化が B のサブクラスのみに開かれます。
これを行う最も簡単な方法は、単純に B に A を含めることです。
クラス B { 保護: A a_; };
次に、B から継承し、A を操作できるクラス C を作成できます。C が A に対して任意の操作を実行できない場合は、A を B で非公開にし、B で C が実行できる保護されたメソッドを提供します。次のように、承認されたことを A に行うために使用します。
クラス B { プライベート: A a_; 保護されています: void doSomethingToA(); };
B が A のいくつかの仮想をオーバーライドする必要がある場合を除き、包含は行くべき道です (クラス B には型 A のプライベート メンバーが含まれます)。その場合、プライベート継承が最も近いものです。
継承したい理由がわかりません。A のすべてを非公開にし、B を友人にします。B は自由に操作できる A のメンバーを持ちます。
あなたがこれを説明する方法は、継承ではなく構成のように聞こえます。例えば
class ComplexMachine {
public:
setState(int state);
};
class Operator {
public:
Operator(ComplexMachine & m) :_m(m) {};
void drive() { _m->setState(1); }
void turn() { _m->setState(2); }
void stop() { _m->setState(0); }
private:
ComplexMachine _m;
};
class SmoothOperator : public Operator { }
B のみが A で動作することを確認したい場合は、A のインスタンスを非公開にし、保護されたインターフェイスを B からその子孫に公開します。
class A
{
public:
void foo() {}
};
class B
{
private:
A a;
protected:
void CallAFoo() { a.foo() };
};
class C : public B
{
void goo() { CallAFoo(); }
};
あなたの質問から私が理解したことから、あなたはいくつかのポリモーフィズムが必要になります。抽象クラス Aが必要で、クラス B はクラス A を継承します。また、protectedキーワードを使用すると、継承するクラスが特定の情報にアクセスできると同時に、他の情報へのアクセスを拒否できます。以下に少し例を示します。
// dynamic allocation and polymorphism
#include <iostream>
using namespace std;
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
virtual int area (void) =0;
void printarea (void)
{ cout << this->area() << endl; }
};
class CRectangle: public CPolygon {
public:
int area (void)
{ return (width * height); }
};
class CTriangle: public CPolygon {
public:
int area (void)
{ return (width * height / 2); }
};
int main () {
CPolygon * ppoly1 = new CRectangle;
CPolygon * ppoly2 = new CTriangle;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
ppoly1->printarea();
ppoly2->printarea();
delete ppoly1;
delete ppoly2;
return 0;
}
cplusplus.comから取得したコード(ポリモーフィズムと抽象クラスに関する情報も含まれています)。
あなたが与えた情報を使って作業する:
クラス B は、クラス A の不変条件を維持する責任を負う必要があり、クラス B は A を操作する唯一の方法であるべきです。派生クラスまたは呼び出し元のクライアントは、A が存在することを知る必要はありません。
(設計の観点からすると、A が存在する必要さえありませんが、私はそのような分離の十分な実際的な理由に遭遇したので、あなたに反対するつもりはありません ;))
これには、多くのボイラープレート コードを記述するか、インターフェイスにいくつかの策略が必要になる場合があります。たとえば、クライアントがクラス A を使用して情報を照会することは許可されているが、それを変更することは許可されていない場合、B は集約された A に const & を渡すことができます。