精华内容
下载资源
问答
  • C++ 类成员函数的函数指针
    万次阅读 多人点赞
    2018-08-23 18:40:17

    一、引言

    当我们在 C++ 中直接像 C 那样使用类的成员函数指针时,通常会报错,提示你不能使用非静态的函数指针:

    reference to non-static member function must be called

    两个解决方法:

    1. 把非静态的成员方法改成静态的成员方法
    2. 正确的使用类成员函数指针(在下面介绍)

     

    关于函数指针的定义和使用你还不清楚的话,可以先看这篇博客了解一下:

    https://blog.csdn.net/afei__/article/details/80549202

     

    二、语法

    1. 非静态的成员方法函数指针语法(同C语言差不多):

    void (*ptrStaticFun)() = &ClassName::staticFun;

    2. 成员方法函数指针语法:

    void (ClassName::*ptrNonStaticFun)() = &ClassName::nonStaticFun;

    注意调用类中非静态成员函数的时候,使用的是 类名::函数名,而不是 实例名::函数名

     

    三、实例:

    #include <stdio.h>
    #include <iostream>
      
    using namespace std;
      
    class MyClass {
    public:
        static int FunA(int a, int b) {
            cout << "call FunA" << endl;
            return a + b;
        }
      
        void FunB() {
            cout << "call FunB" << endl;
        }
      
        void FunC() {
            cout << "call FunC" << endl;
        }
      
        int pFun1(int (*p)(int, int), int a, int b) {
            return (*p)(a, b);
        }
      
        void pFun2(void (MyClass::*nonstatic)()) {
            (this->*nonstatic)();
        }
    };
      
    int main() {
        MyClass* obj = new MyClass;
        // 静态函数指针的使用
        int (*pFunA)(int, int) = &MyClass::FunA;
        cout << pFunA(1, 2) << endl;
         
        // 成员函数指针的使用
        void (MyClass::*pFunB)() = &MyClass::FunB;
        (obj->*pFunB)();
         
        // 通过 pFun1 只能调用静态方法
        obj->pFun1(&MyClass::FunA, 1, 2);
         
        // 通过 pFun2 就是调用成员方法
        obj->pFun2(&MyClass::FunB);
        obj->pFun2(&MyClass::FunC);
     
        delete obj;
        return 0;
    }

    更多相关内容
  • c++类成员函数作为回调函数
  • C++类成员函数指针使用介绍

    万次阅读 多人点赞 2019-09-21 16:07:49
    在之前写过的博客中有介绍过函数指针和指针函数的区别和简单用法(文章在这里),当时的Demo非常简单,都是C语言的写法,但是当在C++中直接像C那样使用类成员函数指针时就会报错:reference to non-static member ...

    前言

    在之前写过的博客中有介绍过函数指针和指针函数的区别和简单用法(文章在这里),当时的Demo非常简单,都是C语言的写法,但是当在C++中直接像C那样使用类成员函数指针时就会报错:reference to non-static member function must be called
    所以如果是C++中的成员函数指针其使用方法是有区别的,这里针对不同的场景做个补充说明。

    类成员函数的指针(非静态)

    指向类成员函数的指针与普通函数指针的区别在于,前者需要匹配函数的参数类型和个数以及返回值类型,还要匹配该函数指针所属的类类型。

    这是因为非静态的成员函数必须被绑定到一个类的对象或者指针上,才能得到被调用对象的this指针,然后才能调用指针所指的成员函数(所有类的对象都有自己数据成员的拷贝,但是成员函数都是共用的,为了区分是谁调用了成员函数,就必须有this指针,this指针是隐式的添加到函数参数列表里去的)。

    所以,对于类成员函数的指针使用包含以下几个步骤:

    声明: 指向类的成员函数的指针需要在指针前面加上类的类型,格式为:

    typedef 返回值 (类名::*指针类型名)(参数列表);
    

    赋值: 需要用类的成员函数地址赋值,格式为:

    指针类型名  指针名 = &类名::成员函数名;
    

    注意:这里的这个&符号是比较重要的:不加&,编译器会认为是在这里调用成员函数,所以需要给出参数列表,否则会报错;加了&,才认为是要获取函数指针。这是C++专门做了区别对待。

    调用: 针对调用的对象是对象还是指针,分别用.*和->*进行调用,格式为:

    (类对象.*指针名)(参数列表);
    
    (类指针->*指针名)(参数列表);
    

    注意:这里的前面一对括号是很重要的,因为()的优先级高于成员操作符指针的优先级。

    直接来看一个示例吧:

    class Calculation
    {
    public:
        int add(int a,int b){ //非静态函数
            return  a + b;
        }
    };
    
    typedef int (Calculation::*FuncCal)(int,int);
    
    int main()
    {
        FuncCal funAdd = &Calculation::add;
        Calculation * calPtr = new Calculation;
        int ret = (calPtr->*funAdd)(1,2);  //通过指针调用
    
        Calculation cal;
        int ret2 = (cal.*funAdd)(3,4);  //通过对象调用
    
        cout << "ret = " << ret << endl;
        cout << "ret2 = " << ret2 << endl;
        return 0;
    }
    

    指向类的静态函数的指针

    类的静态成员函数和普通函数的函数指针的区别在于,他们是不依赖于具体对象的,所有实例化的对象都共享同一个静态成员,所以静态成员也没有this指针的概念。

    所以,指向类的静态成员的指针就是普通的指针。

    class Calculation
    {
    public:
        static int add(int a,int b){ //非静态函数
            return  a + b;
        }
    };
    
    typedef int (*FuncCal)(int,int);
    
    int main()
    {
        FuncCal funAdd = &Calculation::add;
        int ret = (*funAdd)(1,2);  //直接引用
        int ret2 = funAdd(3,4);  //直接引用
    
        cout << "ret = " << ret << endl;
        cout << "ret2 = " << ret2 << endl;
        return 0;
    }
    
    

    总结以上两种情况的区别:

    • 如果是类的静态成员函数,那么使用函数指针和普通函数指针没区别,使用方法一样
    • 如果是类的非静态成员函数,那么使用函数指针需要加一个类限制一下。

    使用函数指针,很多情况下是用在函数的参数中,在一些复杂的计算,如果需要重复调用,并且每次调用的函数不一样,那么这时候使用函数指针就很方便了,可以减少代码量。

    参考资料:
    https://blog.csdn.net/houzijushi/article/details/81503409
    https://www.cnblogs.com/lvchaoshun/p/7806248.html
    https://www.cnblogs.com/AnnieKim/archive/2011/12/04/2275589.html

    展开全文
  • c++类成员函数指针

    千次阅读 2021-01-19 18:04:56
    提出疑问 首先问大家一句,什么是函数指针? 肯定有的人会这样回答,函数指针?...因为成员函数包括了虚函数和非虚函数(这里涉及虚表问题,可以先简单看看列出的虚函数系列,否则接下来问题会有点难以接受。) 虚函数

    提出疑问

    首先问大家一句,什么是函数指针?
    肯定有的人会这样回答,函数指针?不就是指向函数地址的一个指针吗?或者就是一个存放着一个函数首地址的变量?
    当然,那些有点底层基础的肯定会这样说,函数就是一堆连续的机器码,而函数指针,就是存放了这堆连续机器码首地址的变量。
    详细了解函数指针(因为这里主要讲成员函数指针原理):
    c/c++函数指针(Hook前奏1)
    c/c++ typedef定义函数指针(Hook前奏2)
    那么大家是不是回答的时候,考虑的地方是不是仅仅局限于 一般的函数????那么成员函数呢???
    为什么得强调成员函数呢?因为成员函数包括了虚函数和非虚函数(这里涉及虚表问题,可以先简单看看列出的虚函数系列,否则接下来问题会有点难以接受。)
    虚函数系列:
    详解虚函数的实现过程之初探虚表(1)
    详解虚函数的实现过程之单继承(2)
    详解虚函数的实现过程之多重继承(3)
    详解虚函数的实现过程之虚基类(4)
    详解虚函数的实现过程之菱形继承(5)

    c++层面

    首先,上两份成员函数指针代码

    非虚函数

    #include<iostream>
    using namespace std;
    
    class a {
    	
    public:
    	 int add(int a, int b) {
    		return a + b;
    	}
    };
    
    typedef int (a::* pClassFun)(int, int);
    
    
    int main() {
    
    	pClassFun pointer = &a::add;
    	a  aa;
    	cout << (aa.*pointer)(10, 20);
    }
    

    虚函数

    #include<iostream>
    using namespace std;
    
    class a {
    	
    public:
    	virtual int add(int a, int b) {
    		return a + b;
    	}
    };
    
    typedef int (a::* pClassFun)(int, int);
    
    
    int main() {
    
    	pClassFun pointer = &a::add;
    	a  aa;
    	cout << (aa.*pointer)(10, 20);
    }
    

    一般:
    int (*pointer)(int, int); // 声明函数指针 这里,pointer指向的函数类型是int (int, int),即函数的参数是两个int型,返回值也是int型。
    注:pointer两端的括号必不可少,如果不写这对括号,则pointer是一个返回值为int* 的函数。

    成员函数指针:

    int (A::*pf)(int, int); // 声明一个成员函数指针,这里A::*pf两端的括号也是必不可少的,如果没有这对括号,则pf是一个返回A类数据成员(int型)指针的函数。
    注意:和普通函数指针不同的是,在成员函数和指向该成员的指针之间不存在自动转换规则
    pf = &A::add; //正确:必须显式地使用取址运算符(&)
    pf = A::add; // 错误

    当我们初始化一个成员函数指针时,其指向了类的某个成员函数,但并没有指定该成员所属的对象——直到使用成员函数指针时,才提供成员所属的对象。
    仔细观察后,除了一个有virtual,一个没有virtual,其它的都一样。是的,事实的确如此。

    ida动调分析

    我把所写的代码,然后生成可执行程序后,拖入ida进行分析一番
    首先分析

    非虚函数

    看了一下,它俩的汇编代码,主要是经过一个判断跳,如果是虚函数的话,那么它的寻址是比较复杂的(即跳到那个寻址比较多的代码),如果是非虚函数的话,那么它的寻址是超级简单

    在这里插入图片描述
    非虚函数执行完call _main 之后直接去取出 这个成员函数的地址

    在这里插入图片描述
    地址里面直接是函数的主体,毫无疑问,取出来的就是函数地址

    虚函数

    在这里插入图片描述
    看了一下反编译代码差不多,找到关键点(同一个位置的判断跳转,只是命名不同),jz short loc_401427 ,而这个判断跳转取决于上面取出来的eax值,接下来动调一下,

    虚函数执行完call _main 之后又多执行了几行代码

    .text:004013FA mov     [ebp+var_10], 1
    .text:00401401 mov     [ebp+var_C], 0
    .text:00401408 lea     eax, [ebp+var_28]
    .text:0040140B mov     [esp], eax
    .text:0040140E call    __ZN1aC1Ev                      ; a::a(void)
    

    然后取出地址的时候,并非直接取,而是又调用了一个函数,进行一系列的操作

    call __ZN1aC1Ev

    :00410EFC push    ebp
    .text:00410EFD mov     ebp, esp
    .text:00410EFF mov     eax, [ebp+arg_0]
    .text:00410F02 mov     dword ptr [eax], offset off_4469B8
    .text:00410F08 pop     ebp
    .text:00410F09 retn
    

    然后取地址也就这一行

     mov     dword ptr [eax], offset off_4469B8
    

    但是我们跟随一下,它是不是函数主体呢?
    在这里插入图片描述
    毫无疑问,不是。。。。所以这里取出来的是虚表的地址,再进一层,所以这个4469B8是虚表的地址。

    在这里插入图片描述
    才找到函数主体

    而且我们要看的是返回值,因为只有返回值才会赋值给我们的函数指针,并非看[eax],而是看eax,
    在这里插入图片描述
    执行完.text:00410EFF mov eax, [ebp+arg_0]
    发现eax里面的值是0x64ff18,看了一下也就是指针的地址。。

    在这里插入图片描述
    (就是从函数外面把函数指针的地址当做参数传进去,然后把虚表地址 放在指针里面,也就是把它当做虚表指针来使用。。记住,这个指针里面存放的不是函数的地址,而是虚表地址。)然后指针里面的值存放的是虚表的地址,虚表里面又放着虚函数的地址。
    在这里插入图片描述
    edx里面存放的是虚表的地址,然后再进行取内容,即取到了函数的地址,作为参数传过去。至于这加减操作,也就是虚表里面不一定只存放着一个虚函数的地址。。因为eax存放着偏移+1值,这里eax刚开始是既可以用作判断跳转,又可以当虚表偏移,所以加上eax值后必须自减1,回到正确的偏移

    近一步解释

    c++代码

    #include<iostream>
    using namespace std;
    
    class a {
    	
    public:
    	virtual int add(int a, int b) {
    		return a + b;
    	}
    	virtual int decrease(int a, int b) {
    		return a - b;
    	}
    };
    
    typedef int (a::* pClassFun)(int, int);
    
    
    int main() {
    
    	pClassFun pointer = &a::add;
    	a  aa;
    	cout << (aa.*pointer)(10, 20);
    	
    	pClassFun pointer1 = &a::decrease;
    	cout << (aa.*pointer1)(20, 10);
    }
    
    

    ida调试

    加了一个虚函数,加了一个函数指针指向它,接下来我们来看看是不是另外一个虚函数是不是紧接着放在虚表的中的第一个虚函数地址后面。如果是的话,那么另外一个函数指针就直接没用了,它直接通过第一个函数指针值加一个偏移再取内容就ok啦!

    在这里插入图片描述
    第一部分和上面一样。

    在这里插入图片描述
    第二部分是不是没有去再一次寻址了,也就是没有下面这个call

    在这里插入图片描述

    eax存放的是偏移加1值,然后一个指针是4个字节,第一个指针的偏移是0 ~ 3,第二个也就是4 ~ 7,eax存放起始偏移+1,所以也就是5

    在这里插入图片描述
    这里的意思不就是取出虚表地址,再取出函数地址相应的偏移,然后再取一次内容吗?(也就是取函数的地址)
    在这里插入图片描述

    注意

    dec是自减,也就是加上了5,但是偏移是4,所以减1,即第二个指针!!!因为eax存放着偏移+1值,这里eax刚开始是既可以用作判断跳转,又可以当虚表偏移,所以加上eax值后必须自减1,回到正确的偏移

    展开全文
  • c++类成员函数做函数参数

    千次阅读 2020-10-26 17:47:29
    内部的typedef

    类内部的typedef函数声明,属于类成员,在类外声明时必须加类声明作用域(Test::FUNC),且赋值的函数必须为类成员(&Test::Func1)

    下面的类中,Func1和Func2的返回值类型和参数列表都一样,定义的FUNC类似委托类型

    Test.h

    #pragma once
    #include<iostream>
    using namespace std;
    class Test
    {
    public:
    	typedef void (Test::*FUNC)(int);
    	Test();
    	~Test();
    	void Func(FUNC f, int x);
    	void Func1(int x);
    	void Func2(int x);
    };
    
    

    Test.cpp

    #include "Test.h"
    
    
    Test::Test()
    {
    }
    
    
    Test::~Test()
    {
    }
    
    void Test::Func(FUNC f, int x)
    {
    	(this->*f)(x);
    }
    
    void Test::Func1(int x)
    {
    	cout << "Func1:" << x<<endl;
    }
    
    void Test::Func2(int x)
    {
    	cout << "Func2:" << x << endl;;
    }
    
    

    源.cpp

    #include"Test.h"
    int main()
    {
    	Test test;
    	test.Func(&Test::Func1, 1);
    	test.Func(&Test::Func2, 2);
    
    	system("pause");
    }
    

    运行结果

    在这里插入图片描述

    参考文献

    展开全文
  • C++ 类成员函数调用

    千次阅读 2019-04-21 22:39:49
    今天看了一篇写得很好的博客:类成员函数调用分析 看完后,有一些小的心得,大概是帮我理了一遍吧。 全局变量和static变量存放在静态存储区。 与静态成员变量和非静态成员变量不同的是:静态成员函数和非静态...
  • C++类成员函数后面加const

    千次阅读 2020-05-31 18:48:31
        如果成员函数后面加了const关键字,说明这个函数是不能改变中的成员变量的。 如果在编写该函数会修改中的成员变量,编译时会出错,并且也提高了程序的可读性,当我们看到函数后面有const的话就知道这...
  • c++ 类成员函数地址。

    千次阅读 2018-04-10 10:48:32
    类成员函数类型声明和变量定义c++ 成员函数地址声明方式和定义如下:类型声明:typedef void (MyClass::*pFunType)(); 或using pFunType2 = void (MyClass::*)();变量定义:void (MyClass::*p1)();例子:class ...
  • C++类成员函数

    千次阅读 2021-06-08 10:00:48
    1 C++类成员函数 1.1 C++类成员函数的性质 成员函数是函数的一种,它的用法和作用和一般函数基本上是一样的,它也有返回值和函数类型。它与一般函数的区别只是:它是属于一个的成员,出现在体中。它...
  • C++类成员函数作为线程回调函数

    千次阅读 2016-07-28 22:47:06
    C++类成员函数作为回调函数的方法
  • C++类成员函数转换成函数对象

    千次阅读 2016-09-15 19:39:27
    C++中,成员函数(member_function)通常不能直接作为函数对象来使用,最常见的就是创建线程时,不能使用非静态的成员函数来初始化一个线程。 这个主要是因为没有传入this指针,而下面的转换或者绑定,本质是将...
  • C++成员函数作为回调函数使用介绍
  • C++类成员函数继承

    千次阅读 2019-05-20 00:38:23
    对于父类函数(virtual、非virutal),子类有同名函数,无同型函数,则不能调用父类函数 3 . 对于父类函数(virtual、非virtual),如果有同型函数: 非virtual函数由指针类型决定调用哪个 virtual函数由指针指向的...
  • C++类成员函数的访问权限

    千次阅读 2019-09-24 20:31:41
    C++类成员函数的访问权限类成员函数对main函数变量的访问权限之间变量的重复定义 类成员函数对main函数变量的访问权限 main函数本质也是函数,类成员函数只能访问全局变量和中的变量,不能访问main函数里面的...
  • C++类成员函数的this参数

    千次阅读 2020-08-06 18:04:26
    C++中,如果某个成员函数中使用了该的成员(成员变量或成员函数),则该成员函数会通过一个名为this的隐式参数来访问这个成员(成员变量或成员函数)。 例如,有一个 classmyClass() { public: intm_i...
  • 众所周知,的私有变量是无法在外直接访问的,只能通过成员函数访问。 且看下面一段代码: class Stock { private: double total_val;//这是私有的哦~ public: Stock();//默认构造函数 Stock(const ...
  • > #include "stdafx.h" #include #include #include using namespace std;...最后我不得已把run1(),run2(),run()全部声明为static,还是报错:"Severity Code Description Project File Line ...
  • c++类的默认成员函数

    千次阅读 2019-05-14 21:23:08
    这些默认的类成员函数, 在创建的时候, 编译器会自动调用, 但是自动调用并不意味着他们能完成用户所期待的所有任务, 像构造函数, 析构函数, 拷贝构造函数, 赋值重载会在某些情况下需要用户根据程序的需要自定义实现...
  • C++ Thread使用类成员函数

    千次阅读 2021-03-25 12:08:19
    C++ Thread使用类成员函数示例代码编译运行结果 示例代码 #include <thread> #include <iostream> using std::cout; using std::endl; using std::thread; class Job { public: Job(int m) : _m(m){}...
  • c++类成员函数

    万次阅读 2009-09-25 17:04:00
    c++的两大特色是多态和模板。其中多态是通过继承和虚函数来实现的,其中虚...上面一段话很多人都知道,但是如果问普通成员函数,编译器是怎么找到它的入口地址的呢?也就是说,怎么进行调用?为什么A一个foo函数和B
  • 自己总结的如何在多线程中调用c++成员函数和使用c++中标量
  • c++类成员函数作为线程函数

    千次阅读 2018-12-22 12:19:10
    参考:... ... 当我们创建线程的时候 线程处理函数如果是里面的成员函数会报错,需要以下处理。 当需要利用类成员函数( MyClass::thread_func )来创建子线程时,需如下...
  • 在继承之外,在C++中一个类成员函数调用另一个成员的方法主要有:的组合,友元的前向声明,单例模式等,下面主要讲讲这4种方法的实现 方法1:利用的组合 组合通俗来讲就是B有A的属性,如声明一个...
  • C++类成员函数可以访问同类不同对象的私有成员

    千次阅读 多人点赞 2019-01-29 20:38:25
    一个成员函数可以访问这个的私有数据成员,我们要理解这个对的访问限制,而不是针对对象。 C++的访问控制是层面的 class-level , 而不是对象级别的 object-level , 同一个可以访问所有自己实例...
  • C++类成员函数使用时,都会隐式传递一个this指针给该函数,this指针指向该的对象。函数体可以通过显示调用该指针或直接访问内成员。 回调函数是通过指针调用的函数,最常使用的回调函数就是在创建线程时,以一...
  • c++ 多线程调用类成员函数方法

    千次阅读 2021-04-29 16:31:27
    main.cpp #include <iostream> #include <thread> using namespace std; class print { public:... // 传入顺序 线程函数,实例化指针,函数参数 thread t1(&print::printHello,&pt,"dabin"); t1.join(); return 0; }
  • C++调用类成员函数

    万次阅读 2017-12-25 19:41:31
    #ifndef _CLASS_H_ #define _CLASS_H_ class CanExtTxPDO_t { private: unsigned char i; public: unsigned char buffer[10];... CanExtTxPDO_t(unsigned char arg0, unsigned char arg1);... ~CanExtTxPDO_t(v
  • C++类成员函数作为回调函数

    千次阅读 2019-04-16 19:15:24
    普通的C++成员函数都隐含了一个传递函数作为参数,亦即“this”指针,C++通过传递一个指向自身的指针给其成员函数从而实现程序函数可以访问C++的数据成员。这也可以理解为什么C++的多个实例可以共享成员函...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 451,799
精华内容 180,719
关键字:

c++类的成员函数

c++ 订阅
友情链接: esprit算法自编.rar