2

派生クラスへのキャストが問題を解決するという問題に遭遇しました。私は、それがUBにつながる可能性があるというSOの答えを見つけました。それをテストし、コンパイルして正しく動作させました。未定義の動作ですか?もしそうなら、この問題への正しいアプローチは何でしょうか?

class A
{
public:
    A(){};
    ~A(){}
};

class B : public A
{
public:
    B(){};
    ~B(){}
    void Show() { std::cout << "Show" << std::endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    A a;
    B* b = static_cast<B*>(&a);
    b->Show();

    return 0;
}
4

3 に答える 3

9

基本型へのポインターが実際に派生型のインスタンスを指している限り、そのような使用法は C++ 標準に従って未定義ではありません。ただし、コードサンプルでは、​​ポインターbは のインスタンスBまたはその派生型 (存在しないもの) を指しておらず、 のインスタンスを指していますA。したがって、コードは実際には未定義の動作を呼び出します。

私は、それがUBにつながる可能性があるというSOの答えを見つけました。それをテストし、コンパイルして正しく動作させました。

一部のコードがコンパイルされて正しく動作するという事実は、コードが未定義の動作を呼び出す可能性を排除するものではありません。未定義の動作には「動作しているように見える」ものが含まれるためです。未定義の動作を避ける必要があるのは、次に UB を呼び出したときに同じように動作するという保証がないためです。

未定義の動作ですか?もしそうなら、この問題への正しいアプローチは何でしょうか?

あなたのサンプルでは、​​はい、未定義の動作です。提供する例はせいぜい学術的な例であるため、正しいアプローチは、コードが実際に何をすべきかによって異なります。

明確にするために、関数に対する次の変更には明確にmain()定義された動作があり、C++ 標準によって明示的に許可されています。

B objectB;
A* ptrA = &objectB;

B* b = static_cast<B*>(ptrA);
b->Show();

ここでは、ポインター自体の型が であっても、ポインターptrAが実際には のインスタンスを指しているため、適切に定義されています。上記の例では、 から にキャストしてから、キャストされたポインターで の関数の1 つを呼び出します。違いは、質問の例では、実際には のインスタンスを指していないことです。BA*A*B*BbB


関連する条項(強調鉱山):

C++ 標準 5.2.9/8 静的キャスト [expr.static.cast]

タイプ「cv1 Bへのポインター」の右辺値 (Bはクラス タイプ) は、タイプ「cv2 Dへのポインター」D( は から派生したクラス (第 10 節))の右辺値に変換できBます。D」から「へのポインターB」が存在し(4.10)、cv2は cv1 と同じ cv修飾であるか、 cv1より大きいcv修飾であり、 の仮想基底クラスではありません。null ポインター値 (4.10) は、変換先の型の null ポインター値に変換されます。タイプ「cv1 へのポインター」の右辺値がBDBBこれは実際には type のオブジェクトのサブオブジェクトでありD、結果のポインターは type の外側のオブジェクトを指しますD。それ以外の場合、キャストの結果は未定義です。

于 2013-02-16T20:54:24.323 に答える
2

実際に派生インスタンスを指している基本オブジェクトへのポインターを、派生オブジェクトへのポインターにキャストできます。

ただし、コードでは、が指すオブジェクト&aは派生オブジェクトではなく基本オブジェクトであり、実際に行っていることは未定義の動作です。

実装では、クラスに仮想関数またはベースがなく、派生オブジェクトがデータメンバーを追加せず、メソッドのみを追加する場合、「機能する」はずです。それでも、正式に動作が保証されているわけではありません。

それをしないでください。

于 2013-02-16T20:54:54.517 に答える
2

指しているオブジェクトが実際に である場合、キャストは許可されます (したがって機能します) B。あなたの例では、それはプレーンAであるため、未定義の動作が発生します。

なぜそれが機能するのですか?呼び出したメソッドがオブジェクトにアクセスしていないため、機能します(または機能しているように見えます)。いくつかのメンバー変数を追加Bしてそれらにアクセスしようとしたり、仮想関数Show()を作成したりすると、失敗する可能性が高くなります。ShowでもとにかくUBなので、基本的になんでもありです。

于 2013-02-16T20:56:50.083 に答える