1. ホーム
  2. c++

[解決済み] 仮想関数とvtableはどのように実装されているのですか?

2022-08-16 18:11:51

質問

C++で仮想関数がどのようなものかは知っていますが、深いところではどのように実装されているのでしょうか?

vtableは実行時に変更したり、直接アクセスすることもできるのでしょうか?

vtableは全てのクラスに対して存在するのか、それとも少なくとも1つの仮想関数を持つクラスに対してのみ存在するのか?

抽象クラスは単に少なくとも1つのエントリの関数ポインタにNULLを持つだけなのでしょうか?

単一の仮想関数を持つことは、クラス全体を遅くするのでしょうか?それとも仮想関数への呼び出しだけが遅くなるのでしょうか? また、仮想関数が実際に上書きされるかどうかで速度に影響が出るのでしょうか、それとも仮想である限りは影響がないのでしょうか。

どのように解決するのですか?

仮想関数はどのように深いレベルで実装されているのですか?

から "C++における仮想関数" :

プログラムに仮想関数が宣言されると、そのクラスに対して v - テーブルが作成されます。v - テーブルは、1つ以上の仮想関数を含むクラスの仮想関数へのアドレスで構成される。仮想関数を含むクラスのオブジェクトは、メモリ上の仮想テーブルのベースアドレ スを指す仮想ポインタを含んでいます。仮想関数の呼び出しがあるたびに、Vテーブルが関数アドレスへの解決に使用されます。1つまたは複数の仮想関数を含むクラスのオブジェクトは、メモリ内のオブジェクトの一番最初にvptrと呼ばれる仮想ポインタを含んでいます。したがって、この場合のオブジェクトの大きさは、ポインタの大きさだけ増加します。このvptrは、メモリ上の仮想テーブルのベースアドレスを含んでいます。つまり、1つのクラスには、それが含む仮想関数の数に関係なく、1つの仮想テーブルしか存在しないことに注意してください。この仮想テーブルには、そのクラスの1つまたは複数の仮想関数のベースアドレスが格納されます。オブジェクト上で仮想関数が呼び出されると、そのオブジェクトのvptrは、メモリ上のそのクラスの仮想テーブルのベースアドレスを提供します。このテーブルには、そのクラスのすべての仮想関数のアドレスが含まれているため、関数呼び出しを解決するために使用されます。これが、仮想関数呼び出しの際に動的バインディングが解決される方法です。

vtableを変更したり、実行時に直接アクセスすることは可能ですか?

一般的に、答えは「ノー」です。vtable を見つけるために何らかのメモリ操作を行うことはできますが、それを呼び出すための関数シグネチャがどのようなものであるかは、まだわかりません。この機能で実現したいこと(言語がサポートしていること)は、vtableに直接アクセスしたり、実行時に変更したりすることなく実現できるはずです。また、C++の言語仕様では しかし、ほとんどのコンパイラはこの方法で仮想関数を実装しています。

vtableはすべてのオブジェクトに存在するのですか、それとも少なくとも1つの仮想関数を持つオブジェクトにのみ存在するのですか?

I を信じる の答えは、「実装に依存する」です。しかし、実際には、最近のすべてのコンパイラは、クラスが少なくとも1つの仮想関数を持つ場合にのみ vtable を作成すると思います。vtable に関連する空間的なオーバーヘッドと、仮想関数と非仮想関数を呼び出すことに関連する時間的なオーバーヘッドがあります。

抽象クラスは、少なくとも1つのエントリの関数ポインタにNULLを持つだけでよいのでしょうか?

答えは、言語仕様で規定されていないので、実装に依存します。純粋仮想関数を呼び出すと、それが定義されていない場合(通常はされていない)、未定義の動作になります (ISO/IEC 14882:2003 10.4-2)。 実際には、vtableに関数用のスロットは確保されますが、そのアドレスは割り当てられません。このため、派生クラスは関数を実装してvtableを完成させなければなりません。実装によっては、vtableのエントリに単にNULLポインタを置いたり、アサーションと似たようなことをするダミーメソッドへのポインタを置いたりしています。

抽象クラスは純粋仮想関数の実装を定義できますが、その関数は修飾ID構文(つまり、派生クラスから基底クラスのメソッドを呼び出すのと同様に、メソッド名でクラスを完全に指定する)でのみ呼び出すことができることに注意してください。これは、派生クラスがオーバーライドを提供することを要求しながらも、使いやすいデフォルトの実装を提供するために行われます。

単一の仮想関数を持つことでクラス全体が遅くなるのでしょうか、それとも仮想関数への呼び出しだけが遅くなるのでしょうか?

これは私の知識の端にきているので、私が間違っていたら誰か助けてください!

I を信じる

は、クラス内の仮想関数だけが、仮想関数と非仮想関数の呼び出しに関連した時間的なパフォーマンスのヒットを経験すると信じています。クラスの空間オーバヘッドはどちらにしても存在します。vtable がある場合、1 つのクラスにつき 1 つしかないことに注意してください。 クラス につき1つであり オブジェクト .

仮想関数が実際にオーバーライドされると速度に影響が出るのか、仮想関数である限り影響がないのか?

オーバーライドされた仮想関数の実行時間は、ベースとなる仮想関数を呼び出す場合と比較して減少しないと思います。しかし、派生クラスとベース クラスの間で別の vtable を定義することに関連する、クラスのための追加のスペース オーバーヘッドがあります。

追加のリソース。

http://www.codersource.net/published/view/325/virtual_functions_in.aspx (ウェイバックマシン経由)

http://en.wikipedia.org/wiki/Virtual_table

http://www.codesourcery.com/public/cxx-abi/abi.html#vtable