C++指向类成员函数的指针详细解析
发布时间:2022-03-05 04:50:28
标签:类成员函数,指针,c++
首先 函数指针是指向一组同类型的函数的指针;而类成员函数我们也可以相似的认为,它是指向同类中同一组类型的成员函数的指针,当然这里的成员函数更准确的讲应该是指非静态的成员函数。前者是直接指向函数地址的,而后者我们从字面上也可以知道 它肯定是跟类和对象有着关系的。
函数指针实例:
typedef int (*p)(int,int);//定义一个接受两个int型且返回int型变量的函数指针类型
int func(int x,int y)
{
printf("func:x=%d,y=%d/n",x,y);
return (x<y?x:y);
}
int main()
{
p fun=func;//定义函数指针并给它赋上一个函数指针
cout<<"min:"<<(*fun)(4,5)<<endl;//为什么*fun需要用()扩起来呢?因为*的运算符优先级比()低,如果不用()就成了*(fun())
return 0;
}
而“指向类成员函数的指针”却多了一个类的区别:
class A
{
public:
int func(int x,int y)
{
printf("A::func:x=%d,y=%d/n",x,y);
return (x<y?x:y);
}
};
typedef int (A::*p)(int,int);//指针名前一定要加上所属类型类名 A::的限定
int main()
{
p fun=&A::func;
A a; //因为成员函数地址的解引用必须要附驻与某个对象的地址,所以我们必须创建某个对象。
cout<<"min:"<<(a.*fun)(4,5)<<endl;
return 0;
}
嘿嘿。。只是用起来 .* 感觉怪怪滴。
接下来 我们可以再扩展一下下:
#include <tchar.h>
#include <iostream>
#include <stdio.h>
using namespace std;
class A
{
public:
int func1(int x,int y)
{
printf("A::func:x=%d,y=%d/n",x,y);
return (x<y?x:y);
}
virtual int func2(int x,int y)
{
printf("A::func:x=%d,y=%d/n",x,y);
return (x>y?x:y);
}
};
class B:public A
{
public:
virtual int func2(int x,int y)
{
printf("B::func:x=%d,y=%d/n",x,y);
return (x+y);
}
};
typedef int (A::*p)(int,int);//指针名前一定要加上所属类型类名 A::的限定
typedef int (B::*p0)(int,int);
int main()
{
A a; //因为成员函数地址的解引用必须要附驻与某个对象的地址,所以我们必须创建某个对象。
p fun=&A::func1;
cout<<(a.*fun)(4,5)<<endl;
cout<<(b.*fun)(4,5)<<endl<<endl;
fun=&A::func2;
cout<<(a.*fun)(4,5)<<endl;//请注意这里调用的是虚函数,嘿嘿 还真神奇 类成员函数指针也支持多态。
cout<<(b.*fun)(4,5)<<endl<<endl;
//fun=&B::func2; //这样式错误滴,因为不存在派生类的"指向类成员函数的指针"到基类的"指向类成员函数的指针"的隐式转换
fun=(int (A::*)(int,int))&B::func2;//应该进行强制转换
cout<<(a.*fun)(4,5)<<endl;
cout<<(b.*fun)(4,5)<<endl<<endl;
p0 fun0=&B::func2;
cout<<(a.*fun)(4,5)<<endl;
cout<<(b.*fun)(4,5)<<endl<<endl;
fun0=&A::func2; //正确,因为这里进行了隐式转换
cout<<(a.*fun)(4,5)<<endl;
cout<<(b.*fun)(4,5)<<endl<<endl;
//从上面我们不难发现 指向类成员函数的指针基类和派生类的关系和指向类对象的指针基类和派生类的关系完全相反,
//基类成员函数的布局被认为是派生类成员函数布局的一个子集
return 0;
}
接下 是有关模板类的类成员函数指针的使用
实例如下:
#include <tchar.h>
#include <iostream>
#include <stdio.h>
using namespace std;
class A
{
public:
int func(int x,int y)
{
printf("A::func : x=%d,y=%d/n",x,y);
return (x<y?x:y);
}
};
class B
{
public:
int func(int x,int y)
{
printf("B::func : x=%d,y=%d/n",x,y);
return (x>y?x:y);
}
};
template<class T>
class C
{
public:
T c;
void Print()
{
int (T::*p)(int,int)=&T::func;
(c.*p)(4,5);
}
};
int main()
{
C<A> ca;
C<B> cb;
ca.Print();
cb.Print();
return 0;
}
从上面 可以很清晰地看到。。其实它和普通的模板没有什么区别。。只不过将限定名称该为参数名酒OK啦。。。
嘿嘿。。。
0
投稿
猜你喜欢
- 本文实例讲述了Android开发获取重力加速度和磁场强度的方法。分享给大家供大家参考,具体如下:Android获取重力加速度和磁场强度主要依
- 重载1.构造器的重载因为构造器的名字必须与类名相同,所以同一个类的所有构造器名肯定相同,构成重载;为了让系统能区分不同的构造器,多个构造器的
- 引言 批量下载思路,首先前端发送下载请求,携带必要参数,
- 以前只知道控件的onTouchEvent()事件,它的动作有MotionEvent.ACTION_DOWN、MotionEvent.ACTI
- 在 C# WPF开发中颜色对话框控件(ColorDialog)用于对界面中的背景、文字…(拥有颜色属性的所有控件)设置
- 本文实例讲述了WinForm实现自定义右下角提示效果的方法。分享给大家供大家参考。具体实现方法如下:using System;using S
- 这篇文章主要介绍了springmvc处理模型数据Map过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,
- 初学者,照着书上的抄袭制作,但已经理解了里面的意思和应用,并且进行了稍微改善和异常捕捉。这里记录下,以防以后用到这方面的知识点。窗体设计:c
- android Launcher3 设置默认桌面应用,供大家参考,具体内容如下launcher3 的默认桌面应用是在 res/xml里 默认
- 概念:有enum关键字修饰的类,成为枚举类1、枚举规则枚举类的对象可以有类里面定义,不支持重新new出来,枚举类有构造函数,其他的类都一样,
- 之前写过一篇 Java 线程池的使用介绍文章《线程池全面解析》,全面介绍了什么是线程池、线程池核心类、线程池工作流程、线程池分类、拒绝策略、
- 这篇文章主要介绍了Java String的intern用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,
- JDK8中有双冒号的用法,就是把方法当做参数传到stream内部,使stream的每个元素都传入到该方法里面执行一下。代码其实很简单:以前的
- 1、switch支持String做参数/*** * switch支持字符串做参数 jdk7 * @author huangjiawei */
- RestTemplate 401错误调用第三方api 若是服务返回状态码不为200,默认会执行DefaultResponseErrorHan
- Web Services 可以将应用程序转换为网络应用程序。通过使用 Web Services,您的应用程序可以向全世界发布信息,或提供某项
- /// <summary> /// 读写INI文件的类。
- 高并发下restTemplate的错误分析1. 问题现象和分析org.apache.http.conn.ConnectionPoolTime
- 前言倒计时功能在游戏中一直很重要, 不管是活动开放时间,还是技能冷却。 本文实现了一个通用倒计时组件,实现了倒计时的基本功能,支持倒计时结束
- 一、前言随着互联网项目前后端分离方式的流行,前端与后端交给不同的人员开发,项目沟通成本也随之提高。主要表现在WebAPI接口的沟通,Swag