3

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++ 言語に改宗させようとしないでください :))

4

2 に答える 2

4

Visual Studio のメンバー関数には、特殊なレジスタで渡される特殊な呼び出し規約__thiscallがあります。thisどちらかは覚えていませんが、MSDN が教えてくれます。vtable にある関数ポインターを呼び出したい場合は、アセンブラーに降りる必要があります。

もちろん、あなたのコードは非常に未定義の動作を示します.charまたはunsigned charポインターを使用してオブジェクトにエイリアスを付けるだけで問題ありませんint.vtableの仮定全体を無視しても.

于 2011-07-23T18:33:57.963 に答える
0

DeadMGのヒントを使用してOKアセンブラを使用せずに方法を見つけました。

1)fg[]配列の関数からByteExaminer*引数を削除します。2)void callfunc(void (*)());ByteExaminerに関数を追加します。

void ByteExaminer::callfunc(void (*func)())
{
    func();
}

...これは、func()がcallfuncで最初に使用されるため、ecx明らかに機能します。したがって、以前は変更されていないようです。しかし、これは汚いトリックです(上記のコードでわかるように、私は常にクリーンなコードを探しています)。私はまだより良い方法を探しています。

于 2011-07-23T18:48:09.257 に答える