2016-12-21 13 views
0

同じBase-Class/Interface(Baseクラス1)から継承したいくつかの類似したクラスがありますが、同様の機能をいくつか共有しています。それらはすべて、異なるクラスの独自のメンバー変数も持ち、それらのそれぞれは同じBase-Class/Interface(Baseクラス2)を継承します。基本クラス1の変数を基本クラス2の型で定義することは可能ですか?次に、基本クラス1を使用するクラスの実際の実装では、基本クラス2型の変数を適切な型にします。説明するのは難しいので、以下のように簡略化した例です。子変数を参照する親クラス

//Base-Class 1 
class Shape 
{ 
    public Shape() {} 
    ShapeExtra m_var; 

    //The common functions 
    public GetVar(){ return m_var; } 
} 

class Circle : Shape 
{ 
    public Circle() { m_var = new CircleExtra(); } 

    public void CircleFunc() 
    { 
     m_var.CircleExtraFunc(); 
    } 
} 
class Triangle : Shape 
{ 
    public Triangle() { m_var = new TriangleExtra(); } 

    public void TriangleFunc() 
    { 
     m_var.TriangleExtraFunc(); 
    } 
} 
. 
. 
. 



//Base_Class 2 
class ShapeExtra 
{ 
    public ShapeExtra() {} 
} 

class CircleExtra : ExtraClass 
{ 
    public CircleExtra() {} 
    void CircleExtraFunc() {//Do stuff} 
} 
class TriangleExtra : ExtraClass 
{ 
    public TriangleExtra() {} 
    void TriangleExtra() {//Do stuff} 
} 
. 
. 
. 

だから、私は子クラスでm_varは、独自のユニークなバージョンとして、それを保管する必要があります。 、(W/O余分CircleExtra m_var;)今のでGetVar()作品が、CircleFuncで、m_varはまだShapeExtraのタイプであり、したがってCircleExtraFuncが存在することを知りません。私はそれをしたいときはいつでもm_varを投げつけることができましたが、それは私の現実の事例では反復的で価値がありません。 GetVar()の機能をShapeに保ちながら、ShapeExtraに基づいてユニークなクラスの関数を利用する方法はありますか?

私の中に何か残っているものがあれば質問してください。

+0

質問は何ですか? –

+0

「毎回キャストしないでこれを行う方法はありますか?」 –

+2

"子クラスでm_varを独自のバージョンとして保持する必要があります。"それはすでにです。子クラスの 'm_var'は基本クラスの' m_var'と全く関係ありません。基本クラスには独自の 'm_var'があり、各子クラスも同じです。彼らはユニークで独特です。仮想関数とは異なり、クラスメンバは基本クラスの同じ名前のメンバを "オーバーライド"しません。あなたの質問は不明です。 –

答えて

1

単純に継承して、それが不可能なポインタを使用せずに、 C++は静的に厳密に型指定された言語であるためです。

変数と関数の両方を継承できますが、関数の戻り値をキャストする必要があります。

また、関数をオーバーライドして具体的な型を返すこともできますが、関数内に変数をキャストする必要があります。

サブクラスでコンクリートクラスで同じvarを宣言することもできますが、スーパークラスで変数を非表示にして何も継承しません。

私はむしろテンプレートを使用して解決策を検討したいと思います。変数の型をテンプレート型にし、サブクラスで具体的な型を使用してテンプレートを拡張します。それは完全に動作します。

私はC++で最後にプログラミングして以来、長いことがありました。次の例でエラーがある場合はご容赦ください。私はあなたが簡単にそれを働かせることができると確信しています。

template <class S> 
class Shape { 
    S m_var; 
    //...... 
public: 
    S var() { 
     return m_var; 
    } 
    //....... 
} 

class Circle: Shape <CircleExtra> { 
    // var method returns CircleExtra 
    //...... 
} 

編集: メソッドの仮想呼び出しを可能にするために、いくつかのコメントについては、相関戻り値の型を使用することが可能です。次の例のようなものです。

class Shape { 
public: 
    virtual ShapeExtra *var() = 0; 
} 

template <typename SE> 
class ConcreteShape: Shape { 
public: 
    virtual SE *var() { 
     return &m_var; 
    } 
// Constructor, etc. 

private: 
    SE m_var; 
} 

または一部のバリエーション。 SE *がShapeExtra *(TypeパラメータがShapeExtraを継承しています)に関連付けられている限り、具体的なシェイプはテンプレートを拡張することで利益を得ることができます。また、シェイプインターフェイスを使用してメソッドを透過的に表示できます。クラスShapeはまったくデータメンバを持っていないことを

class ShapeExtra 
{ 
public: 

    virtual ~ShapeExtra() { } 
    virtual void SomeCommonShapeFunc() { std::cout << "Shape"; } 
}; 

class Shape 
{ 
public: 

    virtual ShapeExtra &GetVar() = 0; // Accessor function. 
}; 

注:

+0

このアプローチでは、「シェイプ」が基本クラスとして操作される必要があるシナリオは許可されません。 –

+0

これはおそらく私の問題の最良の解決策だと思います。ご協力ありがとうございました! –

+0

@KirillKobelev Shapeは私のシナリオではまあまあのインターフェースであり、独自のインスタンスではありません。私はそれを完全には指定しなかった、申し訳ありません。 –

1

ポインタを使用すると、これは完全に可能です。

#include <iostream> 
#include <memory> 
using namespace std; 

//Extras 
class ShapeExtra 
{ 
    public: 
    ShapeExtra() {} 
    void ShapeFunc() { std::cout << "Shape"; } 
    virtual ~ShapeExtra() = default; //Important! 
}; 

class Shape 
{ 
    public: 
    std::unique_ptr<ShapeExtra> m_var; 

    //require a pointer on construction 
    //make sure to document, that Shape class takes ownership and handles deletion 
    Shape(ShapeExtra* p):m_var(p){} 

    //The common functions 
    ShapeExtra& GetVar(){ return *m_var; } 
    void ShapeFunc() {m_var->ShapeFunc();} 
}; 


class CircleExtra : public ShapeExtra 
{ 
    public: 
    void CircleExtraFunc() {std::cout << "Circle";} 
}; 

class Circle : public Shape 
{ 
    CircleExtra* m_var; 

    public: 
    Circle() : Shape(new CircleExtra()) { 
     m_var = static_cast<CircleExtra*>(Shape::m_var.get()); 
    } 

    void CircleFunc() 
    { 
     m_var->CircleExtraFunc(); 
    } 
}; 


int main() { 
    Circle c; 
    //use the ShapeExtra Object 
    c.GetVar().ShapeFunc(); 
    //call via forwarded function 
    c.ShapeFunc(); 
    //call the circleExtra Function 
    c.CircleFunc(); 
    return 0; 
} 

Test it on ideone

注意ポインタの使用と仮想デストラクタ:

  • ShapeExtra基底クラスで仮想デストラクタを使用することにより、あなたの例を使用して 、あなたはこのような何かを行うことができますShapeExtra*を使用して、派生クラスのオブジェクトを破棄することができます。
  • 普通のC-ポインタの代わりにstd::unique_ptr<ShapeExtra>を使用すると、の破棄でオブジェクトが適切に削除されていることを確認するので、これは重要です。
  • ShapeShapeExtra*の所有権を引き継いでいます。私たちは、私は後でstd::unique_ptr::reset()を使用してShape::m_var
  • 建設順序を逆参照の上nullptrをチェックするために建設をShapeExtra*を必要とするためにここに決めたが、そのこともできCircleデストラクタ
  • CirleExtra*を削除しないことをどの特に手段、 Circleのコンストラクタを呼び出すときには、最初にCircleExtraという新しいものを作成し、最後にCircleというコンストラクタが実行される前にShapeに渡します。
  • 破壊のためにも(仮想関数を経由して)を含め、私たちのためのShapeExtraを破棄され、その後、ShapeCircle最初(最後に作成された)であるCircleExtra
1

は、私は、次のアプローチをお勧めします。各派生クラスのその後は次のものが必要です。Circleにおける仮想メソッドの

class CircleExtra : public ShapeExtra 
{ 
public: 

    void SomeCommonShapeFunc() { std::cout << "Circle"; } 
}; 

class Circle : public Shape 
{ 
    CircleExtra m_var; // Data member with circle specific class. 

public: 

    virtual ShapeExtra &GetVar() { return m_var; } 
}; 

実装は、基本クラスShapeExtraへの参照を返します。これにより、この追加を基本クラスで使用することができます。

ポインタとテンプレートはまったく使用されないことに注意してください。これにより全体の設計が簡単になります。

関連する問題