通过指向派生类的函数调用基本虚方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过指向派生类的函数调用基本虚方法相关的知识,希望对你有一定的参考价值。

我需要通过指针从派生类调用基本方法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”。是否可以通过指向方法的方法调用A :: 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()来调用基本方法。

因此,如果要通过指针调用基本方法,则应将其设置为非虚拟(如注释中提到的@PasserBy)。

代码示例:

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()

以上是关于通过指向派生类的函数调用基本虚方法的主要内容,如果未能解决你的问题,请参考以下文章

虚函数和基类中的this指针的问题!

虚函数的底层实现机制虚函数内存布局以及虚函数局限

关于C++基类、派生类的引用和指针

c++ 虚函数 如何调父类 而非子类

c++,虚函数的作用,承接上一篇随笔

基类与派生类的指针和成员函数调用原理