通过具有公共成员函数指针的对象进行函数调用,而不使用解引用运算符的C++函数

5

好的,我认为标题已经足够描述了(尽管有些令人困惑,抱歉)。

我正在阅读这个库:Timer1

在头文件中,有一个公共成员指针指向一个函数,如下所示:

class TimerOne
{
  public:
  void (*isrCallback)();  // C-style ptr to `void(void)` function
};

存在一个TimerOne类的实例化对象,名为“Timer1”。
Timer1调用函数如下:
Timer1.isrCallback();

这样正确吗?我熟悉通过使用解引用运算符来调用函数指针的方式。
(*myFunc)();

所以我本来期望通过对象进行的上述调用会更像:

(*Timer1.isrCallback)();

那么,通过函数指针调用函数的可接受选项是什么,无论是作为独立的函数指针还是作为对象成员?

另请参阅:

  1. [非常有用!] typedef函数指针?

答案摘要:

以下是调用函数指针的有效方法:

myFuncPtr();
(*myFuncPtr)();
(**myFuncPtr)();
(***myFuncPtr)();
// etc.
(**********************************f)(); // also valid

一个函数指针,即使它是一个类成员,仍然是一个函数指针。为什么规则应该有任何不同呢? - IInspectable
3个回答

5

您可以使用函数指针来做的事情。

1: 第一种是通过显式解引用调用函数:

int myfunc(int n)
{
}

int (*myfptr)(int) = myfunc; 

(*myfptr)(nValue); // call function myfunc(nValue) through myfptr.

第二种方式是通过隐式解引用:

int myfunc(int n)
{
}

int (*myfptr)(int) = myfunc;

myfptr(nValue); // call function myfunc(nValue) through myfptr.

正如你所看到的,隐式解引用方法看起来就像普通的函数调用 - 这也是你期望的,因为函数可以简单地隐式转换为函数指针!

在你的代码中:

void foo()
{
    cout << "hi" << endl;
}

class TimerOne
{
public:

    void(*isrCallback)();
};


int main()
{

    TimerOne Timer1;
    Timer1.isrCallback = &foo;   //Assigning the address
    //Timer1.isrCallback = foo;   //We could use this statement as well, it simply proves function are simply implicitly convertible to function pointers. Just like arrays decay to pointer.
    Timer1.isrCallback();         //Implicit dereference
    (*Timer1.isrCallback)();      //Explicit dereference
        return 0;
}

由于看起来您可能错过了:您的代码注释没有完全进入代码块。 - jaggedSpire
什么都不要!!!! 这会导致错误。你的类中有一个名为“*isrCallback”的成员吗?没有,你有一个名为“isrcallBack”的函数指针... 希望你明白我的意思。 - Nishant
1
@GabrielStaples,它取消引用isrCallback的结果,如此处所示(http://coliru.stacked-crooked.com/a/ce7d73a651525f25)。 - chris
2
@Nishant,函数名不像数组名一样是指向函数的指针。这在这里有所说明(http://coliru.stacked-crooked.com/a/08621fb5f8c4e1d3)。函数只是隐式可转换为函数指针。 - chris
@Nishant,感谢您抽出时间写下详细的答案。您涵盖了所有基础知识,包括隐式 vs 显式、成员 vs 非成员、显式地址分配(使用&)vs 隐式地址分配。这些都是我想要澄清的问题。我昨晚已将其标记为被接受的答案。 - Gabriel Staples
显示剩余3条评论

4
您无需取消引用函数指针即可调用它。根据标准([expr.call]/1),后缀表达式应具有函数类型或函数指针类型。
因此(*myFunc)()是有效的,myFunc()也是有效的。事实上,(**myFunc)()也是有效的,并且您可以随意取消引用多次(您能想出为什么吗?)

Brian,前几天我在Quora上读到了关于你的文章(http://www.quora.com/Brian-Bi-1)。我没想到你会回答我的问题。好吧...我能猜出为什么吗?也许是因为一个函数本身就是一个指针,而你不能超越它最外层的级别来取消引用指针?...不太确定。 - Gabriel Staples
我能找出原因吗?因为编译器是这样设计的。它将函数视为指向函数的指针,这与指向函数的指针相同,显然也与指向指向函数的指针相同。编译器被编程为将函数视为内存中的地址。我不知道,为什么会这样?我想听听你的答案。我相信你可以提供更多的见解。 - Gabriel Staples
1
每当函数出现在需要进行此类转换的上下文中时,函数就会被转换为函数指针。因此,如果您取消引用函数指针以获取函数,则始终可以再次取消引用它:*运算符需要一个指针,因此该函数将被转换回函数指针。这可以无限次地完成。 - Brian Bi

2

您需要:

Timer1 calls the function as follows:

Timer1.isrCallback();

How is this correct?

Timer1.isrCallback 的类型是 void (*)(),它是一个指向函数的指针。这就是为什么你可以使用那种语法的原因。
这类似于使用:
void foo()
{
}

void test_foo()
{
   void (*fptr)() = foo;
   fptr();
}

你也可以使用:

void test_foo()
{
   void (*fptr)() = foo;
   (*fptr)();
}

但第一种形式同样有效。

更新,针对OP的评论

根据发布的类定义,您将使用:

(*Timer1.isrCallback)(); 

使用

(Timer1.*isrCallback)(); 

isrCallback必须定义为非成员变量,其类型为指向TimerOne成员变量的指针。

void (TimerOne::*isrCallback)();

例子:

#include <iostream>

class TimerOne
{
  public:
  void foo()
  {
     std::cout << "In TimerOne::foo();\n";
  }
};

int main()
{
   TimerOne Timer1;
   void (TimerOne::*isrCallback)() = &TimerOne::foo;
   (Timer1.*isrCallback)();
}

输出:

In TimerOne::foo();

(测试此代码)


如果您想将isrCallbak定义为TimerOne的成员变量,则需要使用:

#include <iostream>

class TimerOne
{
  public:
  void (TimerOne::*isrCallback)();
  void foo()
  {
     std::cout << "In TimerOne::foo();\n";
  }
};

int main()
{
   TimerOne Timer1;
   Timer1.isrCallback = &TimerOne::foo;

   // A little complicated syntax.
   (Timer1.*(Timer1.isrCallback))();
}

输出:

In TimerOne::foo();

(测试这段代码)


通过对象来实现,后一种形式是这样的:(*Timer1.isrCallback)(); 还是这个 Timer1.*isrCallback();?有什么区别吗? - Gabriel Staples

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接