当前位置: 首页 > 科技观察

C++类成员函数指针语法友好指南

时间:2023-03-13 20:56:24 科技观察

一旦理解了一般原则,C++类成员函数指针就不那么令人生畏了。如果您正在寻找性能、复杂性或许多可能的问题解决方法,那么在涉及极端情况时,C++始终是一个不错的选择。当然,功能往往伴随着复杂性,但C++的某些特性几乎无法区分。在我看来,C++的类成员函数指针可能是我遇到过的最复杂的表达式,但我将从一些更简单的开始。文章中的示例可以在我的Github存储库中找到。C语言:函数指针让我们从一些基础知识开始:假设您有一个函数,它接受两个整数作为参数并返回一个整数:intsum(inta,intb){returna+b;}在纯C中,您可以创建指向此函数的指针,将其分配给您的sum(...)函数,并通过取消引用来调用它。函数的签名(参数、返回类型)必须与指针的签名匹配。否则,函数指针的行为类似于普通指针:int(*funcPtrOne)(int,int);funcPtrOne=∑intresultOne=funcPtrOne(2,5);如果你使用一个指针作为参数并返回一个指针,这看起来会很难看:resultTwo=*funcPtrTwo(&array[0]);C语言中的函数指针存放的是子程序的地址。指向类成员函数的指针让我们进入C++:好消息是您可能不需要使用类成员函数指针,除非在极少数情况下,例如下一个示例。首先,您已经知道定义一个类及其成员函数之一:classMyClass{public:intsum(inta,intb){returna+b;}};1.在某个类中定义一个指向成员函数的指针DeclareapointertoamemberfunctionoftheMyClassclass.此时,您不知道要调用的确切函数。您只需在MyClass类中声明一个指向任何成员函数的指针。当然,签名(参数,返回值类型)需要匹配你接下来要调用的sum(...)函数:int(MyClass::*methodPtrOne)(int,int);2.为特定函数赋值与C语言(或静态成员函数)相比,类成员函数指针不需要指向绝对地址。在C++中,每个类都有一个虚函数表(vtable),用来存放每个成员函数的地址偏移量。类成员函数指针指向vtable中的一个条目,因此它也只存储偏移量。这些原则使多态性成为可能。因为sum(...)函数的签名与您的指针声明相匹配,您可以将签名分配给它:methodPtrOne=&MyClass::sum;3.调用成员函数如果要使用指针调用类的成员函数,必须提供一个类的实例:MyClassclsInstance;intresult=(clsInstance.*methodPtrOne)(2,3);您可以使用.运算符来访问,使用*取消引用指针,方法是在调用函数时提供两个整数作为参数。很丑吧?但是你可以进一步应用它。在类中使用类成员函数指针假设您正在创建一个具有客户端/服务器原则体系结构的应用程序,该体系结构具有后端和前端。您现在不需要关心后端,相反您将拥有一个基于C++类的前端。前端依赖于后端提供的数据来完成初始化,所以需要额外的初始化机制。同时,您希望通用地实现此机制,以便将来可以使用其他初始化函数(可能是动态的)来扩展您的前端。首先定义一个数据类型来存储初始化函数(init)的指针,并描述这个函数应该在什么时候被调用(ticks):templatestructDynamicInitCommand{void(T::*init)();//指向附加初始化函数unsignedintticks;//init()调用后的滴答数};Frontend类的以下示例代码:classFrontend{public:Frontend(){DynamicInitCommandinit1,init2,init3;init1={&Frontend::dynamicInit1,5};init2={&Frontend::dynamicInit2,10};init3={&Frontend::dynamicInit3,15};m_dynamicInit.push_back(init1);m_dynamicInit.push_back(init2);m_dynamicInit.push_back(init3);}voidtick(){std::cout<<"tick:"<<++m_ticks<>::iteratorit=m_dynamicInit.begin();while(it!=m_dynamicInit.end()){if(it->ticksinit)((*this).*(it->init))();//这里是具体的调用it=m_dynamicInit.erase(it);}否则{它++;}}}unsignedintm_ticks{0};private:voiddynamicInit1(){std::cout<<"dynamicInit1called"<>m_dynamicInit;};Frontend完成实例化后,tick()函数会在固定时间被后端调用。例如,您可以每200毫秒调用一次:intmain(intargc,char*argv[]){FrontendfrontendInstance;while(true){frontendInstance.tick();//仅用于模拟目的std::this_thread::sleep_for(std::chrono::milliseconds(200));}}Fronted有三个额外的初始化函数,它们必须根据m_ticks的值选择调用哪个。有关在什么值ticks等于时调用哪个初始化函数的信息存储在数组m_dynamicInit中。在构造函数(Frontend())中,将此信息附加到一个数组,以便在5、10和15个滴答后调用其他初始化函数。后端调用tick()函数时,m_ticks值递增,遍历数组m_dynamicInit,判断是否必须调用初始化函数。如果是这种情况,你必须通过引用this指针来解引用成员函数指针:((*this).*(it->init))()总结如果你不熟悉类成员函数指针,它们可能会出现这有点复杂。我做了很多试验和错误,花了一些时间才找到正确的语法。然而,一旦您了解了一般原则,方法指针就变得不那么可怕了。这是迄今为止我在C++中发现的最复杂的语法。

最新推荐
猜你喜欢