【问题标题】:virtual method callbacks in C++11/14/17?C++ 11/14/17 中的虚拟方法回调?
【发布时间】:2019-11-21 07:09:22
【问题描述】:

我有一些订阅函数,当有事情发生时会调用我的回调。 (假设它是一个计时器,当经过一定的毫秒数时会传递给我一个对象。)我想要调用的是一个虚拟方法。我觉得 std::functionstd::bind 或 lambdas 是解决方案的一部分。

到目前为止,我使用的 C++99 方法涉及知道如何调用虚方法的单行 C 函数。订阅函数将 C 函数和一个 void* 用户数据作为参数。例如:

class Foo {
  virtual void OnTimerA( Data* pd );
};

void OnTimerACB( Data* pd, void* pvUserData ) {
  ( (Foo*) pvUserData )->OnTimerA( pd );
}

/* Inside some method of Foo; 1000 is a number of milliseconds to call me back in;
   second arg is a function pointer; third is a void* user data that is passed back
   to the C callback. */
SubscribeToTimerOld( 1000, OnTimerACB, this );

我希望的是一种写作方式:

SubscribeToTimerNew( 1000, OnTimerA );

或类似的东西,至少不需要编写单行 C 绑定回调。

我感觉SubscribeToTimerNew() 的参数可能是某种std:function,而不仅仅是写OnTimerA,我必须用std::bind 写一些东西来获取this 指针在那里。

除了bind,也许 lambda 是一种方法?这可以编译,但我不知道如何扩展它以让事件处理程序将参数传递给OnTimerA()。 (我的链接器当前不工作,所以不知道它是否按需要链接或运行。)

SubscribeTimer(  1000, [this](){this->OnTimerA();} );

提到一个我已经放弃的替代方案:给Foo 一个超类,其中包含一个名为OnTimer() 的方法,该方法将在计时器关闭时调用。现在SubscribeTimer() 只需要经过一段时间。我不喜欢这样,因为它不允许注册多个计时器。如果是这样,您可以给他们(比如说)整数计时器 ID,并将 OnTimer() 实现为 switch,但这似乎比 C++99 解决方案复杂得多。

在(我假设的)几种方法中,除了涉及多少类型的最明显问题之外,是否有任何权衡(例如,堆使用)? (这是一个高性能应用程序,我希望尽量减少或消除堆使用。)

【问题讨论】:

  • 阅读有关性能比较的信息:stackoverflow.com/questions/14306497/… tldr 引用已接受的答案:“C++11 的 std::function 调用仅增加了 2 条 CPU 指令,因此在我们的示例中为 5 条。作为结论:无论你使用哪种函数指针技术,开销差异都非常小。”

标签: c++11 c++14 c++17 std-function stdbind


【解决方案1】:

C++11、C++14 和 C++17 完全不同,尤其是在涉及 lambda 时。 lambda 是创建回调的好方法。例如,请参阅Why use std::bind over lambdas in C++14?

使用现代 C++,您可以使用 std::function 作为回调类型,然后您可以使用任何可调用的东西作为实际回调。引用https://en.cppreference.com/w/cpp/utility/functional/function:

类模板std::function是一个通用的多态函数 包装。 std::function 的实例可以存储、复制和调用任何 Callable 目标——函数、lambda 表达式、绑定表达式或 其他函数对象,以及指向成员函数的指针和 指向数据成员的指针。

例子:

#include <functional>
#include <iostream>

using Callback = std::function<void(int)>;

void subscribe(Callback callback, int duration) {
    callback(duration);
}

struct Foo {
    void operator()(int duration) {
        std::cout << __PRETTY_FUNCTION__ << ' ' << duration << '\n';
    }
};

struct Bar {
    virtual void myFunction(int duration) {
        std::cout << __PRETTY_FUNCTION__ << ' ' << duration << '\n';
    }
};

void freeFunction(int duration) {
    std::cout << __PRETTY_FUNCTION__ << ' ' << duration << '\n';
}


struct Zorg {
    static void staticFunction(int duration) {
        std::cout << __PRETTY_FUNCTION__ << ' ' << duration << '\n';
    }
};

int main() {
    Foo foo;
    subscribe(foo, 128);

    Bar bar;
    auto lambda = [&bar](int duration) {
        bar.myFunction(duration);
    };
    subscribe(lambda, 256);

    subscribe(freeFunction, 512);

    subscribe(Zorg::staticFunction, 1024);
}

输出:

void Foo::operator()(int) 128

virtual void Bar::myFunction(int) 256

void freeFunction(int) 512

static void Zorg::staticFunction(int) 1024

【讨论】:

  • 您可以通过实现实际订阅来改进您的答案,而不仅仅是在subscribe 中调用传递的std::function。这将表明std::function 可以存储以供以后使用,回调的目的是什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-05
  • 1970-01-01
  • 2015-09-09
  • 2020-05-18
  • 2011-10-14
  • 1970-01-01
  • 2013-03-08
相关资源
最近更新 更多