見出し画像

C++ 再入門 その20 - 仮想関数の仕組みと実行時型情報

C言語でも関数ポインタのテーブルをうまく使えば、呼び出す関数をコンパイル時ではなく「実行時」に決めて呼び出すことも可能ですが、普通はコンパイル時に呼び出す関数を決定し、関数のアドレスがコードに埋め込まれます。C++でも普通の関数については、やはり継承されたクラスであってもコンパイル時にどの関数を呼び出すのかについては決まるので、呼び出す関数のアドレスがコードに埋め込まれることに違いはありません。

しかしながら virtual の付いた仮想関数(から継承された関数)については、クラス毎に仮想関数から継承されたクラスに対する関数ポインタのアドレスを並べた配列を用意して、インスタンスのthisポインタを経由して「実行時に」呼び出す関数を決める仕組みを使います。

仮想関数テーブル

仮想関数は親クラスの参照であっても、そのインスタンスは継承したクラスのものである場合もあります。ですから参照にはインスタンスの仮想関数テーブルへのポインタも持っていて、仮想関数から継承された関数(メソッド)を呼び出すときには、そのテーブルから実際に呼び出す関数を決定します。このような仕組みで同じクラスへの参照であってもインスタンスごとに異なる関数を呼び出せるので、いわゆるポリモーフィズムを実現しているのです。

第26回目 オジブェクト指向の3大特長の3つ目「動的ポリモーフィズム」

実装の仕組みとしては仮想関数の名前をクラスごとの番号(テーブルの要素番号)に変換して、何番目の関数を呼ぶという形に直しているのです。C言語で関数ポインタの配列を使うコードを書いたことがあれば、おおよそそのやり方は同じです。デバッガなどを使うと、その動作が見えることもありますが、普通に使う分には参照で使うメモリが何だか大きいなであるとか、ほんの僅かですが関数呼び出しに時間がかかるのかなという程度しかわかりません。

C++ の仮想関数と VTable について

vtableの中身を見てみる

V-Table(VF-Table)

参照に値を代入するたびにインスタンスの型は変わり得るので、このようなテーブルを用意して実行時に呼び出す関数を決めるしかポリモーフィズムを実装するのは出来ません。

仮想関数は こう呼び出される vtableの秘密 [C++]

まあ仕組み自身を知らなくても仮想関数は使えるのですが、動的(実行時)に呼び出す関数を決めるということは、インスタンスにその型に関する情報を持たせることが必要になるわけです。逆にいうと、この情報を活用することで仮想関数の呼び出し以外にも役に立つことが出てきます。

実行時型情報

例えば type_id 演算子を使い type_info クラスを記述して演算子に関する情報を取得したり、親クラスから子クラスへの型変換を安全に使うことが出来るようになります。

第 6 章 実行時の型識別

ランタイム型情報

実行時型情報

何だか小難しいことが書いてありますが、実行時にならないと実際の動作が決まらない以上、その動作について確認する方法があるんだなということです。つまりロジックとしてはコードに書いてあっても、インスタンスによっては、その関数を呼び出すことが出来なかったりすることはあるので、事前にチェックしたり、そこで発生した例外をハンドルする必要が出てくるわけですね。

これを活用するものとして dynamic_cast 演算子を使って安全に型変換を行う方法があるのですが、この辺りでキャスト(型変換)について整理しておこうと思いますが、それは長くなるので次回にします。

ヘッダ画像は、以下のものを使わせていただきました。https://commons.wikimedia.org/wiki/File:ISO_C%2B%2B_Logo.svg
Jeremy Kratz - https://github.com/isocpp/logos , パブリック・ドメイン,
https://commons.wikimedia.org/w/index.php?curid=62851110による

#プログラミング #プログラミング言語 #プログラミング講座 #CPP #仮想関数 #VTABLE #実行時型情報 #RTTI

いいなと思ったら応援しよう!

kzn
頂いたチップは記事を書くための資料を揃えるために使わせていただきます!