3

我正在使用 Eckel 的“Thinking in C++”学习 C++。它声明如下:

  • 如果一个类包含虚方法,则为该类创建一个虚函数表等。大致解释了函数表的工作原理。(我知道 vtable 不是强制性的,但 Visual C++ 会创建一个。)
  • 调用对象作为参数传递给被调用函数。(这可能不适用于 Visual C++(或任何编译器)。)我试图找出 VC++ 如何将调用对象传递给函数。

为了在 Visual C++ 中测试这两个点,我创建了以下类(使用 Visual Studio 2010,WinXP Home 32bit):

字节检查器.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);
}

The navigation through the vtable in bruteFG() works - when I call fg[0](this), f() is called. What does NOT work, however, is the passing of this to the function - meaning that this->b[0] is not printed correctly (garbage comes out instead. I'm actually lucky this doesn't produce a segfault).

So the actual output for

ByteExaminer be;
be.bruteFG();

is:

virtual f(); b[0]: 1307
virtual g(); b[1]: 0

So how should I proceed to get the correct result? How are the this pointers passed to functions in VC++?

(Nota bene: I'm NOT going to program this way seriously, ever. This is "for the lulz"; or for the learning experience. So don't try to convert me to proper C++ianity :))

4

2 回答 2

4

Member functions in Visual Studio have a special calling convention, __thiscall, where this is passed in a special register. Which one, I don't recall, but MSDN will say. You will have to go down to assembler if you want to call a function pointer which is in a vtable.

Of course, your code exhibits massively undefined behaviour- it's only OK to alias an object using a char or unsigned char pointer, and definitely not an int pointer- even ignoring the whole vtable assumptions thing.

于 2011-07-23T18:33:57.963 回答
0

OK using DeadMG's hint I've found a way without using assembler:

1) Remove the ByteExaminer* arg from the functions in the fg[] array 2) Add a function void callfunc(void (*)()); to ByteExaminer:

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

... this apparently works because func() is the first thing to be used in callfunc, so ecx is apparently not changed before. But this is a dirty trick (as you can see in the code above, I'm always on the hunt for clean code). I'm still looking for better ways.

于 2011-07-23T18:48:09.257 回答