快捷搜索:  汽车  科技

c语言函数指针的定义与赋值:类成员函数指针语法的友好指南

c语言函数指针的定义与赋值:类成员函数指针语法的友好指南class MyClass { public: int sum(int a int b) { return a b; } }; 1、定义一个指针指向某一个类中一个成员函数声明一个指针指向 MyClass类成员函数。在此时,你并不知道想调用的具体函数。你仅仅声明了一个指向MyClass类中任意成员函数的指针。当然,签名(参数、返回值类型)需要匹配你接下想要调用的sum(...)函数:让我们来进入 C :好消息是你也许不需要使用类成员函数指针,除非在一个特别罕见的情况下,比如说接下来的例子。首先,你已经知道定义一个类和其中一个成员函数:int sum(int a int b) { return a b; } 在纯 C 语言中,你可以创建一个指向这个函数的指针,将其分配给你的 sum(...)函数,通过解引用来调用它。函数的签名(参数、返回类型)

一旦你理解了一般原则,C 类成员函数指针不再那么令人生畏。

c语言函数指针的定义与赋值:类成员函数指针语法的友好指南(1)

如果你正在寻找性能、复杂性或许多可能的解决方法来解决问题,那么在涉及到极端的情况下,C 总是一个很好的选择。当然,功能通常伴随着复杂性,但是一些 C 的特性几乎难以分辨。根据我的观点,C 的类成员函数指针也许是我接触过的最复杂的表达式,但是我会先从一些较简单的开始。

文章中的例子可以在我的 Github 仓库里找到。

C 语言:函数指针

让我们先从一些基础开始:假设你有一个函数接收两个整数作为参数返回一个整数:

int sum(int a int b) { return a b; }

在纯 C 语言中,你可以创建一个指向这个函数的指针,将其分配给你的 sum(...)函数,通过解引用来调用它。函数的签名(参数、返回类型)必须符合指针的签名。除此之外,一个函数指针表现和普通的指针相同:

int (*funcPtrOne)(int int); funcPtrOne = ∑ int resultOne = funcPtrOne(2 5);

如果你使用指针作为参数并返回一个指针,这会显得很丑陋:

int *next(int *arrayOfInt){ return arrayOfInt; } int *(*funcPtrTwo)(int *intPtr); funcPtrTwo = &next; int resultTwo = *funcPtrTwo(&array[0]);

C 语言中的函数指针存储着子程序的地址。

指向类成员函数的指针

让我们来进入 C :好消息是你也许不需要使用类成员函数指针,除非在一个特别罕见的情况下,比如说接下来的例子。首先,你已经知道定义一个类和其中一个成员函数:

class MyClass { public: int sum(int a int b) { return a b; } };

1、定义一个指针指向某一个类中一个成员函数

声明一个指针指向 MyClass类成员函数。在此时,你并不知道想调用的具体函数。你仅仅声明了一个指向MyClass类中任意成员函数的指针。当然,签名(参数、返回值类型)需要匹配你接下想要调用的sum(...)函数:

int (MyClass::*methodPtrOne)(int int);

2、赋值给一个具体的函数

为了和 C 语言(或者 静态成员函数)对比,类成员函数指针不需要指向绝对地址。在 C 中,每一个类中都有一个虚拟函数表(vtable)用来储存每个成员函数的地址偏移量。一个类成员函数指针指向 vtable 中的某个条目,因此它也只存储偏移值。这样的原则使得多态变得可行。

因为 sum(...)函数的签名和你的指针声明匹配,你可以赋值签名给它:

methodPtrOne = &MyClass::sum;

3、调用成员函数

如果你想使用指针调用一个类成员函,你必须提供一个类的实例:

MyClass clsInstance; int result = (clsInstance.*methodPtrOne)(2 3);

你可以使用 .操作符来访问,使用*对指针解引用,通过提供两个整数作为调用函数时的参数。这是丑陋的,对吧?但是你可以进一步应用。

在类内使用类成员函数指针

假设你正在创建一个带有后端和前端的 客户端/服务器原理架构的应用程序。你现在并不需要关心后端,相反的,你将基于 C 类的前端。前端依赖于后端提供的数据完成初始化,所以你需要一个额外的初始化机制。同时,你希望通用地实现此机制,以便将来可以使用其他初始化函数(可能是动态的)来拓展你的前端。

首先定义一个数据类型用来存储初始化函数(init)的指针,同时描述何时应调用此函数的信息(ticks):

template

下面一个 Frontend类示例代码:

class Frontend { public: Frontend{ DynamicInitCommand

Frontend完成实例化后,tick函数会被后端以固定的时间时间调用。例如,你可以每 200 毫秒调用一次:

int main(int argc char* argv[]){ Frontend frontendInstance; while(true){ frontendInstance.tick; // 仅用于模拟目的 std::this_thread::sleep_for(std::chrono::milliseconds(200)); } }

Fronted有三个额外的初始化函数,它们必须根据m_ticks的值来选择调用哪个。在 ticks 等于何值调用哪个初始化函数的信息存储在数组m_dynamicInit中。在构造函数(Frontend())中,将此信息附加到数组中,以便在 5、10 和 15 个 tick 后调用其他初始化函数。当后端调用tick函数时,m_ticks值会递增,同时遍历数组m_dynamicInit以检查是否必须调用初始化函数。

如果是这种情况,则必须通过引用 this指针来取消引用成员函数指针:

((*this).*(it->init))

总结

如果你并不熟悉类成员函数指针,它们可能会显得有些复杂。我做了很多尝试和经历了很多错误,花了一些时间来找到正确的语法。然而,一旦你理解了一般原理后,方法指针就变得不那么可怕了。

这是迄今为止我在 C 中发现的最复杂的语法。 你还知道更糟糕的吗? 在评论中发布你的观点!

via: https://opensource.com/article/21/2/ccc-method-pointers

作者:Stephan Avenwedde选题:lujun9972译者:萌新阿岩校对:wxy

本文由 LCTT原创编译,Linux中国荣誉推出

猜您喜欢: