2

class A基本クラスとして がありclass Bます。

以下で説明するように、仮想関数内で非仮想関数を呼び出しました。abc()xyz()

ランタイム ポリモーフィズムにより、B:xyzが呼び出されます – これは理解しています。

ただし、非仮想関数のように、なぜその後にB:abcand notが続くのかわかりません。A:abcabc

注意: 次の質問に出くわしました:非仮想関数を呼び出す仮想関数。仮想関数内での呼び出しabc()は と同等this->abc()であり、したがって出力であることが言及されています。ただし、この部分を理解しているかどうかはわかりません。

反対のことを行うと(つまり、仮想関数を呼び出す非仮想関数)、その時間に正確なランタイムポリモーフィズムが表示されるためです。その場合、this ポインターはどうなりますか?

//Virtual function calling non-virtual 
class A
{
  public:
  void abc()
  {
    cout<<"A:abc"<<endl;
  }

  virtual void xyz()
  {
    cout<<"A:xyz"<<endl;
    abc();
  }
};


class B: public A
{
  public:
  void abc()
  {
    cout<<"B:abc"<<endl;
  }

  void xyz()
  {
    cout<<"B:xyz"<<endl;
    abc();
  }
};

int main() {

  A *obj3 = new B;
  obj3->xyz();\
  return 0;
}
Output
B:xyz
B:abc
//Non-virtual calling virtual function
#include <iostream>
using namespace std;

class A
{
  public:

  void abc()
  {
    cout<<"A:abc"<<endl;
    xyz();
  }

  virtual void xyz()
  {
    cout<<"A:xyz"<<endl;
  }
};

class B: public A
{
  public:

  void abc()
  {
    cout<<"B:abc"<<endl;
    xyz();
  }

  void xyz()
  {
    cout<<"B:xyz"<<endl;
  }
};

int main() {

  A *obj3 = new B;
  obj3->abc(); 
  return 0;
}
Output
A:abc
B:xyz
4

1 に答える 1

4

非仮想 関数の呼び出しはabc、コンパイル時に効果的に解決されます。そのため、の別のメンバー関数class Bから呼び出されると、関数のバージョンが呼び出され、元のオブジェクトへのclass Bポインター ( ) が渡されます。this呼び出されています。同様に、関数内から呼び出された場合、定義が使用されます。つまり、コンパイラにとって、非仮想関数は、クラスの特定のインスタンスではなく、クラスに関連付けられます。class Aclass A

ただし、仮想 xyz関数はコンパイラによって異なる方法で処理されます。この場合、関数への参照またはポインタがクラス定義に追加されます (詳細は実装固有ですが、これは一般にvtableと呼ばれるものに追加されます)。クラスのオブジェクトが作成されると、その関数ポインターおよび/または vtable のコピーが含まれます。コンパイラは、そのような仮想関数を呼び出すコードを見つけると、それを適切な関数ポインタを介した呼び出しに変換します。したがって、関数は実際の「一緒に移動」しますオブジェクト: 関数が (コード内の) 派生クラスまたは基本クラスから呼び出されるかどうかは関係ありません。呼び出される関数は、呼び出されたオブジェクト (インスタンス) に属する関数です。

要約すると、非仮想関数の呼び出しはコンパイル時に解決されますが、仮想関数の呼び出しは (概念的に)実行時に解決されます。

この「vtable」の作成を実際に確認するには、次のコードをコンパイルして実行してみてください。

#include <iostream>

class A {
public:
    int i;
    ~A() = default;
    void foo() { std::cout << i << std::endl; }
};

class B {
public:
    int i;
    virtual ~B() = default;
    virtual void foo() { std::cout << i << std::endl; }
};

int main()
{
    std::cout << sizeof(A) << std::endl;
    std::cout << sizeof(B) << std::endl;
    return 0;
}

2 つのクラスの唯一の違いは、一方が仮想関数を持ち、もう一方が持たないことです。ただし、これにより、クラス オブジェクトのサイズに大きな違いが生じます: vtable のサイズ (おそらく、最適なアリングメントのための「パディング」を伴う)データの)! (私の 64 ビット Windows では、MSVC を使用して、4 と 16 のサイズを取得しますが、実際の値はコンパイラとプラットフォームによって異なります。)

于 2021-04-12T21:11:31.410 に答える