从另一个指针指向函数的指针

Pointer to function from another pointer

我是 c++ 的新手,我正在尝试制作一个通用开关(即设备,而不是 C++ 语句),可用于在我的 Arduino 中使灯闪烁、打开和关闭蜂鸣声等项目。

我可以创建一个可切换的界面并在我想要 "switch" 的 class 中实现它。但由于我这样做是出于学习目的,并且我看到了 C++ 中的指向函数的能力(这对我来说是新的,因为我来自 C# 和 Java),我认为这将是一个很好的机会试一试...

问题是我只能在我的代码中传递函数,如果它是本地函数,但如果我尝试传递来自另一个对象(例如 led)的函数,它将不起作用。

一些代码来说明问题。这是 switch.cpp,它在其构造函数中接收 OnOff 函数,并且它有一个在 Arduino ino main class 的循环方法中调用的更新方法:

auto_switch.cpp

using switch_function = void(*)();
auto_switch::auto_switch(const switch_function on_function, const switch_function off_function, const int max_speed_count)
{
    //sets all variables...
}

void auto_switch::update(const unsigned long millis)
{
    //turn switch on and off...
}

这是我的 ino 文件

ino file

#include <Arduino.h>
#include "led.h"
#include "auto_switch.h"

led* main_led;
auto_switch* led_switch;
int slow_speed;

//ugly code
void turn_led_on()
{
    main_led->turn_on();
}

//ugly code
void turn_led_off()
{
    main_led->turn_off();
}

void setup() {
    main_led = new led(2, 3, 4, true, color::white);

    //ugly code
    led_switch = new auto_switch(turn_led_on, turn_led_off, 3);

    slow_speed = led_switch->add_speed(100, 100, 3, 1000);
    led_switch->set_active_speed(slow_speed);
    led_switch->turn_on();
}

void loop() {
    led_switch->update(millis());
}

它有效,但我必须创建一个本地函数(turn_led_onturn_led_off)才能将内部函数作为参数分配给 auto_switch 构造函数,部分我写了 //ugly code

我想做这样的事情,中间没有胶水代码:

//doesn't work
led_switch = new auto_switch(main_led->turn_on, main_led->turn_off, 3);

可能吗?我已经阅读了一些关于函数的静态指针和一些对此有帮助的标准函数,如果我做对了,在这种情况下胶水代码是必要的,这样编译器就可以知道我猜这些函数来自哪里(来自哪个对象), 但由于我需要调用的函数不能是静态的,所以我放弃了这个选项,我认为标准函数不能与 Arduino 一起使用,或者可以但不应该用于性能限制...

无论如何,它是否有意义,可以使用指向函数的指针来完成,还是我应该创建一个接口或其他东西?

在决定如何去做之前,问题是你想做什么以及为什么。因为,使用简单的 C++ 习语也许有更好的选择。

选项 1:多态性专业化

你想专门化你的开关的一些功能,而不是调用 auto_switch 的功能,你会调用更专门的圆顶吗?

在这种情况下你不会这样做:

//doesn't work
led_switch = new auto_switch(main_led->turn_on, main_led->turn_off, 3);

但是您将依赖于基础中具有虚函数的多态性class:

class auto_switch {
...
    virtual void turn_on();  
    virtual void turn_off(); 
...
}; 

并为 LED 写一个专门的 class:

class led_witch : public auto_switch {
...
    void turn_on() override;  
    void turn_off() override; 
...
}; 

其实编译器会在后台生成一些函数指针,不过你不用关心:

auto_switch s1=new auto_switch(...); 
auto_switch s2=new led_switch(...);   // no problem !! 

s1->turn_on();     // calls auto_switch::turn_on() 
s2->turn_on();     // calls led_switch::turn_on() since the real type of s2 is led_switch 

但是如果每个对象的行为在对象的真实 class 的基础上是动态的,则相同 class 的对象共享一个在编译时预定义的行为。如果这不行,请转到下一个选项。

方案二:成员函数指针

另一个对象的功能只能用手边的那个对象调用。因此,拥有一个指向 led 函数的函数指针是不够的:您还需要知道它将应用于哪个 led。

这就是成员函数指针不同并且有些限制的原因:您只能调用成员函数指针 class 的函数。如果多态性足够(即,如果派生 class 具有已在基 classe 中预见的函数的不同实现)那么你很幸运。如果您想使用仅存在于派生 class 中而不存在于基础 class 中的函数,它将无法编译。

这里是 auto_swith 的简化版本:我提供了一个函数,但也提供了一个指向必须调用该函数的对象的指针:

class auto_switch{
    void (led::*action)(); 
    led *ld; 
public: 
    auto_switch(void(led::*a)(), led*l) : action(a), ld(l) {}
    void go () { (ld->*action)(); }
}; 
// usage:  
auto_switch s(&led::turn_off, &l1);
s.go(); 

Online demo

选项 3:功能性方式(这可能是您正在寻找的方式吗?)

另一种变体是使用标准函数库来绑定成员函数及其执行对象(以及任何需要的参数):

class auto_switch{
    std::function<void()> action; 
public: 
    auto_switch(function<void()>a) : action(a) {}
    void go () { action(); }
}; 

在这里你可以绑定任何东西:任何class的任何函数:

auto_switch s(bind(&led::turn_off, l1));
s.go(); 
auto_switch s2(bind(&blinking_led::blink, l2));
s2.go(); 

Online demo

选项 4:命令模式

现在,如果你想在打开和关闭开关时对某个对象执行某些操作,但你需要完全的灵活性,你可以只实现 command pattern :这使您可以在任何对象上执行任何操作。而且您甚至不需要函数指针。