通过指针调用基本虚拟方法从派生类函数

Call base virtual method by pointer to function from derived class

本文关键字:方法 派生 类函数 虚拟 指针 调用      更新时间:2023-10-16

我需要调用来自Pointer derived class的基本方法a :: foo()。

#include <iostream>
struct A{
    virtual void foo() { std::cout << "A::foo()" << std::endl; }
};
struct B:A{
    virtual void foo() { std::cout << "B::foo()" << std::endl; }
    void callBase(void (A::*f)()){
        (this->*f)();
    }
};
int main(){
    B* p=new B();
    p->callBase(&A::foo);
}

此代码输出" B :: foo"。是否可以通过指向方法调用:: foo()?

好吧,您可以使用覆盖this值的一些技巧来做类似的事情。不过,您可能永远不应该尝试这样做,vtable指针并不是要通过手动修改。

要做您描述的事情,我们需要将指针指向A的vtable。我们的对象p只有指向B的vtable,因此我们需要将第二个指针存储在A构造函数中的一个字段中。

这是代码:

#include <iostream>
struct A{
    virtual void foo() { std::cout << "A::foo()" << std::endl; }
    int *a_vtable_ptr;
    // First, save value of A's vtable pointer in a separate variable.
    A() { a_vtable_ptr = *(int**)this; }
};
struct B:A{
    virtual void foo() { std::cout << "B::foo()" << std::endl; }
    void callBase(void (A::*f)()){
        int *my_vtable_ptr = *(int**)this;
        // Then modify vtable pointer of given object to one that corresponds to class A.
        *(int**)this = a_vtable_ptr;
        (this->*f)(); // Call the method as usual.
        // Restore the original vtable pointer.
        *(int**)this = my_vtable_ptr;
    }
};
// Function main() is not modified.
int main(){
    B* p=new B();
    void (A::*f)() = &A::foo;
    p->callBase(f);
}

输出:

A::foo()
Process finished with exit code 0

虚拟方法旨在实现多态性和指示虚拟方法支持其多态性行为。但是您通过明确调用p->A::foo()

来调用基本方法。

因此,如果您想通过指针调用基本方法,则应将其定为非虚拟(如评论中提到的 @tasserby)。

代码示例:

struct A {
    virtual void foo() { std::cout << "A::foo()" << std::endl; }
    void bar() { std::cout << "A::bar()" << std::endl; }
    void callBase(void (A::*f)()) { (this->*f)(); }
};
struct B : A {
    virtual void foo() { std::cout << "B::foo()" << std::endl; }
    void bar() { std::cout << "B::bar()" << std::endl; }
};
int main()
{
    A* p = new B();
    p->foo();
    p->bar();
    p->callBase(&A::foo);
    p->callBase(&A::bar);
    p->A::foo();
    p->A::bar();
}

输出:

B::foo()
A::bar()
B::foo()
A::bar()
A::foo()
A::bar()