2012-05-08 21 views
4

私は派生クラスに静的メンバーを強制する方法は?

Primitiveは純粋仮想関数を通じてそのサブクラスで、例えばintersect()、いくつかの機能を強制するなど、私はSphere他のいくつかのclasses--の派生元の基本クラス、PrimitivePlaneを、持っています。 intersectの計算はインスタンスデータに依存するため、メンバーメソッドとして持つことは意味があります。

私の問題は次のように発生します。 派生したすべてのインスタンスが、そのメンバーの型を識別できるように、たとえばstd::string type()メンバメソッドを使用します。同じクラスのすべてのインスタンスが同じ型を返すので、type()staticメソッドにすることは意味があります。また、すべてのPrimitiveサブクラスにこのメソッドを実装したいので、上記のintersect()のような純粋な仮想関数にしたいと思います。

ただし、静的仮想メソッドはC++では使用できません。 C++ static virtual members?Can we have a virtual static method ? (c++) 似たような質問がありますが、派生クラスに関数を強制する必要はありません。

誰でも私を助けることができますか?

+1

ポリモーフィズムが必要な場合は、なぜ静的タイプが必要でしょうか?インタフェースで基本クラスポインタを使用し、実行時に型を動的に把握できるようにします。それは、仮想メソッドの全体のポイントのようなものです。 – AJG85

+0

バーチャルスタティックはどのように呼びますか? –

+0

私は[http://stackoverflow.com/questions/325555/c-static-member-method-call-on-class-instance]や 'this'ポインタのようなインスタンスを通して呼び出しを行うことを考えていました、例えば[http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fcplr039.htm] – wsaleem

答えて

0

派生クラスごとに適切に実装された静的な静的メソッドを呼び出すこともできます(静的な文字列を返す)こともできます。文字列は静的であるために

#include <iostream> 
#include <string> 

struct IShape { 
    virtual const std::string& type() const =0; 
}; 

struct Square : virtual public IShape { 
    virtual const std::string& type() const { return type_; } 
    static std::string type_; 
}; 
std::string Square::type_("square"); 

int main() { 

    IShape* shape = new Square; 
    std::cout << shape->type() << "\n"; 

} 

あなたはとにかくすべてのサブクラスのtype()メソッドを実装する必要があります注ので、あなたができる最善です。ただし、文字列の代わりにenumを使用することを検討することもできます。コード内で不要な文字列の比較を避けてください。

今、問題の根本に戻って、私はデザインに多少の欠陥があると思います。同じ種類の形状であっても、交点から生じる形状の種類が大きく異なるため、実際にはすべての種類の図形で動作する一般的な交差関数を持つことはできません(2つの平面は平面、線、すべて、例えば)。だから一般的な解決策を提供しようとすると、あなたはこれらの種類のチェックをその場で実行することに気付くでしょう。

+0

ありがとう、私は仮想メソッドを介して静的な文字列を返す考えが好きです。しかし、私はすべてのサブクラスで 'static std :: string myType'宣言を使用します。これはコードの重複のようです。それを避けるために継承を使用することはできませんか? – wsaleem

+0

残念なことに、とにかく各サブクラスの仮想メソッドから実際に離れていくことはできません。私はいくつかの情報とコメントを追加しました。それは難しい問題です... – juanchopanza

+0

@juanchopanzaすることができます、私の答えを参照してください。 –

2

あなたが提供したリンクで説明した理由から、仮想メンバーを静的にすることはできません。

派生クラスで関数を強制する要件についての質問は、派生クラスが関数を実装する必要があることを強制する抽象基本クラスで関数を純粋仮想にすることによって処理されます。

4

これを1秒間考えてみましょう。私はあなたが2つのサブクラスを持っているだけではないので、これを一般化しようと思います。

最初に心に浮かぶのは、コードの複製、拡張性、親密性です。これらを拡張しましょう:

さらにクラスを追加したい場合は、可能な限り最小限の場所でコードを変更する必要があります。

intersect操作が可換あるので、ABと交差するためのコードがBAと交差するので、それ自体が論外であるクラス内のロジックを維持するためのコードと同じ場所にする必要があります。

また、新しいクラスを追加すると、既存のクラスを変更する必要はなく、デリゲートクラスを拡張する必要があります(はい、ここにパターンがあります)。

これはあなたの現在の構造である、私は仮定(またはintersectのための同様の、おそらく、戻り値の型が、今のために重要ではありません):

struct Primitive 
{ 
    virtual void intersect(Primitive* other) = 0; 
}; 
struct Sphere : Primitive 
{ 
    virtual void intersect(Primitive* other) 
}; 
struct Plane : Primitive 
{ 
    virtual void intersect(Primitive* other); 
}; 

は、我々はすでに我々がPlane内部の交差点ロジックを望んでいないことを決めましたまたはSphereので、私たちは新しいclassを作成:

struct Intersect 
{ 
    static void intersect(const Sphere&, const Plane&); 
    //this again with the parameters inversed, which just takes this 
    static void intersect(const Sphere&, const Sphere&); 
    static void intersect(const Plane&, const Plane&); 
}; 

これは、新しい機能を追加することがありますクラス、および新しいロジックです。たとえば、Lineクラスを追加する場合は、メソッドintersec(const Line&,...)を追加するだけです。

新しいクラスを追加するときに、既存のコードを変更しないことを忘れないでください。したがって、交差関数内の型をチェックすることはできません。

私たちは、種類に応じて異なる動作をし、このための行動クラス(戦略パターン)を、作成することができ、私たちはその後拡張することができます。

struct IntersectBehavior 
{ 
    Primitive* object; 
    virtual void doIntersect(Primitive* other) = 0; 
}; 
struct SphereIntersectBehavior : IntersectBehavior 
{ 
    virtual void doIntersect(Primitive* other) 
    { 
     //we already know object is a Sphere 
     Sphere& obj1 = (Sphere&)*object; 
     if (dynamic_cast<Sphere*>(other)) 
      return Intersect::intersect(obj1, (Sphere&) *other); 
     if (dynamic_cast<Plane*>(other)) 
      return Intersect::intersect(obj1, (Plane&) *other); 

     //finally, if no conditions were met, call intersect on other 
     return other->intersect(object); 
    } 
}; 

そして、私たちが持っているだろう私たちのオリジナルの方法で:

struct Sphere : Primitive 
{ 
    virtual void intersect(Primitive* other) 
    { 
     IntersectBehavior* intersectBehavior = BehaviorFactory::getBehavior(this); 
     return intersectBehavior.doIntersect(other); 
    } 
}; 
struct Sphere : Primitive 
{ 
    virtual void intersect(Primitive* other) 
    { 
     SphereIntersectBehavior intersectBehavior; 
     return intersectBehavior.doIntersect(other); 
    } 
}; 

でもクリーンなデザインは、行動のうち抽象実際の型に、工場を実装することになります

であり、intersectは仮想クラスである必要はありません。なぜなら、これはすべてのクラスでこれを行うためです。新しいクラスを追加するときは、この設計

  • に既存のコードを変更する必要が従っていない場合は

  • は、それぞれの新しいタイプ
  • のみIntersectBehaviorを延長する単一の場所での実装を持っています新しいタイプのためのIntersectクラスの実装を提供する

そして、私はこれをさらに完成させることができると確信しています。

+0

詳細な回答ありがとうございます。私は元の郵便の表現の経済はあなたを迷子に導いたと思う。 'intersect()'は、他の 'Primitive'インスタンス(ポインタ)ではなく、' const Ray& 'を引数としてとります。それぞれの「プリミティブ」クラスは、それが「レイ」とどのように交差するかを知っているのが妥当と思われます。外側に 'intersect'を動かすにはカスタムの' Intersect'クラスがカプセル化に違反する 'Primitive'内部にアクセスする必要があります。 – wsaleem

+0

あなたのタイプの識別は、最終的には私が一般的に集めた「dynamic_cast」に沸きます。どんなビュー? – wsaleem

+0

@wsaleemも、netiherには 'id'メンバーがいます。しかし、 'dynamic_cast'はすでに存在しています。 –

1

同じクラスのすべてのインスタンスが同じ型を返すので、type()を静的メソッドにするのが理にかなっています。

いいえ。関数を呼び出すためにオブジェクトのインスタンスを必要としないときは、静的メソッドを使用します。この場合、オブジェクトのタイプを識別しようとしているので、にはにインスタンスが必要です。

すべてのオブジェクトはすべてのオブジェクトで共有されるため、重複は心配する必要はありません。 1つの例外は、関数がインラインである場合ですが、コンパイラはオーバーヘッドを最小限に抑えるために最善を尽くし、コストが大きすぎる場合は非インラインにすることがあります。

P.S.クラス階層の外部でクラスを識別するように要求するのは、通常、悪いコードの匂いです。別の方法を探してみてください。

+0

''インスタンスを必要としないときは静的メソッドを使用します。インスタンスを必要とするためです。 ""ありがとう、それは本当です。私はそれを考えなかった。 ''すべてのメソッド本体はすべてのオブジェクトで共有されているので、重複について心配する必要はありません。 – wsaleem

+0

あなたと@ ajg85の両方が私の好奇心を感じます。私は詳細を教えてください、そして、より良い選択肢を提案するかもしれません。 'Primitive *'オブジェクトを通して 'Primitive :: emitXml()'メソッドを呼びたいと思います。発行されたXMLには、 ' ...'、ポインタが指し示す 'Primitive'オブジェクトの型に依存します。 'emitXml()'関数の 'type'属性の値を埋める' type() '関数が必要です。 – wsaleem

+0

@wsaleem、それは本当に正当なユースケースです。多くの場合、オブジェクト型に応じて異なる動作を得るために 'if'ステートメントでそれを使用することが意図されています。これが私が参照していたコードの匂いです。 –

関連する問題