11

とにかくC++で一種の仮想静的メンバーを持つことはありますか?

例えば:

class BaseClass {
    public:
        BaseClass(const string& name) : _name(name) {}
        string GetName() const { return _name; }
        virtual void UseClass() = 0;
    private:
        const string _name;
};


class DerivedClass : public BaseClass {
    public:
        DerivedClass() : BaseClass("DerivedClass") {}
        virtual void UseClass() { /* do something */ }
};

この例は些細なことですが、すべての派生クラスで常に同じになる複雑なデータのベクトルがある場合、基本クラスのメソッドからアクセスする必要がありますか?

class BaseClass {
    public:
        BaseClass() {}
        virtual string GetName() const = 0;
        virtual void UseClass() = 0;
};


class DerivedClass : public BaseClass {
    public:
        DerivedClass() {}
        virtual string GetName() const { return _name; }
        virtual void UseClass() { /* do something */ }
    private:
        static const string _name;
};

string DerivedClass::_name = "DerivedClass";

すべてのクラスでメンバー _name とそのアクセサー GetName() を再実装する必要があるため、この解決策では満足できません。私の場合、_name の動作と 10 分の 1 の派生クラスに従うメンバーがいくつかあります。

何か案が?

4

5 に答える 5

9

ここに1つの解決策があります:

struct BaseData
{
  const string my_word;
  const int my_number;
};

class Base
{
public:
    Base(const BaseData* apBaseData)
    {
        mpBaseData = apBaseData;
    }
    const string getMyWord()
    {
        return mpBaseData->my_word;
    }
    int getMyNumber()
    {
        return mpBaseData->my_number;
    }
private:
    const BaseData* mpBaseData;
};

class Derived : public Base
{
public:
    Derived() : Base(&sBaseData)
    {
    }
private:
    static BaseData sBaseData;
}

BaseData Derived::BaseData = { "Foo", 42 };
于 2008-08-29T20:11:52.930 に答える
2

答えは質問にあるようです-あなたが提案した方法は正しい方向にあるようですが、それらの共有メンバーが多数ある場合は、それらを構造体またはクラスに集めて、それを過ぎて次のようにします。基本クラスのコンストラクタへの引数。

「共有」メンバーを派生クラスの静的メンバーとして実装することを主張する場合は、派生クラスのコードを自動生成できる場合があります。XSLT は、単純なクラスを自動生成するための優れたツールです。

一般に、この例では「仮想静的」メンバーの必要性は示されていません。このような目的では、継承は実際には必要ないためです。代わりに、基本クラスを使用して、コンストラクターで適切な値を受け入れさせる必要があります。各「サブタイプ」の引数の単一のインスタンスを作成し、共有データの重複を避けるためにポインターを渡します。別の同様のアプローチは、テンプレートを使用し、関連するすべての値を提供するクラスをテンプレート引数として渡すことです (これは一般に「ポリシー」パターンと呼ばれます)。

結論として、元の例では、そのような「仮想静的」メンバーは必要ありません。あなたが書いているコードにそれらがまだ必要であると思われる場合は、詳しく説明し、より多くのコンテキストを追加してみてください。

上記で説明した例:

class BaseClass {
    public:
        BaseClass(const Descriptor& desc) : _desc(desc) {}
        string GetName() const { return _desc.name; }
        int GetId() const { return _desc.Id; }
        X GetX() connst { return _desc.X; }
        virtual void UseClass() = 0;
    private:
        const Descriptor _desc;
};


class DerivedClass : public BaseClass {
    public:
        DerivedClass() : BaseClass(Descriptor("abc", 1,...)) {}
        virtual void UseClass() { /* do something */ }
};

class DerDerClass : public BaseClass {
    public:
        DerivedClass() : BaseClass("Wowzer", 843,...) {}
        virtual void UseClass() { /* do something */ }
};

この解決策について詳しく説明し、初期化解除の問題の解決策を提供したいと思います。

少し変更するだけで、派生クラスのインスタンスごとに「記述子」の新しいインスタンスを必ずしも作成しなくても、上記の設計を実装できます。

各記述子の単一のインスタンスを保持するシングルトン オブジェクト DescriptorMap を作成し、次のように派生オブジェクトを構築するときにそれを使用できます。

enum InstanceType {
    Yellow,
    Big,
    BananaHammoc
}

class DescriptorsMap{
    public:
        static Descriptor* GetDescriptor(InstanceType type) {
            if ( _instance.Get() == null) {
                _instance.reset(new DescriptorsMap());
            }
            return _instance.Get()-> _descriptors[type];
        }
    private:
        DescriptorsMap() {
            descriptors[Yellow] = new Descriptor("Yellow", 42, ...);
            descriptors[Big] = new Descriptor("InJapan", 17, ...)
            ...
        }

        ~DescriptorsMap() {
            /*Delete all the descriptors from the map*/
        }

        static autoptr<DescriptorsMap> _instance;
        map<InstanceType, Descriptor*> _descriptors;
}

これで、次のことができます。

class DerivedClass : public BaseClass {
    public:
        DerivedClass() : BaseClass(DescriptorsMap.GetDescriptor(InstanceType.BananaHammoc)) {}
        virtual void UseClass() { /* do something */ }
};

class DerDerClass : public BaseClass {
    public:
        DerivedClass() : BaseClass(DescriptorsMap.GetDescriptor(InstanceType.Yellow)) {}
        virtual void UseClass() { /* do something */ }
};

実行の最後に、C ランタイムが非初期化を実行するときに、autoptr を含む静的オブジェクトのデストラクタも呼び出します。これにより、DescriptorsMap のインスタンスが削除されます。

これで、実行終了時に削除される各記述子のインスタンスが 1 つになりました。

派生クラスの唯一の目的が関連する「記述子」データを提供することである場合 (つまり、仮想関数を実装するのではなく)、基本クラスを非抽象化し、適切なインスタンスを作成するだけで間に合わせる必要があることに注意してください。毎回記述子。

于 2008-08-29T16:14:57.600 に答える
1

テンプレートを「基本クラス」として使用するという Hershi の提案に同意します。あなたが説明していることから、サブクラス化ではなくテンプレートの使用のように聞こえます。

次のようにテンプレートを作成できます (これをコンパイルしようとはしていません)。


template <typename T>
class Object
{
public:

  Object( const T& newObject ) : yourObject(newObject) {} ;
  T GetObject() const { return yourObject } ;
  void SetObject( const T& newObject ) { yourObject = newObject } ;

protected:

  const T yourObject ;
} ;

class SomeClassOne
{
public:

  SomeClassOne( const std::vector& someData )
  {
    yourData.SetObject( someData ) ;
  }

private:

  Object<std::vector<int>> yourData ;
} ;

これにより、テンプレート クラス メソッドを使用して、データを使用し、テンプレート クラスのさまざまな側面を共有するカスタム クラス内から、必要に応じてデータを変更できます。

継承の使用を意図している場合は、BaseClass で void* ポインターを使用し、キャストなどを処理するという「喜び」に頼る必要がある場合があります。

ただし、説明に基づくと、継承ではなくテンプレートが必要なようです。

于 2008-08-29T17:52:17.523 に答える
1

@Hershi:そのアプローチの問題は、各派生クラスの各インスタンスにデータのコピーがあり、何らかの形で高価になる可能性があることです。

おそらく、このようなことを試すことができます(コンパイル例なしで唾を吐きますが、アイデアは明確なはずです)。


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

struct DerivedData
{
  DerivedData(const string & word, const int number) :
    my_word(word), my_number(number) {}
  const string my_word;
  const int my_number;
};

class Base {
public:
  Base() : m_data(0) {}
  string getWord() const { return m_data->my_word; }
  int getNumber() const { return m_data->my_number; }
protected:
  DerivedData * m_data;
};


class Derived : public Base {
public:
  Derived() : Base() {
    if(Derived::s_data == 0) {
      Derived::s_data = new DerivedData("abc", 1);
    }
    m_data = s_data;
  }
private:
  static DerivedData * s_data;
};


DerivedData * Derived::s_data = 0; 

int main()
{
  Base * p_b = new Derived();
  cout getWord() << endl;
}

静的オブジェクトの削除に関するフォローアップの質問について: 頭に浮かぶ唯一の解決策は、Boost 共有ポインターのようなスマート ポインターを使用することです。

于 2008-08-29T16:48:06.040 に答える
0

リーフ クラスでコードを複製する必要がないように思われるので、基本クラスから中間基本クラスを派生させてみませんか。この中間クラスは静的データを保持し、すべてのリーフ クラスを中間基本クラスから派生させることができます。これは、すべての派生クラスにわたって保持される 1 つの静的データが必要であることを前提としています。これは、あなたの例からそう思われます。

于 2009-05-21T22:49:05.120 に答える