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

Pointer to function from another pointer

本文关键字:指针 函数 另一个      更新时间:2023-10-16

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

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

问题是,只有当它是本地函数时,我才能在我的代码中传递该函数,但是如果我尝试从另一个对象(例如 LED (传递函数,它将无法正常工作。

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

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

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

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

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

备选办法1:多态性专业化

您是否想专门化交换机的某些功能,因此与其调用auto_switch的功能,不如将圆顶称为更专业的功能?

在这种情况下,您不会执行以下操作:

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

但相反,您将依赖于基类中虚函数的多态性:

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

并为 LED 编写一个专门的类:

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 

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

选项 2:成员函数指针

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

这就是为什么成员函数指针不同且有些约束的原因:您只能调用成员函数指针的类函数。 如果多态性足够(即,如果派生类对基类中已经预见的函数有不同的实现(,那么你很幸运。 如果要使用仅存在于派生类中而不存在于基类中的函数,则它不会编译。

这里是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(); 

在线演示

选项3:功能方式(这可能是您要找的吗?

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

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

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

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

在线演示

选项 4:命令模式

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