如何创建指向函数指针的指针?

How to create a pointer to a function pointer?

本文关键字:指针 函数 何创建 创建      更新时间:2023-10-16

我正在尝试创建一个具有 2 个函数的结构,如果以后需要,可以重写。函数是:onClicked(( 和 onClickedRight((。结构的代码:

typedef struct {
QString text;
QString infoText;
QUrl iconSrc;
QColor iconColor;
void (*onClicked)() = nullptr;
void (*(*onClickedRight))() = &onClicked; // by default, execute the same function from onClicked()
} ConfigButton;

我如何尝试执行这些功能:

ConfigButton b;
...
// test if click funtion has been defined, to execute it
if (b.onClicked)
b.onClicked(); // this one work just fine
...
if (*(b.onClickedRight))
(*(b.onClickedRight))(); // this one crashed

甚至可能吗?我错过了什么吗?

onClicked是一个函数时,&onClickedonClicked的计算结果都指向同一个东西——指向函数的指针。

如果要创建指向函数指针的指针,则首先需要指向作为变量的函数的指针。

但是,鉴于您的使用情况,您只需要一个指向函数的指针。

typedef struct {
QString text;
QString infoText;
QUrl iconSrc;
QColor iconColor;
void (*onClicked)() = nullptr;
void (*onClickedRight)() = onClicked;
} ConfigButton;

if ( b.onClickedRight)
b.onClickedRight();

我认为你仍然可以用指向函数指针的指针来解决你的问题,但它有点笨拙,因为你必须以与使用"普通"函数指针不同的方式调用这个指针指针。调用看起来像(*(aButton.onRightClick))(),你需要让onRightClick指向指向函数的指针对象,而不是直接赋值函数。

我想您正在寻找一种方法来定义onRightClick默认情况下应"继承"onClick的行为,除非用户覆盖它并为onRightClick分配不同的行为。我认为可能的解决方案应满足两个要求:

1( 如果onRightClick没有被覆盖,它应继承对onClick所做的所有更改

2(如果onRightClick被覆盖,它就会与onClick解耦。

您可以使用"简单"函数指针来解决此问题,方法是onRightClick分配一个函数,该函数仅调用分配给onClick的函数。以下代码显示了C++;该方法可以转移到 C 中(尽管您需要将"this"传递给调用onClick的函数:

void defaultOnClick() {
std::cout << "defaultOnClick" << std::endl;
}
void otherClick() {
std::cout << "otherClick" << std::endl;
}
void rightClick() {
std::cout << "rightClick" << std::endl;
}

typedef std::function<void(void)> onClickFunctionType;
struct ConfigButton {
onClickFunctionType onClick = defaultOnClick;
onClickFunctionType onRightClick = [this](){ this->onClick(); };
} ;
int main() {
ConfigButton configButton;
std::cout << "default for both onClick and onRightClick (calling onClick)" << std::endl;
configButton.onClick();
configButton.onRightClick();
std::cout << "special click for onClick; 'inherited' by onRightClick" << std::endl;
configButton.onClick = otherClick;
configButton.onClick();
configButton.onRightClick();
std::cout << "special click for onClick; different one for onRightClick" << std::endl;
configButton.onRightClick = rightClick;
configButton.onClick();
configButton.onRightClick();
}

输出:

default for both onClick and onRightClick (calling onClick)
defaultOnClick
defaultOnClick
special click for onClick; 'inherited' by onRightClick
otherClick
otherClick
special click for onClick; different one for onRightClick
otherClick
rightClick

答案已调整,因为 OP 已从标签列表中删除 c。

代码按原样工作。所以你做错了别的事情。

但是,以这种方式使用指向函数的指针的指针可能没有所需的语义。如果将结构复制到另一个结构,则副本中的onClickedRight成员不会指向其自身实例中的onClicked指针。相反,它指向原始实例的onClicked指针。

a.onClickedRight = &a.onClicked;
b = a;
assert(b.onClickedRight == &a.onClicked); // Is this intentional?

这意味着您必须格外小心如何使用包含指向其自身成员的指针(以及指向任何内容的指针,实际上(的结构。您可能需要某种深度复制方法(因此,根据 TRoT,您需要一个复制构造函数、一个赋值运算符和一个析构函数(。

无论如何,C++代码并不是真正的惯用语。就我自己而言,我可能会利用虚拟方法。虚拟方法语法可以轻松适应此用例。

struct ConfigButton {
QString text;
QString infoText;
QUrl iconSrc;
QColor iconColor;
virtual void onClicked() const = 0;
virtual void onClickedRight () const { onClicked(); }
};
struct Foo : ConfigButton {
void onClicked () const {
//...
}
};

如果您遵循此方法,这也将起作用。

一种可能的方法是让函数实现触发处理程序的逻辑。您已经有一些调用者无论如何都必须执行的逻辑(if (onClicked)(,因此这最大限度地减少了调用者出错的可能性。

struct ConfigButton {
// ...
void Fire_OnClicked()
{
if ( onClicked )
onClicked();
}
void Fire_OnClickedRight()
{
if ( onClickedRight )
onClickedRight();
else
Fire_OnClicked();
}
private: 
void (*onClicked)() = nullptr;
void (*onClickedRight)() = nullptr;
};

您可以将其与std::function版本结合使用,测试空,而不是要求"空"由执行默认操作的 lambda 表示。如果有多个处理程序,您希望具有默认回退,则可以通过创建模板Fire函数来减少样板。


另一种可能有效的方法是制作具有与std::function语义相似的自定义处理程序类型,但如果未设置任何函数,则其()运算符将执行默认操作。

在 C 语言函数中,指针是唯一将指针隐藏在 typedefs 后面有意义的地方

https://godbolt.org/z/Gb_WEy

#include <stdio.h>
typedef int (*fptr_t)();
typedef struct
{
fptr_t fptr;
fptr_t *pfptr;
fptr_t **ppfptr;
fptr_t ***pppfptr;
}MYSTRUCT_t;
int foo(char *caller)
{
printf("Function name = %s, caller = %sn", __FUNCTION__, caller);
return 0;
}
int main()
{
MYSTRUCT_t mystr;
mystr.fptr = foo;
mystr.pfptr = &mystr.fptr;
mystr.ppfptr = &mystr.pfptr;
mystr.pppfptr = &mystr.ppfptr;
printf("mystr.fptr=%p mystr.pfptr=%p func=%pn", (void *)mystr.fptr, (void *)mystr.pfptr, (void *)&foo);
foo("foo");
mystr.fptr("mystr.fptr");
(*mystr.pfptr)("mystr.pfptr");
(*(*mystr.ppfptr))("mystr.ppfptr");
(*(*(*mystr.pppfptr)))("mystr.pppfptr");
}