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

时间:2020-01-29 19:12:22

标签: c++ function pointers struct

我正在尝试创建一个具有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

有可能吗?我想念什么吗?

5 个答案:

答案 0 :(得分:2)

onClicked是一个函数时,&onClickedonClicked的计算结果相同,即指向该函数的指针。

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

但是,根据您的用法,您只需要一个指向函数的指针即可。

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

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

答案 1 :(得分:2)

我认为您仍然可以使用指向函数指针的指针来解决您的问题,但这有点笨拙,因为您必须以与“普通”函数指针不同的方式调用此指针。调用看起来像(*(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

答案 2 :(得分:1)

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

The code works as is。所以你做错了其他事情。

但是,以这种方式使用指向函数指针的指针可能没有所需的语义。如果该结构被复制到另一个结构,则副本中的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 {
        //...
    }
};

如果您遵循此方法,则this will also work.

答案 3 :(得分:1)

一种可能的方法是使函数实现触发处理程序的逻辑。无论如何,您已经有一定的逻辑(<RequestOption>Shop</RequestOption>),呼叫者必须这样做,因此可以最大程度地降低呼叫者犯错的可能性。

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; }; 版本结合使用,测试是否为空,而不是通过执行默认操作的lambda来表示“空”。而且,如果您要使用多个处理程序以使用默认后备广告,则可以通过创建模板Fire功能来减少样板。


另一种可行的方法是使自定义处理程序类型的语义与std::function类似,但是如果未设置任何功能,其std::function运算符将执行默认操作。

答案 4 :(得分:0)

在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 = %s\n", __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=%p\n", (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");
}