14

基本クラス メソッドのアドレスを保持できるポインター型があるとします。サブクラス メソッドのアドレスを割り当てて、正しく動作することを期待できますか? 私の場合、基本クラスのポインターで使用しており、オブジェクトの動的型は派生クラスです。

struct B
{
    typedef void (B::*MethodPtr)();
};

struct D: public B
{
    void foo() { cout<<"foo"<<endl; }
};

int main(int argc, char* argv[])
{
    D d;
    B* pb = &d;

    //is the following ok, or undefined behavior?
    B::MethodPtr mp = static_cast<B::MethodPtr>(&D::foo);
    (pb->*mp)();
}

static_cast について話すとき、標準は次のように述べています。

5.2.9.9タイプ「タイプ cv1 T の D のメンバーへのポインター」の右辺値は、タイプ「タイプ cv2 T の B のメンバーへのポインター」の右辺値に変換できます。ここで、B は D の基底クラス (節 10) です。 「T 型の B のメンバーへのポインター」から「T 型の D のメンバーへのポインター」への有効な標準変換が存在し (4.11)、cv2 が cv1 と同じ cv-qualification、または cv1 より大きい cv-qualification である。63) null メンバ ポインタ値 (4.11) は、目的の型の null メンバ ポインタ値に変換されます。クラス B が元のメンバーを含む場合、または元のメンバーを含むクラスの基本クラスまたは派生クラスである場合、メンバーへの結果のポインターは元のメンバーを指します。それ以外の場合、キャストの結果は未定義です。[注: クラス B は元のメンバーを含む必要はありませんが、メンバーへのポインターが逆参照されるオブジェクトの動的型には、元のメンバーが含まれている必要があります。5.5 参照]

いつものように、私は基準を解読するのにとても苦労しています。大丈夫だと言っていますが、上記のテキストが私のコード例の状況に本当に当てはまるかどうかは100%確信が持てません.

4

1 に答える 1

12

有効です。

クラス B に元のメンバーが含まれている場合、

B には D::Foo が含まれていないので、いいえ。

または、元のメンバーを含むクラスのベースです [...]

B は D の基底なので、これが成り立ちます。結果として:

メンバーへの結果のポインターは、元のメンバーを指します

条項 5.2.9 9 は、§ 4.11 で指定されているように、ダウンキャストもできる場合にのみアップキャストできると述べています。

「cv T 型の B のメンバーへのポインター」型の右辺値 (B はクラス型) は、「cv T 型の D のメンバーへのポインター」型の右辺値 (D は派生クラス) に変換できます ( B の 10 節)。 B が D のアクセス不可能 (11 節)、あいまい (10.2) または仮想 (10.1) 基本クラスである場合、この変換を必要とするプログラムは形式が正しくありません。

これは、B がアクセス可能であり、仮想ではなく、D の継承図に 1 回しか表示されない限り、ダウンキャストできることを示しています。

メソッド ポインターのアップキャストに固有の危険性はmp、実際の型が B であるオブジェクトを呼び出す可能性があることです。D::* を処理するコード ブロックが D* も処理する限り、これを回避できます。

于 2010-11-25T02:31:51.733 に答える