7

以下のコードでは、pC == pA:

class A
{
};

class B : public A
{
public:
    int i;
};

class C : public B
{
public:
    char c;
};

int main()
{
    C* pC = new C;
    A* pA = (A*)pC;

    return 0;
}

しかし、純粋仮想関数をBに追加し、それをCに実装すると、pA!= pC:

class A
{
};

class B : public A
{
public:
    int i;
    virtual void Func() = 0;
};

class C : public B
{
public:
    char c;
    void Func() {}
};

int main()
{
    C* pC = new C;
    A* pA = (A*)pC;

    return 0;
}

この場合、なぜpAがpCと等しくないのですか?どちらもまだメモリ内の同じ「C」オブジェクトを指していますか?

4

3 に答える 3

6

新しい仮想関数がオブジェクトへのvtableポインターの注入を引き起こしているため、ポインターに異なる値が表示されています。VC ++は、vtableポインターをオブジェクトの先頭に配置しています(これは一般的ですが、純粋に内部の詳細です)。

説明しやすいように、Aに新しいフィールドを追加しましょう。

class A {
public:
    int a;
};
// other classes unchanged

さて、メモリ内で、あなたpAAは次のようになります。

pA --> | a      |          0x0000004

BとCをミックスに追加すると、次のようになります。

pC --> | vtable |          0x0000000
pA --> | a      |          0x0000004
       | i      |          0x0000008
       | c      |          0x000000C

ご覧のとおり、vtableの後ろpAのデータを指しています。これは、vtableやその使用方法、さらにはそこにあることについても何も知らないためです。 はvtableを認識しているため、テーブルを直接指しているため、使用が簡単になります。pC

于 2012-04-03T22:37:03.867 に答える
3

オブジェクトへのポインタはベースオブジェクトへのポインタに変換可能であり、その逆も可能ですが、変換は簡単である必要はありません。ベースポインタが派生ポインタとは異なるを持つことは完全に可能であり、多くの場合必要です。そのため、強い型のシステムと変換があります。すべてのポインタが同じであれば、どちらも必要ありません。

于 2012-04-03T22:11:32.700 に答える
0

質問に基づいた私の仮定は次のとおりです。

1)CからAにキャストし、期待どおりの動作が得られる場合があります。
2)仮想関数を追加しましたが、そのキャストは機能しなくなりました(Aへのキャストの直後にAからデータをプルできなくなったため、意味のないデータが取得されます)。

これらの仮定が当てはまる場合、発生している問題はBに仮想テーブルを挿入することです。これは、クラスのデータが基本クラスのデータと完全に一致しなくなったことを意味します(クラスがバイトを追加したため、あなたから隠されている仮想テーブル)。楽しいテストは、sizeofをチェックして、未知のバイトの増加を観察することです。

これを解決するには、データを収集するためにAからCに直接キャストしないでください。Aにあり、BとCに継承されるゲッター関数を追加する必要があります。

コメントの更新を考えると、これを読む必要があると思います。仮想テーブルとメモリレイアウト、およびコンパイラに依存する方法について説明しています。そのリンクは、私が上で説明したことをより詳細に説明していますが、異なる値であるポインターの例を示しています。本当に、なぜあなたが間違った質問をしているのかを知りましたが、それでも情報はあなたが望んでいたもののようです。CからAへのキャストでは、この時点で仮想テーブルが考慮されます(C-8は4であり、32ビットシステムでは仮想テーブルに必要なアドレスのサイズになると思います)。

于 2012-04-03T22:13:55.867 に答える