c++で仮想関数テーブルを扱う


概要

初めまして、初投稿となります。
普段は仕事でゲームプログラマをしており、家では趣味でゲームエンジン(ライブラリ)を制作したりしています。
今まで面倒臭がって発信などあまりしていませんでしたが、自身のスキルアップのためにもこれからは色々投稿していこうと思います。
大体はゲームに関する記事になると思います。

はてなブログもやっているのですがそちらにも同じような記事を投稿をするかもしれません。

あまり文章を書かない人生を送ってきたので拙いものになると思いますがよろしくお願いします。

1.仮想関数テーブルとは
2.仮想関数テーブルを取得してみる
3.取得したところで何に使うのか
4.あとがき

1.仮想関数テーブルとは

仮想関数テーブルとは↓
https://ja.wikipedia.org/wiki/%E4%BB%AE%E6%83%B3%E9%96%A2%E6%95%B0%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB

ポリモーフィズムを実現するために作られた関数のアドレスを持つ配列のことです
メンバ関数にvirtual修飾子を付けるとテーブルに関数アドレスが追加され、呼び出す際にそれを参照することで実現しています。

2.仮想関数テーブルを取得してみる

(私の環境はwin/msvcなので異なる環境だと上手くいかないかもしれません)

main.cpp
class Base
{
public:
virtual void Func1(){}
virtual void Func2(){}
};

class Child : public Base
{
public:
virtual void Func2()override{}
};

int main()
{
Child* object = new Child();
const void* const* vTable = *reinterpret_cast<const void** const*>(object );

return 0;
}

vTableの中身

vTable[0]にはBase::Func1のアドレス
1にはChild::Func2へのアドレス
2は末端なのでnullアドレス
が格納されています
デストラクタも継承していれば同じように格納されます。

3.取得したところで何に使うのか

仮想関数テーブルを取得することで関数を継承しているかどうかや、unityのSendMessgeみたいな文字列で関数を呼び出したりすることもできます。(仮想関数しか呼び出せませんが)

後者のメリットは個人的にあまり感じませんが、前者の継承しているかどうか知れることはかなりメリットだと思います。

例えばunityのbehaviorクラスでは仮想関数としてstart, update, lateUpdateなどが定義されています。中身は空なのでサブクラスが継承しない限り呼び出す必要がありません。
startはまだいいですが、updateなど毎フレーム呼ばれる関数はオーバーヘッドがかかり、継承していない場合中身がないのに呼び出すのは無駄です。

ちなみに私が作成しているエンジンではBehavior登録時に継承関係をチェックし、継承していない関数は呼び出さないようにしています。

継承関係はテーブルの関数アドレスが基底クラスの関数アドレスを指しているかどうかで知ることができます。

実際やるにはまだ必要な事がありますが今回はここまでにしておこうと思います。
また機会があれば書きます。

4.あとがき

初投稿にしてあまり需要があるのかどうかよくわからないものになりました
というかゲームと関係ないですね
でも付けるタグがないので付けます

ゲームエンジンを作成している同志の方がいれば参考にしていただけたら幸いです

自分はあまり知識があるほうではないですが、これから頑張っていきたいと思います

それでは!