2

派生およびベースptrsにアクセスするコードがいくつかあり、アドレスを出力すると、それらは同じです。この場合、コンパイラは、参照しているアドレスが A か B かをどのように判断しますか?

コードは次のとおりです。

#include <iostream>

class A
{
public:
   A() : val_(0) {
       ptrA = this;
   }

   virtual void set(int val) { val_ = val; }

   virtual void printval() = 0;

   static A* ptrA;

   int val_;
};

class B : public A
{
public:
   B() : A() {
      ptrB = this;
   }

   virtual void printval() { std::cout << A::val_ << std::endl; }

   static B* ptrB;
};

A* A::ptrA = 0;
B* B::ptrB = 0;

int main() {

   A* p = new B();
   p->set(3);
   p->printval();

   std::cout << "A part address=" << A::ptrA << std::endl;
   std::cout << "B part address=" << B::ptrB << std::endl;

   return 0;
}

印刷物は次のとおりです。

A part address=00501F40
B part address=00501F40

どちらのアドレスもまったく同じです。コンパイラは追加情報も保存しますか?

編集:はい、プログラムが実行時にどのように知っているかを言うつもりでした。

4

2 に答える 2

2

実装に依存するメカニズムを介して組み込まれた追加情報。プログラムをコンパイルすると、コンパイラはブックキーピングに必要なすべてのコードを静かに追加します。
ほとんどすべてのコンパイラで、動的ディスパッチは仮想テーブルとポインタを介して実装されます。

食べ物を読む:

仮想関数を呼び出すと、ハードウェアで何が起こりますか? 間接的な層は何層ありますか? オーバーヘッドはどのくらいありますか?

于 2013-03-28T18:28:45.367 に答える
0

実際、あなたのような「通常の」C 実装を想定すると、クラス B のデータの多くは構造体であり、その構造体の最初のメンバーはクラス A のオブジェクトです。B とそれに含まれる A の両方が同じ場所、同じ住所です。

B の先頭に A オブジェクトがあるため、B オブジェクトへのポインターを A オブジェクトへのポインターに変換できます。これは、A データへのポインターであるため、A オブジェクトのように動作します。

仮想関数はより複雑です。A データの内部には、通常は表示されませんが、テーブルへのポインターがあります。オブジェクトが実際に単純な A オブジェクトである場合、そのポインターは A の仮想関数のアドレスを持つテーブルを指します。オブジェクトが B オブジェクトの場合、そのポインターは仮想関数のアドレスを持つテーブルを指します。その結果、コンパイル時の型が A へのポインターのように見えるポインターがある場合、コンパイラーはテーブル内のアドレスを検索してその関数を呼び出します。指し示されたオブジェクトの実際のタイプが B である場合、このテーブルは B の仮想関数のアドレスを提供します。

于 2013-03-28T18:32:33.787 に答える