关于C++中全局函数和静态成员函数的疑问

Really_want 2015-01-13 03:14:48

// 关于C++中全局函数和静态成员函数的疑问

// 例如这样写:
class Kity //类名没有意义
{
static void fun(); //静态成员函数
};
void foo() { } //全局函数
typedef void (*FUNPTR)();

int main()
{
FUNPTR pfn1 = foo;
FUNPTR pfn2 = (FUNPTR)Kity::fun; //总感觉这里出了点什么问题,但又不清不楚的。多希望有人能点到本质上来啊。
// FUNPTR pfn2 = (FUNPTR)&Kity::fun;
return 0;
}

...全文
697 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
lm_whales 2015-01-21
  • 打赏
  • 举报
回复
静态函数属于类,是类的成员函数,只是不从属与对象, 不需要创建任何该类的对象,就可以使用 成员指针,是用来指向对象的成员的,所以成员函数指针,指向的是非静态成员函数 成员变量指针,指向的是非静态成员变量 普通函数,性质就等同于C的函数,是整个文件范围的,如果它还是个非静态函数, 可以用extern 声明一般本文件以外的程序也可以引用 函数指针,不区分外部函数和静态函数,也不管函数是否 类的静态函数 函数指针只需要一个函数的地址数据值,就可以引用函数了。 所以实际上,他绕过了 函数定义的作用域啊,内部链接,外部链接这些限制 实际上,如果 this 指针,不做特殊处理的话, 而如果你,可以获得 类的非静态成员函数的地址, 那么你也可用函数指针,多加一个参数,调用类的非静态函数 只是C++ 对 非静态函数的实现方式,没有具体规定 而VC等编译器,对 this 指针这个参数做了特殊处理,(通过寄存器 ecx 传递,函数参数则通过 堆栈传递) 而且C++不可能通过简单的 C,C++语法就获得非静态函数的地址 ,,,,主要是每个编译器的实现方式不同,没有统一的方法, 所以干脆就彻底不作规定, 并且这种做法也破坏了C++面向对象特性的封装性 所以C++并不提供方法,实现获取非静态函数地址的方法
Really_want 2015-01-19
  • 打赏
  • 举报
回复
引用 19 楼 lm_whales 的回复:
6~7楼,你表达的很不准确,
对于静态函数,类外部,必须采用类名引用方式才能得到函数指针,或者调用函数
对于 非静态函数,成员函数指针,是成员指针的一种,
是一种特殊的指针,很多指针语法不适用成员指针。
成员指针,只有通过对象,才能引用,
因为编译器,不可能无中生有的创造一个,

程序员没有指定的对象,
来调用成员函数,或者读写成员变量的值。

编译器不知道,你用谁的成员指针,
做事情,所以必须指定对象。
才能使用成员指针
另外,对于C++你不可能,直接用C或者C++的方法,取得成员函数的地址。
你唯一可能的方式,
是通过调试或者,查看反汇编,以及查看目标代码,
等手段,来得到非静态函数的地址。

成员指针,丑陋,离奇,但是必须这么用
成员指针,包括成员函数指针,成员变量指针

例如

class C{
    public:
C(int x=0,int y=0,int z=0):a(x),b(y),c(z){};
int  a,b,c;

int fun_x(){cout <<"fun_x:a="<<a<<endl;return 0;};
int fun_y(){cout <<"fun_y:b="<<b<<endl;return 0;};
int fun_z(){cout <<"fun_z:c="<<c<<endl;return 0;};
static int fun_static(){cout <<"static " <<endl;return 0;};
};

///成员变量指针,是一种非独立数据,必须配合 对象,才能指向 对象的 成员变量.
///成员函数指针,同样是一种非独立数据,必须配合对象,成员函数指针,才有意义
///(通过对象,才能传递 隐含参数 this 这个指针,给函数)

int (&rfs)() = C::fun_static; //C++ 函数可以有引用,类静态函数也可以有引用
int (*pfs_0)() = &C::fun_static;
int (*pfs)() = C::fun_static; ///类静态函数,和类外定义的函数的差别,只是访问方式和,保护属性不同。作用域不同
                           ///所以指向它的指针,不需要成员(函数)指针,只需普通函数指针就可以了
int (C::*pfx)() =&C::fun_x; ///非静态成员函数,需要成员(函数)指针,普通函数指针不可以
int C::*pa =&C::a;     ///成员变量指针

int main()
{

    pfs();
    C c(1,2,3);
    C &r= c;
    C *p= &c;

   cout<<"obj:"; (c.*pfx)();
   cout<<"ref:"; (r.*pfx)();
   cout<<"ptr:"; (p->*pfx)();
    pfx =&C::fun_y;

   cout<<"obj:"; (c.*pfx)();
   cout<<"ref:"; (r.*pfx)();
   cout<<"ptr:"; (p->*pfx)();

    pfx =&C::fun_z;

   cout<<"obj:"; (c.*pfx)();
   cout<<"ref:"; (r.*pfx)();
   cout<<"ptr:"; (p->*pfx)();
    cout <<c.*pa<<endl;
    pa =&C::b;
    cout <<c.*pa<<endl;;
    pa =&C::c;
    cout <<c.*pa<<endl;;
    cout <<"staic member fun ref:";rfs();
    cout<<"staic member fun ptr unuse &:";pfs();
    cout<<"staic member fun ptr use &:";pfs_0();
    cout << "end!" << endl;

    return 0;
}
输出 
static
obj:fun_x:a=1
ref:fun_x:a=1
ptr:fun_x:a=1
obj:fun_y:b=2
ref:fun_y:b=2
ptr:fun_y:b=2
obj:fun_z:c=3
ref:fun_z:c=3
ptr:fun_z:c=3
1
2
3
staic member fun ref:static
staic member fun ptr unuse &:static
staic member fun ptr use &:static
end!
很丰富的关于 普通成员 和 静态成员 的解说,也讲得非常到位。可是关于静态函数和普通函数(全局函数)的区别,我更关心这个,我只看到这两句: ///类静态函数,和类外定义的函数的差别,只是访问方式和,保护属性不同。作用域不同 ///所以指向它的指针,不需要成员(函数)指针,只需普通函数指针就可以了 int (*pfs)() = C::fun_static; 我还在思考“类静态函数,和类外定义的函数的差别,只是访问方式和,保护属性不同。”这句话的引出的一些“为什么”。
lm_whales 2015-01-19
  • 打赏
  • 举报
回复
6~7楼,你表达的很不准确,
对于静态函数,类外部,必须采用类名引用方式才能得到函数指针,或者调用函数
对于 非静态函数,成员函数指针,是成员指针的一种,
是一种特殊的指针,很多指针语法不适用成员指针。
成员指针,只有通过对象,才能引用,
因为编译器,不可能无中生有的创造一个,

程序员没有指定的对象,
来调用成员函数,或者读写成员变量的值。

编译器不知道,你用谁的成员指针,
做事情,所以必须指定对象。
才能使用成员指针
另外,对于C++你不可能,直接用C或者C++的方法,取得成员函数的地址。
你唯一可能的方式,
是通过调试或者,查看反汇编,以及查看目标代码,
等手段,来得到非静态函数的地址。

成员指针,丑陋,离奇,但是必须这么用
成员指针,包括成员函数指针,成员变量指针

例如

class C{
    public:
C(int x=0,int y=0,int z=0):a(x),b(y),c(z){};
int  a,b,c;

int fun_x(){cout <<"fun_x:a="<<a<<endl;return 0;};
int fun_y(){cout <<"fun_y:b="<<b<<endl;return 0;};
int fun_z(){cout <<"fun_z:c="<<c<<endl;return 0;};
static int fun_static(){cout <<"static " <<endl;return 0;};
};

///成员变量指针,是一种非独立数据,必须配合 对象,才能指向 对象的 成员变量.
///成员函数指针,同样是一种非独立数据,必须配合对象,成员函数指针,才有意义
///(通过对象,才能传递 隐含参数 this 这个指针,给函数)

int (&rfs)() = C::fun_static; //C++ 函数可以有引用,类静态函数也可以有引用
int (*pfs_0)() = &C::fun_static;
int (*pfs)() = C::fun_static; ///类静态函数,和类外定义的函数的差别,只是访问方式和,保护属性不同。作用域不同
                           ///所以指向它的指针,不需要成员(函数)指针,只需普通函数指针就可以了
int (C::*pfx)() =&C::fun_x; ///非静态成员函数,需要成员(函数)指针,普通函数指针不可以
int C::*pa =&C::a;     ///成员变量指针

int main()
{

    pfs();
    C c(1,2,3);
    C &r= c;
    C *p= &c;

   cout<<"obj:"; (c.*pfx)();
   cout<<"ref:"; (r.*pfx)();
   cout<<"ptr:"; (p->*pfx)();
    pfx =&C::fun_y;

   cout<<"obj:"; (c.*pfx)();
   cout<<"ref:"; (r.*pfx)();
   cout<<"ptr:"; (p->*pfx)();

    pfx =&C::fun_z;

   cout<<"obj:"; (c.*pfx)();
   cout<<"ref:"; (r.*pfx)();
   cout<<"ptr:"; (p->*pfx)();
    cout <<c.*pa<<endl;
    pa =&C::b;
    cout <<c.*pa<<endl;;
    pa =&C::c;
    cout <<c.*pa<<endl;;
    cout <<"staic member fun ref:";rfs();
    cout<<"staic member fun ptr unuse &:";pfs();
    cout<<"staic member fun ptr use &:";pfs_0();
    cout << "end!" << endl;

    return 0;
}
输出 
static
obj:fun_x:a=1
ref:fun_x:a=1
ptr:fun_x:a=1
obj:fun_y:b=2
ref:fun_y:b=2
ptr:fun_y:b=2
obj:fun_z:c=3
ref:fun_z:c=3
ptr:fun_z:c=3
1
2
3
staic member fun ref:static
staic member fun ptr unuse &:static
staic member fun ptr use &:static
end!
bravery36 2015-01-16
  • 打赏
  • 举报
回复
类的static函数主要是影响了函数的名字而已,类型和全局那个是一样的,你写的Kity::fun本身就是代表函数的名字,没有什么特别的。
我看你有戏 2015-01-16
  • 打赏
  • 举报
回复

class Kity  
{
public:
    static void fun()
	{

	}
};
改成这样 看下这篇文章 一般函数指针和类的成员函数指针 http://www.cnblogs.com/xianyunhe/archive/2011/11/26/2264709.html
Really_want 2015-01-16
  • 打赏
  • 举报
回复
引用 11 楼 lm_whales 的回复:
C++ 允许函数名前面加个 &表示函数地址。 你可能认为 有&的才是正确的, 但是不加& 的更原始,那是(兼容C)的方式。 其实 如果不需要修改 指针的内容, 可以用引用代替指针
谢谢,请抬头看6、7楼。
Really_want 2015-01-16
  • 打赏
  • 举报
回复
引用 13 楼 henry3695 的回复:

class Kity  
{
public:
    static void fun()
	{

	}
};
改成这样 看下这篇文章 一般函数指针和类的成员函数指针 http://www.cnblogs.com/xianyunhe/archive/2011/11/26/2264709.html
谢谢,文章我仔细看了。确实有不少可以值得学习的地方。但 /*指向一般函数的指针*/ typedef int (*pGeneralFun)(int, int); /*类外部的接口函数,实现对类的静态成员函数的封装*/ int GeneralResult(pGeneralFun fun, int a, int b) { return (*fun)(a, b); } cout<<"The sum of a and b is "<<GeneralResult(CA::Sum, a, b)<<endl; 这最后一行显然CA::Sum不是pGeneralFun类型。 也相当于把CA::Sum这个函数强转成了pGeneralFun类型。只是悄悄地做了而已!
Really_want 2015-01-16
  • 打赏
  • 举报
回复
引用 15 楼 wuhailin1987 的回复:

class Kity 
{
    static void fun(); //这个函数 有命名空间限制的,只可以在   Kity  类中被调用
};
void foo() { }  //全局函数 --- 这个是任何地方都可以调用的
有用。+1
Really_want 2015-01-16
  • 打赏
  • 举报
回复
引用 14 楼 bravery36 的回复:
类的static函数主要是影响了函数的名字而已,类型和全局那个是一样的,你写的Kity::fun本身就是代表函数的名字,没有什么特别的。
你说的“Kity::fun本身就是代表函数的名字”,这个可以理解,可以在汇编中看到函数被加了一些额外的字符;可“类型和全局那个是一样的”就有点儿,不解了。 1. 如果说类型相同,那他们的函数类型都是void (*)()吗? 2. 如果是一样的,那为什么还要强转呢?
豆浆爱蟹蟹 2015-01-16
  • 打赏
  • 举报
回复

class Kity 
{
    static void fun(); //这个函数 有命名空间限制的,只可以在   Kity  类中被调用
};
void foo() { }  //全局函数 --- 这个是任何地方都可以调用的
lm_whales 2015-01-15
  • 打赏
  • 举报
回复
C++ 允许函数名前面加个 &表示函数地址。 你可能认为 有&的才是正确的, 但是不加& 的更原始,那是(兼容C)的方式。 其实 如果不需要修改 指针的内容, 可以用引用代替指针
lucifer886 2015-01-14
  • 打赏
  • 举报
回复
C++的真正意义上的成员函数在mangling的时候会将成员函数func()的名称编程类似这样的,func(void *),会在成员函数前面填充一个this指针。而static不会,就是直接按照C的方式进行mangling的。 C++里的static成员函数和成员变量你就不要把它当“类成员”去理解,直接当成是C风格的函数和变量就行了(全局),只不过使用时在编译的时候对public、private相关语法作了检测而已。
allenltiverson 2015-01-14
  • 打赏
  • 举报
回复
编译器差异会给你答案
热血打工人 2015-01-14
  • 打赏
  • 举报
回复
static void fun(); //静态成员函数 如果你使用了static静态函数,函数fun()就可以在类的外面使用。 而不会出像栈那样的问题。
Really_want 2015-01-13
  • 打赏
  • 举报
回复
引用 5 楼 ll3826982 的回复:
很明白的告诉你,2种方法都没有问题,是正确的,不过都是地址罢了。
不好意思,让你误解了。重点不是这里有没有'&'符号: FUNPTR pfn2 = (FUNPTR)Kity::fun; 在有的编译器中可以不加"&"号,而在有的编译器则可能必需加上,我写这条注释的本意是想表达,重点不在加不加‘&’符号上。
Really_want 2015-01-13
  • 打赏
  • 举报
回复
我是楼主: 1. 经过这么一句后: FUNPTR pfn2 = (FUNPTR)Kity::fun 可以直接通过pfn2调用原来要用Kity::fun方式才能调用的函数。比如直接写pfn2()而不用写Kity::fun(); 2. Kity::fun如果不用强制类型转换,函数类型应该是void (Kity::*)();可强转了以后成了void (*)(); 一个和类紧密联系(既然声明作为类的成员函数,联系应该是紧密的)的函数,经过强转,成了一个普通函数。 3. 我就是觉得,为什么要强转它呢?在我看来“强转”就好像是“武力”解决问题的方式,肯定有,应该有更合理的方式不是吗?要是没有虚函数,我想用基类指针调用派生类对象的函数也可以将函数指针强转(不考虑安全与否,暂只讨论是否可行): class A{ }; class B: public A { void fb(); } void test(A* p) { ( (B*)p )->fb(); } 很明显,虚函数完全给人一种合理的表现方式。让我感觉很有安全感!而让我用强转,就好像在舞刀弄枪! 4. 所以,FUNPTR pfn2 = (FUNPTR)Kity::fun,这样的方式不知是不是真正的技巧所在。
ll3826982 2015-01-13
  • 打赏
  • 举报
回复
很明白的告诉你,2种方法都没有问题,是正确的,不过都是地址罢了。
lincolnandlinda 2015-01-13
  • 打赏
  • 举报
回复
楼主的疑问到底是什么?
ztenv 版主 2015-01-13
  • 打赏
  • 举报
回复
函数指针,与类函数成员指针,两种不同类型的指针即使函数的参数、返回值一样; 这在c++primer中都有讲的,可以去看看。
赵4老师 2015-01-13
  • 打赏
  • 举报
回复
计算机组成原理→DOS命令→汇编语言→C语言(不包括C++)、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言(包括C++)、架构…… 对学习编程者的忠告: 多用小脑和手,少用大脑、眼睛和嘴,会更快地学会编程! 眼过千遍不如手过一遍! 书看千行不如手敲一行! 手敲千行不如单步一行! 单步源代码千行不如单步Debug版对应汇编一行! 单步Debug版对应汇编千行不如单步Release版对应汇编一行! 单步类的实例“构造”或“复制”或“作为函数参数”或“作为函数返回值返回”或“参加各种运算”或“退出作用域”的语句对应的汇编代码几步后,就会来到该类的“构造函数”或“复制构造函数”或“运算符重载”或“析构函数”对应的C/C++源代码处。 VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。 对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的时候。 (Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。) 想要从本质上理解C指针,必须学习汇编以及C和汇编的对应关系。 从汇编的角度理解和学习C语言的指针,原本看似复杂的东西就会变得非常简单! 指针即地址。“地址又是啥?”“只能从汇编语言和计算机组成原理的角度去解释了。” 但我又不得不承认: 有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物; 而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。 而我本人属前者。 不要企图依赖输出指针相关表达式...的值【比如printf("%p\n",...);或者cout<<...】来理解指针的本质, 而要依赖调试时的反汇编窗口中的C/C++代码【比如void *p=(void *)(...);】及其对应汇编指令以及内存窗口中的内存地址和内存值来理解指针的本质。 这辈子不看内存地址和内存值;只画链表、指针示意图,画堆栈示意图,画各种示意图,甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗?本人深表怀疑! 这辈子不种麦不收麦不将麦粒拿去磨面;只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头,压面条,烤面包,……能从本质上理解面粉、理解面食吗?本人深表怀疑!! 提醒: “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。 (Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。) 想要从本质上理解C指针,必须学习C和汇编的对应关系。” 不是一回事! 不要迷信书、考题、老师、回帖; 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实! 有人说一套做一套,你相信他说的还是相信他做的? 其实严格来说这个世界上古往今来所有人都是说一套做一套,不是吗? 不要写连自己也预测不了结果的代码! 电脑内存或文件内容只是一个一维二进制字节数组及其对应的二进制地址; 人脑才将电脑内存或文件内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息…… 十字链表交换任意两个节点C源代码(C指针应用终极挑战)http://download.csdn.net/detail/zhao4zhong1/5532495
加载更多回复(1)

64,652

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧