-->

C++: Accessing Virtual Methods

2019-02-24 11:33发布

问题:

I'm trying to use the virtual method table to call functions by index in a class... Suppose we have the following code:

class Base
{
public:
    Base() {}
    virtual ~Base() {}

    virtual Base* call_func(unsigned int func_number)
    {
       // Some way to call f_n
    }
protected:
    virtual Base* f_1() const = 0;
    virtual Base* f_2() const = 0;
    virtual Base* f_3() const = 0;
};

I've already implemented this using function arrays, if-statement and case-statement... so, Is there a a better approach to call methods using just pointers (accessing to the vtable for example) or something like that?

Sorry for my horrible English :S... and thanks in advance!

EDIT: Thanks for all the suggestion! I'm going to expand my question:

After resolve this i'm going to create derived classes (for example derived1 and derived 2) with different implementations of f_1, f_2, f_3 and have a class control like this:

class Control
{
protected:
    Base* current;

public:
    Control(Base* curr = new derived1): current(curr) {}
    virtual ~Control() 
    {
        delete current;
    }
    virtual void do_something(unsigned int func_numb)
    {
        delete current
        Base* new = current->next_state(stl);
        current = new;
    }
};

回答1:

Either a switch statement:

switch (func_number)
{
    case 1:
        f_1();
        break;
    case 2:
        f_2();
        break;
    case 3:
        f_3();
        break;
}

Or use an array of function pointers.



回答2:

There is no portable way to access the virtual function table; the language specification doesn't specify how virtual dispatch should be implemented, so there is no requirement for the table even to exist, let alone be accessible to the program.

There is no significantly better way to do what you want than the approaches you mention: a table of function pointers, or an if/switch condition.



回答3:

I assume you just want to find all possible ways to solve it.

You can use map (or vector) of pointers to member functions and initialize them once (in constructor or statically). That can emulate the vtable.

Something between these lines:

class Base
{
public:
    Base() {
        functions.insert(std::make_pair(1,&Base::f_1));
        functions.insert(std::make_pair(2,&Base::f_2));
        functions.insert(std::make_pair(3,&Base::f_3));
        }
    virtual ~Base() {}
    virtual Base* call_func(unsigned int func_number)
    {
    return (this->*functions[func_number])();
}
protected:
    std::map<unsigned int, Base*(Base:: *)()const> functions;
virtual Base* f_1() const = 0;
virtual Base* f_2() const = 0;
virtual Base* f_3() const = 0;

};

This should work even for inherited classes (I would make call_func non-virtual, though). And yes, you should check if the item really is in the map (or vector) and if it's not a nullptr.



回答4:

Note1: It is safer to access you're methods using method-mapping or a switch-case method than using the vtable pointer.

Note2: The small assembly part works on VC++, not sure for other compilers.

Although there exists a way to access the virtual table functions:

// create our object
X *obj = new X();

// access the vtable pointer
int* vptr = *(int**)obj;

// set the this pointer
__asm
{
  mov ecx, obj
}

// call the first method from the vtable
( (void (*)()) vptr[0] )();

See for a deep explanation here: http://kaisar-haque.blogspot.nl/2008/07/c-accessing-virtual-table.html