可能です!
しかし、正確には何が可能か、絞り込みましょう。静的呼び出し「SomeDerivedClass :: myfunction()」とポリモーフィック呼び出し「base_class_pointer-> myfunction()」を介して同じ関数を呼び出すことができるようにするために必要なコードの重複のため、人々はしばしばある種の「静的仮想関数」を必要とします。このような機能を許可する「法的」な方法は、関数定義の複製です。
class Object
{
public:
static string getTypeInformationStatic() { return "base class";}
virtual string getTypeInformation() { return getTypeInformationStatic(); }
};
class Foo: public Object
{
public:
static string getTypeInformationStatic() { return "derived class";}
virtual string getTypeInformation() { return getTypeInformationStatic(); }
};
基本クラスに多数の静的関数があり、派生クラスがそれらのすべてをオーバーライドする必要があり、仮想関数に重複する定義を提供するのを忘れた場合はどうなるでしょうか。右、実行中に、追跡が難しい奇妙なエラーが発生します。コードの重複を引き起こすのは悪いことです。次はこの問題を解決しようとします(そして、私はそれが完全にタイプセーフであり、typeidやdynamic_castのようなブラックマジックが含まれていないことを前に伝えておきたいと思います。)
したがって、派生クラスごとにgetTypeInformation()の定義を1つだけ提供する必要があり、それがstaticの定義でなければならないことは明らかです。getTypeInformation()が仮想の場合、 "SomeDerivedClass :: getTypeInformation()"を呼び出すことができないため、関数。基本クラスへのポインターを介して、派生クラスの静的関数をどのように呼び出すことができますか?vtableは仮想関数へのポインターのみを格納するため、vtableでは不可能です。仮想関数を使用しないことにしたため、vtableを変更することはできません。次に、基本クラスへのポインターを介して派生クラスの静的関数にアクセスできるようにするには、なんらかの方法でその基本クラス内のオブジェクトのタイプを格納する必要があります。1つのアプローチは、「不思議な繰り返しのテンプレートパターン」を使用して基本クラスをテンプレート化することですが、ここでは適切ではなく、「型消去」と呼ばれる手法を使用します。
class TypeKeeper
{
public:
virtual string getTypeInformation() = 0;
};
template<class T>
class TypeKeeperImpl: public TypeKeeper
{
public:
virtual string getTypeInformation() { return T::getTypeInformationStatic(); }
};
これで、変数「キーパー」を使用して、基本クラス「オブジェクト」内にオブジェクトのタイプを格納できます。
class Object
{
public:
Object(){}
boost::scoped_ptr<TypeKeeper> keeper;
//not virtual
string getTypeInformation() const
{ return keeper? keeper->getTypeInformation(): string("base class"); }
};
派生クラスでは、構築中にキーパーを初期化する必要があります。
class Foo: public Object
{
public:
Foo() { keeper.reset(new TypeKeeperImpl<Foo>()); }
//note the name of the function
static string getTypeInformationStatic()
{ return "class for proving static virtual functions concept"; }
};
構文糖を追加しましょう:
template<class T>
void override_static_functions(T* t)
{ t->keeper.reset(new TypeKeeperImpl<T>()); }
#define OVERRIDE_STATIC_FUNCTIONS override_static_functions(this)
子孫の宣言は次のようになります。
class Foo: public Object
{
public:
Foo() { OVERRIDE_STATIC_FUNCTIONS; }
static string getTypeInformationStatic()
{ return "class for proving static virtual functions concept"; }
};
class Bar: public Foo
{
public:
Bar() { OVERRIDE_STATIC_FUNCTIONS; }
static string getTypeInformationStatic()
{ return "another class for the same reason"; }
};
使用法:
Object* obj = new Foo();
cout << obj->getTypeInformation() << endl; //calls Foo::getTypeInformationStatic()
obj = new Bar();
cout << obj->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic()
Foo* foo = new Bar();
cout << foo->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic()
Foo::getTypeInformation(); //compile-time error
Foo::getTypeInformationStatic(); //calls Foo::getTypeInformationStatic()
Bar::getTypeInformationStatic(); //calls Bar::getTypeInformationStatic()
利点:
- コードの重複が少ない(ただし、すべてのコンストラクターでOVERRIDE_STATIC_FUNCTIONSを呼び出す必要がある)
短所:
- すべてのコンストラクターのOVERRIDE_STATIC_FUNCTIONS
- メモリとパフォーマンスのオーバーヘッド
- 複雑さの増大
未解決の問題:
1)ここではあいまいさを解決する方法として、静的関数と仮想関数に異なる名前がありますか?
class Foo
{
public:
static void f(bool f=true) { cout << "static";}
virtual void f() { cout << "virtual";}
};
//somewhere
Foo::f(); //calls static f(), no ambiguity
ptr_to_foo->f(); //ambiguity
2)すべてのコンストラクタ内でOVERRIDE_STATIC_FUNCTIONSを暗黙的に呼び出す方法は?
const
、メソッドのシグネチャは、暗黙のthis
ポインターに定数としてフラグを立て、暗黙のパラメーターがないため静的メソッドに適用できません。