草庐IT

C++ 函数指针 & 类成员函数指针

runoob 2023-03-28 原文

一、函数指针

函数存放在内存的代码区域内,它们同样有地址.如果我们有一个 int test(int a) 的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。

1、函数指针的定义方式

data_types (*func_pointer)( data_types arg1, data_types arg2, ...,data_types argn);

例如:

int (*fp)(int a); // 这里就定义了一个指向函数(这个函数参数仅仅为一个 int 类型,函数返回值是 int 类型)的指针 fp。

实例


int test(int a) { return a; } int main(int argc, const char * argv[]) { int (*fp)(int a); fp = test; cout<<fp(2)<<endl; return 0; }

注意:函数指针所指向的函数一定要保持函数的返回值类型,函数参数个数,类型一致。

2、typedef 定义可以简化函数指针的定义

实例


int test(int a) { return a; } int main(int argc, const char * argv[]) { typedef int (*fp)(int a); fp f = test; cout<<f(2)<<endl; return 0; }

3、 函数指针同样是可以作为参数传递给函数的

实例


int test(int a) { return a-1; } int test2(int (*fun)(int),int b) { int c = fun(10)+b; return c; } int main(int argc, const char * argv[]) { typedef int (*fp)(int a); fp f = test; cout<<test2(f, 1)<<endl; // 调用 test2 的时候,把test函数的地址作为参数传递给了 test2 return 0; }

执行以上代码,输出结果为:

10

4、利用函数指针,我们可以构成函数指针数组,更明确点的说法是构成指向函数的指针数组。

实例


void t1(){cout<<"test1"<<endl;} void t2(){cout<<"test2"<<endl;} void t3(){cout<<"test3"<<endl;} int main(int argc, const char * argv[]) { typedef void (*fp)(void); fp b[] = {t1,t2,t3}; // b[] 为一个指向函数的指针数组 b[0](); // 利用指向函数的指针数组进行下标操作就可以进行函数的间接调用了 return 0; }

二、指向类成员函数的函数指针

定义:类成员函数指针(member function pointer),是 C++ 语言的一类指针数据类型,用于存储一个指定类具有给定的形参列表与返回值类型的成员函数的访问信息。

基本上要注意的有两点:

  • 1、函数指针赋值要使用 &
  • 2、使用 .* (实例对象)或者 ->*(实例对象指针)调用类成员函数指针所指向的函数

下面看两个例子:

A) 类成员函数指针指向类中的非静态成员函数

对于 nonstatic member function (非静态成员函数)取地址,获得该函数在内存中的实际地址

对于 virtual function(虚函数), 其地址在编译时期是未知的,所以对于 virtual member function(虚成员函数)取其地址,所能获得的只是一个索引值

实例


//指向类成员函数的函数指针 #include <iostream> #include <cstdio> using namespace std; class A { public: A(int aa = 0):a(aa){} ~A(){} void setA(int aa = 1) { a = aa; } virtual void print() { cout << "A: " << a << endl; } virtual void printa() { cout << "A1: " << a << endl; } private: int a; }; class B:public A { public: B():A(), b(0){} B(int aa, int bb):A(aa), b(bb){} ~B(){} virtual void print() { A::print(); cout << "B: " << b << endl; } virtual void printa() { A::printa(); cout << "B: " << b << endl; } private: int b; }; int main(void) { A a; B b; void (A::*ptr)(int) = &A::setA; A* pa = &a; //对于非虚函数,返回其在内存的真实地址 printf("A::set(): %p\n", &A::setA); //对于虚函数, 返回其在虚函数表的偏移位置 printf("B::print(): %p\n", &A::print); printf("B::print(): %p\n", &A::printa); a.print(); a.setA(10); a.print(); a.setA(100); a.print(); //对于指向类成员函数的函数指针,引用时必须传入一个类对象的this指针,所以必须由类实体调用 (pa->*ptr)(1000); a.print(); (a.*ptr)(10000); a.print(); return 0; }

执行以上代码,输出结果为:

A::set(): 0x8048a38
B::print(): 0x1
B::print(): 0x5
A: 0
A: 10
A: 100
A: 1000
A: 10000

B) 类成员函数指针指向类中的静态成员函数

实例


#include <iostream> using namespace std; class A{ public: //p1是一个指向非static成员函数的函数指针 void (A::*p1)(void); //p2是一个指向static成员函数的函数指针 void (*p2)(void); A(){ /*对 **指向非static成员函数的指针 **和 **指向static成员函数的指针 **的变量的赋值方式是一样的,都是&ClassName::memberVariable形式 **区别在于: **对p1只能用非static成员函数赋值 **对p2只能用static成员函数赋值 ** **再有,赋值时如果直接&memberVariable,则在VS中报"编译器错误 C2276" **参见:http://msdn.microsoft.com/zh-cn/library/850cstw1.aspx */ p1 =&A::funa; //函数指针赋值一定要使用 & p2 =&A::funb; //p1 =&A::funb;//error //p2 =&A::funa;//error //p1=&funa;//error,编译器错误 C2276 //p2=&funb;//error,编译器错误 C2276 } void funa(void){ puts("A"); } static void funb(void){ puts("B"); } }; int main() { A a; //p是指向A中非static成员函数的函数指针 void (A::*p)(void); (a.*a.p1)(); //打印 A //使用.*(实例对象)或者->*(实例对象指针)调用类成员函数指针所指向的函数 p = a.p1; (a.*p)();//打印 A A *b = &a; (b->*p)(); //打印 A /*尽管a.p2本身是个非static变量,但是a.p2是指向static函数的函数指针, **所以下面这就话是错的! */ // p = a.p2;//error void (*pp)(void); pp = &A::funb; pp(); //打印 B return 0; }

总结

类成员函数指针与普通函数指针不是一码事。前者要用 .* 与 ->* 运算符来使用,而后者可以用 * 运算符(称为"解引用"dereference,或称"间址"indirection)。

普通函数指针实际上保存的是函数体的开始地址,因此也称"代码指针",以区别于 C/C++ 最常用的数据指针。

而类成员函数指针就不仅仅是类成员函数的内存起始地址,还需要能解决因为 C++ 的多重继承、虚继承而带来的类实例地址的调整问题,所以类成员函数指针在调用的时候一定要传入类实例对象。

原文地址:https://blog.csdn.net/crayondeng/article/details/16868351

有关C++ 函数指针 & 类成员函数指针的更多相关文章

  1. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  2. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  3. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

  4. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  5. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  6. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  7. ruby-on-rails - 将字符串转换为 ruby​​-on-rails 中的函数 - 2

    我需要一个通过输入字符串进行计算的方法,像这样function="(a/b)*100"a=25b=50function.something>>50有什么方法吗? 最佳答案 您可以使用instance_eval:function="(a/b)*100"a=25.0b=50instance_evalfunction#=>50.0请注意,使用eval本质上是不安全的,尤其是当您使用外部输入时,因为它可能包含注入(inject)的恶意代码。另请注意,a设置为25.0而不是25,因为如果它是整数a/b将导致0(整数)。

  8. ruby - 在 ruby​​ 中使用 .try 函数和 .map 函数 - 2

    我需要从json记录中获取一些值并像下面这样提取curr_json_doc['title']['genre'].map{|s|s['name']}.join(',')但对于某些记录,curr_json_doc['title']['genre']可以为空。所以我想对map和join()使用try函数。我试过如下curr_json_doc['title']['genre'].try(:map,{|s|s['name']}).try(:join,(','))但是没用。 最佳答案 你没有正确传递block。block被传递给参数括号外的方法

  9. ruby - 是否可以从也在该模块中的类内部调用模块函数 - 2

    在这段Ruby代码中:ModuleMClassC当我尝试运行时出现“'M:Module'的未定义方法'helper'”错误c=M::C.new("world")c.work但直接从另一个类调用M::helper("world")工作正常。类不能调用在定义它们的同一模块中定义的模块函数吗?除了将类移出模块外,还有其他解决方法吗? 最佳答案 为了调用M::helper,你需要将它定义为defself.helper;结束为了进行比较,请查看以下修改后的代码段中的helper和helper2moduleMclassC

  10. ruby 变量作为同一对象(指针?) - 2

    >>a=5=>5>>b=a=>5>>b=4=>4>>a=>5如何将“b”设置为实际的“a”,以便在示例中,变量a也将变为4。谢谢。 最佳答案 classRefdefinitializeval@val=valendattr_accessor:valdefto_s@val.to_sendenda=Ref.new(4)b=aputsa#=>4putsb#=>4a.val=5putsa#=>5putsb#=>5当您执行b=a时,b指向与a相同的对象(它们具有相同的object_id).当你执行a=some_other_thing时,a将指向

随机推荐