2

以下のコードには 2 つのクラスがあります。タイプ 2 のオブジェクトが 1 つ作成され、クラス 1 のポインタに割り当てられます。

out 関数の呼び出しについては、クラス one の out 関数が呼び出されます。

#include<iostream>
using namespace std;

class one
{
    public :
        void  out()
        {
            cout<<"one ";
        }
};

class two
{
    public : 
        void out()
        {
            cout<<"two ";
        }
};

int main()
{ 
    two dp[3];
    one *bp = (one *)dp;
    for (int i=0; i<3;i++)
    (bp++)->out();
}    

出力

one one one

私によると、出力は1つではなく2つになるはずです。タイプ 2 のオブジェクトを作成したとき、そのオブジェクトのメモリ位置にはクラス 2 の関数のアドレスが含まれていました。

編集-さらに、クラス2の関数の名前を変更しても、出力は変更されません。

4

3 に答える 3

4

初心者が、すべてのC++ メンバー関数がオブジェクトに「属している」と想定することは珍しくありません。
お気づきのように、そうではありません。

概念的に - 正確な手順はコンパイラの実装の詳細です -outメンバー関数は、次のような「フリー」関数に変換されます。

void one_out(one* this) { cout << "one"; }
void two_out(two* this) { cout << "two"; }

非メンバー関数の場合、必要なのはそれだけです。

コンパイラが見たとき

  (bp++)->out();

bp がへのポインタであることを知っoneている (あなたが嘘をついたことを知らない) ので、

one_out(bp++);

それはコンパイラが行うことだからです。

于 2012-06-19T17:49:50.023 に答える
1

あなたのマシンの出力は「1 1 1」だったかもしれませんが、爆発したり、アイスクリームを手に入れたり、ミサイルを発射したりするのと同じくらい簡単だったかもしれません。あなたのコードは未定義の動作を呼び起こします。

class one
/*...*/

class two
/*...*/

onetwoはまったく無関係なクラスであることに注意してください。から派生twoしていないoneか、その逆です。それらはまったく異なるタイプです。

このため...

two dp[3];
one *bp = (one *)dp;
for (int i=0; i<3;i++)
    (bp++)->out();

このコードは、未定義の動作を引き起こします*。 bpタイプ のオブジェクトを指すのではなく、 タイプ のoneオブジェクトを指しますtwo。上記のコードでは、この方法でポインターをキャストすることはできません。

one(* 注: 未定義の動作は、オブジェクトが実際には であるときにメソッドを呼び出そうとしたときに発生しましたtwo。キャスト自体は未定義の動作を引き起こしません。)

あなたが使用しているこのキャスト構文(one *)dpは、C スタイルのキャストであり、この例ではreinterpret_cast<one*>(bp);. reinterpret_cast自己文書化コードを書く以外の理由がなければ、それが本当にやりたいことである場合に使用するのが最善です。

one*本当にからを取得しようとしているtwo*場合は、2 つのオプションがあります。

  1. UB を呼び出さずにキャストできるように継承階層を作成する
  2. oneaから atwoまたはその逆を構築できるように、変換演算子を作成します。

あなたの場合、oneオブジェクトの配列をループし、twoそれらのポインターを介してメソッドを実行しようとしているため、おそらく上記の #1 が最善の策です。

class one
{
    public :
        virtual void  out()
        {
            cout<<"one ";
        }
};

class two : public one
{
    public : 
        void out()
        {
            cout<<"two ";
        }
};

これで、ループが機能し、実際に見たランダムな動作ではなく、コードが「two two two」を出力します。

于 2012-06-19T17:50:05.293 に答える
-3

out()メソッドは宣言されていないためvirtual、オブジェクトのランタイム型ではなく、オブジェクトの静的型でディスパッチされます。

また、2 つのクラス間にサブタイプの関係がないため、この方法でポインターを割り当てるのは正しくありません。

于 2012-06-19T16:38:33.207 に答える