0

(この質問は反射に関連していますが、実際には反射についてではありません)

このクラスの階層 ( とclass Aなどclass B : public A) があり、インスタンス固有のデータに加えて、クラス固有のデータをすべてのインスタンスで共有したいと考えています。たとえばFunnyClassName、クラスごとに文字列が必要だとします。

次のように、クラスごとのデータに非仮想ゲッターを使用できるようにしたいと考えています。

/*can it be static? */ const std::string& A::GetFunnyName();

最も重要なことは、クラスを継承する際にボイラープレート コードをまったく使用しないか、できるだけ少なくしたいということです。class Agetter は(クラス階層のルート) に一度実装されます。クラス B は、別の方法でその FunnyClassName を指定する必要があります。

クラスの型ハッシュをキーとして使用する Multiton オブジェクトが合理的な解決策の基礎になる可能性があることが示唆されています (たとえば、SO に関するここでの質問で間接的に)。そうですか?これを行う「標準」コードはありますか (STL または Boost など)? 関連する別のアプローチはありますか?

:

  • これは不可能だと思いますか?この質問この(簡潔な)回答を参照してください。しかし、一部のコメンターやレスポンダーが示唆するように、非静的ゲッターが必要になる場合があり、すべてのクラスでゲッターを書き換える必要がない (つまり、RTTI を使用する) というより弱い制約を作成する必要があります。
  • C++ に静的仮想データ メンバーがある場合、これは些細なことです - virtual static const std::string FunnyName。静的仮想メソッドでも可能ですが、基本クラスに実装されているゲッターのみの要求を削除した場合に限ります。と のようなものが/* static??*/ static const std::string& A::GetFunnyName() { return "Aye"; }あり/* static??*/ const std::string& B::GetFunnyName() { return "Bee"; }ます。
  • テンプレート化されたクラスの場合には特に興味はありませんが、それにも対処したいのであれば、それは素晴らしいことです。
  • FunnyName() は単なる例です。かもしれませんconst Thing& GetFunnyThing()。typeid.name() からのクラスの名前、またはそのデマングリングなどは必要ありません。
  • C++11 は問題ありませんが、C++03 でのソリューションの方が優れています。C++14 は使用しないでください。
4

3 に答える 3

2

を使用したくない場合はvirtual、テンプレートを使用できます。このイディオムの名前はCuriously recurring template patternで、ATL と WTL で使用されます。

コードを見てください。

#include <iostream>
#include <string>

template <typename C>
class Super
{
public:
    std::string GetFunnyName() const
    {
        C *thiz = static_cast<C *>(this);
        return thiz->GetFunnyName();
    }
};
class A : public Super<A>
{
public:
    std::string GetFunnyName() const
    {
        return "A";
    }
};
class B : public Super<B>
{
public:
    std::string GetFunnyName() const
    {
        return "B";
    }
};

template <typename TSuper>
void OutputFunny(const TSuper &obj)
{
    std::cout << obj.GetFunnyName() << "\n";
}

int main()
{
    A a;
    B b;

    OutputFunny(a);
    OutputFunny(b);
}

(実際の例)

Binheritを作成する場合はA、次のようにコーディングします。

template <typename C>
class A_base : public Super<C>
{
    ...
};
class A : public A_base<A> { };

class B : public A_base<B>
{
    ...
};

(実際の例)

サンプル コードでは、コンパイル時のポリモーフィズムを使用しています。したがって、実行時に適用することはできません。実行時に「FunnyName」を取得したい場合はvirtual実行時ポリモーフィズムを使用する必要があります。


不思議なことに、繰り返されるテンプレート パターンは次のように機能します。

パターンの基本形が見えてきます。

template <typename C>
class Super
{
    void foo()
    {
        C *thiz = static_cast<C *>(this);
        thiz->foo();
    }
    ...
};

class Derived : public Super<Derived>
{
    void foo()
    {
        std::cout << "fooo!!\n";
    }
    ...
};

派生クラスは、それ自体をテンプレート パラメーターとして継承SuperDerivedます。

Super<Derived>は次のように具体化されます。

template <>
class Super<Derived>
{
    void foo()
    {
        Derived *thiz = static_cast<Derived *>(this); // 1
        thiz->foo();                                  // 2
    }
};

On1では、thisポインタをにキャストし、このキャストされたポインタで onDerived *を呼び出します。ポインタの型が であるため、ステートメントは を呼び出します。foo2Derived *thiz->foo();Derived::foo

ウィキペディアのページの説明は良さそうです)

于 2014-08-20T09:15:23.103 に答える
0

これが質問に答えるかどうかはわかりませんが、使用を検討する必要がありますtypeid。これは RTTI の一部であるため、静的型と動的型を区別できます。

基本クラスに次のコードを含めます。

struct A {
    ...
    std::string GetFunnyName() {return typeid(*this).name();}
};

異なる派生クラスに対して返される文字列は異なります。ただし、これらの文字列がどのように見えるかを制御することはできません (たとえば、型名のマングル バージョンが含まれる場合があります)。

を使用して、これらのシステム生成の名前をなどstd::mapのより好ましい名前に変換したい場合がありますが、派生クラスの名前を抽出することはできません (または、可能ですが、移植可能な方法ではできません)。FunnyName1FunnyName2

これがデモです。


FunnyThing編集:本当に ではなく で作業したいのでFunnyName、必ず . を使用する必要がありますmap。静的オブジェクトにします。

struct A {
private:
    static std::map<std::string, Thing*> my_map;
    ...
}

次に、それを使用して次のように変換stringThingます。

struct A {
    ...
public:
    Thing& GetFunnyThing() {return *my_map[typeid(*this).name()];}
    ...
};

ここで、各派生クラスは、戻りたいものRegisterThingを「宣言」するために使用する必要があります。Thing

struct A {
    ...
protected:
    static void RegisterThing(std::string name, Thing* thing) {my_map[name] = thing;}
    ...
}

このメソッドを 1 回だけ適切なタイミングで呼び出すことは、さまざまな方法で実装できます (Singleton パターンのように) ので、例を挙げて問題を複雑にしたくありません。

于 2014-08-20T11:52:56.040 に答える