5

私には、次のコードのように avoid(Derived::*)()を aにキャストすることは完全に安全に見えvoid(Base::*)()ます:

#include <iostream>
#include <typeinfo>
using namespace std;
struct Base{
    void(Base::*any_method)();
    void call_it(){
        (this->*any_method)();
    }
};
struct Derived: public Base{
    void a_method(){
        cout<<"method!"<<endl;
    }
};
int main(){
    Base& a=*new Derived;
    a.any_method=&Derived::a_method;
    a.call_it();
}

しかし、コンパイラは でのキャストについて不平を言いa.any_method=&Derived::a_method;ます。これは微妙なプログラミング エラーを防ぐための障害なのか、それともコンパイラ ライターの作業を楽にするためのものなのか? クラスに型Baseの知識のないメンバー関数へのポインターを持たせるための回避策はありますか(つまり、テンプレート引数を使用してテンプレートを作成できません)。Derived BaseDerived

4

4 に答える 4

7

にのみ存在し、に存在しないDerived::a_method()データメンバーを使用しようとし、それをオブジェクト(またはから派生したが関連していないオブジェクト)で呼び出すとどうなりますか?DerivedBaseBaseBaseDerived

逆の変換は理にかなっていますが、これは意味がありません。

于 2012-04-15T14:24:30.870 に答える
3

いいえ、潜在的に危険です。

派生クラス関数は、のすべての派生クラスプロパティを使用できます*this。基本クラス関数へのポインターは、派生型でないものも含め、任意の基本クラスインスタンスで呼び出すことができます。

派生クラスではないインスタンスの派生クラスプロパティへのアクセスは機能しないため、派生クラス関数へのポインターを基本クラスポインターへのポインターにキャストすることは正しく許可されていません。

一方、基本クラス関数へのポインターを派生クラス関数へのポインターにキャストすることは安全で合法です。

于 2012-04-15T14:26:00.763 に答える
1

を使用する必要がありますstd::function<void()>。これは、任意のクラスの任意のメンバー、ラムダ、フリー関数、関数オブジェクトなど、必要なものであれば何でもかまいません。これは非常に便利です。

#include <iostream>
#include <typeinfo>
using namespace std;
struct Base{
    std::function<void()> any_method;
    void call_it(){
        any_method();
    }
};
struct Derived: public Base{
    void a_method(){
        cout<<"method!"<<endl;
    }
};
int main(){
    Derived* d = new Derived;
    Base& a= *d;
    a.any_method = [d] { d->a_method(); };
    a.call_it();
}

any_methodここで、の実際の実装がから完全に抽象化されていることがわかりますstruct Base。また、Derivedメソッドの呼び出しなど、あらゆることを実行する関数オブジェクトを提供できます。

于 2012-04-15T14:28:36.233 に答える
0

これは少し意外かもしれないと思います。しかし、あなたがそれについて考えるならば、それは理にかなっています。

2つのタイプ間のキャストを自動化するには、次の関係が成り立つ必要があります。最初のタイプのインスタンスはすべて、2番目のタイプで表現可能である必要があります。

たとえば、dがのインスタンスである場合、のインスタンスはすべてのインスタンスでもあるため、Derived自動的にとしてキャストできます。これが継承です。Base&DerivedBase

さて、メンバー関数へのポインタに関しては、関係は実際には逆になっています。Baseのインスタンスにはどのような方法も存在することは明らかですDerivedが、その逆は当てはまりません。結局のところ、派生の全体的なポイントは、新しい機能をより頻繁に追加することです。

これを視覚化する別の方法は、自由関数を使用することです。thisは通常の関数の単なる暗黙のパラメータです。明示的にすると、次のようになります。

void Base@call_it(Base& self);

void Derived@a_method(Derived& self);

dここで、 typeDerivedbtypeの2つのインスタンスがある場合、次のようになりますBase

  • Base@call_it(d)理にかなっている
  • Derived@a_method(b)コンパイルエラーです

後者の場合もありますDerived@a_method(dynamic_cast<Derived&>(b))が、これにより、プロパティを実際に検証するためのランタイムチェックが導入されます。静的には決定できません。

于 2012-04-15T14:54:22.773 に答える