【问题标题】:Cannot call a member function pointer on a object typecasted as void *无法在类型转换为 void * 的对象上调用成员函数指针
【发布时间】:2015-07-17 13:12:46
【问题描述】:

所以我想使用成员函数指针的真正原因是从对象类型未知且所有对象都被视为 void * 对象的地方调用函数。但我无法这样做,即使从理性上看,函数指针似乎指向子例程的地址,因此对象的类型应该是无关紧要的。我的假设是系统所要做的就是将“this”指针设置为调用成员函数指针的对象。下面是我的代码。我怎样才能达到我想要的目标

class C2
{
    public:
       int y;
       void SetValue(int);
};

void C2::SetValue(int k)
{
    cout<<"Setting Value inside C2"<<endl;
    y=k;
}

int main()
{
    void (C2::*f)(int);
    C2 *Ob=new C2();
    void *Ob2=(void *)Ob;
    f = &C2::SetValue;
    ((Ob2)->*f)(10);
}

这段代码甚至没有用 VC++ 编译器编译,错误为

错误 C2296: '->*' : 非法,左操作数的类型为 'void *'

【问题讨论】:

  • 如何在不知道成员所属类型的情况下拥有成员函数指针?在您的示例中,f 的类型为 void (C2::*)(int),因此您知道必须在 C2 的实例上调用它。
  • 您想要从void 到您的类型的隐式转换?!
  • 但是我已经将函数指针设置为指向特定类的成员函数,即类 C2。所以期望是不管调用函数指针的对象是什么,函数都应该被执行
  • 听起来您可能想要使用继承和虚函数。 void * 不应该是现代 C++ 中常见的东西。
  • @Raks "不管调用函数指针的对象是什么,函数都应该被执行" => 这违背了 C++ 类型系统。 C++ 是强类型和静态类型的:如果你有一个函数需要一个 int 作为参数,你只能用一个 int 来调用它。成员函数指针也是如此,编译器会阻止您使用 void * 调用成员函数,因为这不是类型安全的(您的对象可能不是 C2),因此您必须明确“绕过”该类型系统使用演员表:(static_cast&lt;C2&gt;(Ob2)-&gt;*f)(10);

标签: c++ pointers function-pointers


【解决方案1】:

解决问题的简单方法是将static_cast 你的Ob2 设置为正确的类型,因为无论如何你都需要知道函数所属的类:

(static_cast<C2*>(Ob2)->*f)(10);

您可以使用模板自动执行此操作:

template <typename Ret, class C, typename... Params, typename... Args>
Ret callMemPtr (Ret (C::*fun) (Params...), void* obj, Args&&... args)
{
    return (static_cast<C*>(obj)->*fun)(std::forward<Args>(args)...);   
}

//usage
callMemPtr(f,Ob2,10);

但是,这似乎是一种奇怪的方法。使用继承或某种形式的类型擦除可能会更好。

【讨论】:

    【解决方案2】:

    从理论上讲,函数指针似乎指向子程序的地址,因此对象的类型应该是无关紧要的。

    虽然对于指向独立函数的指针和指向static 成员函数的指针肯定是这样,但指向实例成员函数的指针是不同的。指向成员函数的指针可能是数据结构,而不是单个指针,因为它需要支持虚拟调度。

    考虑这个代码片段:

    #include <iostream>
    struct Base {
        virtual void foo() = 0;
    };
    struct Derived : public Base {
        virtual void foo() {
            std::cout << "Hello" << std::endl;
        }
    };
    typedef void (Base::*MemFn)();
    int main() {
        Derived d;
        MemFn f(&Base::foo);
        (d.*f)();
        return 0;
    }
    

    在上面,f 是指向 Base::foo 的指针,但 Derived::foo 被调用 (demo)。

    这就是不能将函数指针转换为void* 的原因之一。 void指针是指向数据的指针,而函数指针是指向代码的指针。

    【讨论】:

    • 那么当我们说 f=&C::SetValue 并且 f 声明为 void (C2::*f)(int); 时,变量“f”实际上指向的是什么?
    • @Raks 成员函数指针的实际结构是特定于编译器的。编译器决定它需要在那里存储什么,以便能够弄清楚要调用什么。例如,如果虚拟调度是使用 vtables 实现的,那么这个结构可以包含一个偏移量到 vtable 中,其中存储了实际的指针。
    • 但是如果函数指针不是虚函数,那么甚至不需要虚拟表查找。我认为重要的是设置“this”指针指向调用函数的对象。
    • @Raks 这里MemFn f(&amp;Base::foo); 编译器知道函数是virtual。但是,这里(d.*f)(); 不知道f 是不是virtual,它必须准备好处理这两种可能性。
    • @Raks 因为设置指针的位置和使用指针的位置可能在不同的翻译单元中。从设置的那一刻起,指针必须是自给自足的。在调用点,编译器不知道指针中设置了什么特定函数(否则,指针将无用)。不过,它需要有完整的信息才能进行调用。
    【解决方案3】:

    我不知道你为什么想要这个,但你可以去 yolo 并这样做:

    #include <iostream>
    
    class test
    {
    public:
        test(){}
        void echo(int x)
        {
            std::cout << "I'm being called: " << x << std::endl;
        }
    };
    
    int main()
    {
        test t;
        (t.*((void(test::*)(int))&test::echo))(10);
    
        std::cin.get();
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-06
      • 1970-01-01
      • 2013-03-10
      • 2019-03-28
      相关资源
      最近更新 更多