Eckel の「Thinking in C++」を使って C++ を学んでいます。次のように述べています。
- クラスに仮想メソッドが含まれている場合、そのクラスなどに対して仮想関数テーブルが作成されます。関数テーブルの仕組みを大まかに説明します。(vtable が必須ではないことはわかっていますが、Visual C++ によって作成されます。)
- 呼び出しオブジェクトは、呼び出された関数に引数として渡されます。(これは、Visual C++ (または任意のコンパイラ) には当てはまらない場合があります)。VC++ が呼び出し元オブジェクトを関数に渡す方法を調べようとしています。
Visual C++ で両方のポイントをテストするために、次のクラスを作成しました (Visual Studio 2010、WinXP Home 32 ビットを使用)。
ByteExaminer.h:
#pragma once
class ByteExaminer
{
public:
short b[2];
ByteExaminer(void);
virtual void f() const;
virtual void g() const;
void bruteFG();
};
ByteExaminer.cpp:
#include "StdAfx.h"
#include "ByteExaminer.h"
using namespace std;
ByteExaminer::ByteExaminer(void)
{
b[0] = 25;
b[1] = 26;
}
void ByteExaminer::f(void) const
{
cout << "virtual f(); b[0]: " << hex << b[0] << endl;
}
void ByteExaminer::g(void) const
{
cout << "virtual g(); b[1]: " << hex << b[1] << endl;
}
void ByteExaminer::bruteFG(void)
{
int *mem = reinterpret_cast<int*>(this);
void (*fg[])(ByteExaminer*) = { (void (*)(ByteExaminer*))(*((int *)*mem)), (void (*)(ByteExaminer*))(*((int *)(*mem + 4))) };
fg[0](this);
fg[1](this);
}
動作中の vtable のナビゲーションbruteFG()
- を呼び出すとfg[0](this)
、f()
が呼び出されます。ただし、機能しないthis
のは、関数への の受け渡しです。つまり、this->b[0]
正しく出力されません (代わりにゴミが出てきます。これで segfault が発生しないのは幸運です)。
したがって、実際の出力は
ByteExaminer be;
be.bruteFG();
は:
virtual f(); b[0]: 1307
virtual g(); b[1]: 0
では、正しい結果を得るにはどうすればよいでしょうか。ポインタはVC++の関数にどのようにthis
渡されますか?
(注記: 私はこのような方法で真剣にプログラミングするつもりはありません。これは「気まぐれ」または学習経験のためです。ですから、私を適切な C++ 言語に改宗させようとしないでください :))