C++ 函数指针参数和类继承自动转换

Posted

技术标签:

【中文标题】C++ 函数指针参数和类继承自动转换【英文标题】:C++ Function pointer arguments and classes inheritance automatic cast 【发布时间】:2019-12-13 10:32:51 【问题描述】:

首先抱歉,如果问题的名称不够清楚。我真的不知道如何称呼这个问题。

所以我在一个类中有一个函数指针,它像一个 java 回调一样工作,我使用一些参数调用它,比如它本身是从父类派生的,就像在这个例子中一样:

class Parent;

using f_Call = void(*)(Parent*);

class Parent

public:
    void setCallback(f_Call call)
    
        mOnCall = call;
    

protected:
    f_Call mOnCall = nullptr;
;

class Child1 : Parent

public:
    void doSomething()
    
        // some work..
        if (mOnCall)
            mOnCall(this);
    
;

void onCallExe(Parent* p)

    Child1* child = (Child1*)p;
    // do some more work...


int main()

    Child1 child;
    child.setCallback(onCallExe);
    child.doSomething();

我的问题是,c++ 是否有办法在 onCallExe 中自动进行从父级到子级的强制转换,所以我不必为我调用的每个函数都这样做。

谢谢!

【问题讨论】:

如果你总是想在回调中使用Child*,为什么不相应地更改签名呢? 嗨@dave,我的项目中有 16-18 个子类,它们最终会执行该函数。 在这种情况下,我认为没有自动的方法可以做到这一点。 (编译器如何知道要为任何给定的回调函数转换到哪个子类?)在我看来,您可能希望在 Parent 类中添加更多虚函数,因此您根本不需要转换。 听到这个消息很难过。无论如何,感谢您的帮助! 您不想使用函数指针。你想要std::function<void()>(是的,没有参数)并且你想要传递一个带有捕获的对象的lambda。 【参考方案1】:

不要使用函数指针。相反,您想要std::function<void()>(是的,没有参数)并传递一个带有捕获对象的lambda。

using f_Call = std::function<void()>;

class Parent  
public:
      void setCallback(f_Call call) 
           mOnCall = call;
      
      protected:
          f_Call mOnCall;
;

class Child1 : public Parent 
public:
      void doSomething() 
           // some work..
           if (mOnCall)
               mOnCall(); // no argument!
       
;

int main()  
    Child1 child;
    child.setCallback([&child]() /* do whatever with the child */ );
    child.doSomething();

如果需要,您可以在函数模板中隐藏 lambda 的创建。

template <class Obj, class CB>
void setCallback (Obj& obj, CB cb) 
     obj.setCallback([&obj]()cb(obj););

然后将任何带有 Child 参数的旧函数传递给全局 setCallback 模板。

void onCallExe(Child1& child)  
    // do some more work...


Child1 child;
setCallback(child, onCallExe);

【讨论】:

【参考方案2】:

Curiously recurring template pattern 可能是一个选项。但是,您将为每个派生类创建单独的基类,因此您不能多态地使用它们——除非您提供一个单独的公共基类。如果循环模板函数覆盖了基础中的一个虚拟函数,您可能最终会到达您想要获取的位置:

struct Base

    virtual ~Base()  
    virtual void f() = 0;
;

template <typename T>
struct Intermediate : Base

    void f() override
    
        if(callback)
            callback(static_cast<T*>(this));
    
    void setCallback(void(*c)(T*))
    
        callback = c;
    

private:
    void(*callback)(T*) = nullptr;
;

struct Derived : Intermediate<Derived>

;

void g(Derived*)  

void demo()

    Derived d;
    d.setCallback(g);
    d.f();

(如果你不需要多态,你可以跳过Base 类——那么f 也不必是虚拟的。)

如果你想通过指针或引用 Base 设置回调,你会有点麻烦,因为你不能有虚拟模板成员函数。您可以提供一个独立的辅助函数:

template <typename T>
void setCallback(Base& b, void(*callback)(T*))

    dynamic_cast<Intermediate<T>&>(b).setCallback(callback);

如果b 的类型不合适,动态转换将抛出std::bad_cast - 不幸的是,这是一个相当昂贵的运行时事情,是让编译器确定指向/引用对象是否是正确类型的安全方法(通常)不可能。

【讨论】:

以上是关于C++ 函数指针参数和类继承自动转换的主要内容,如果未能解决你的问题,请参考以下文章

C++ 将指针转换为指针 (x**) -> (y**)

C++ this 指针,函数调用中的隐藏参数

可与仅派生方法一起使用的 C++ 继承函数指针

基于C++代码的UE4学习—— 带一个参数的FParamDelegateSignature动态代理与函数指针

将c ++引用的地址传递给指针

C++:将派生指针转换为 void 指针,然后转换为抽象指针,并访问成员函数