类的成员函数指针

 

类的成员函数指针

 

1.在今天看到一个很奇怪的代码,更奇怪的时竟然可以编译通过!:

template<class AT, class BT>

int delay( int ms, BT * target = NULL )

{

    BT * tar;

    //只是指定一个指针,还没初始化

    void (BT::*fn)(int);

    //BT声明一个指针函数

    tar->*fn(0);

    //竟然可以直接调用!

    return 0;

}

 

2.反正就是不好理解呀,那就么自己试试啦!

#include<iostream>

using namespace std;

class C

{

    public:

        C(){}

        int ShowName(int);

};

 

int C::ShowName(int)

{

    cout<<"ShowName in class C!"<<endl;

}

 

我知道函数指针多用于作参数传递,那就写个调用函数

//如果参数写成int (*cfn)(int) 就变成普通函数指针而不是指定类C里的函数指针了

int invokeShowName( int (C::*cfn)(int)  )

{

//方法一,直接调用

    cnf(0);

 

//方法二,以指针方式

    C * p;

    (p->*cfn)(0);

}

 

3.调用方式(先注释 invokeShowName里的调用!)

(1) invokeShowName( C::ShowName );

报错: 对非静态成员函数 ‘int C::ShowName(int)’ 的使用无效

(2)    C * pc = new C();

 

    invokeShowName( pc->ShowName );

报错: 实参类型为 ‘int (C::)(int)’,与 ‘int (C::*)(int)’ 不匹配

 

(3) invokeShowName( &C::ShowName );

竟然OK!! 什么意思嘛, 竟然不通过对象就可以调用到类的非静态成员!!

 

4.  invokeShowName调用

(1) cnf(0)

报错: 必须用 ‘.*’ or ‘->*’ 调用 <cfn (...)’ 中的成员函数指针

 

(2)

    C * p;

      

    (p->*cfn)(0);

 

OK

 

6.来直接点吧!!竟然也可以!!~~~注意从头到尾都没有生成对象,只是有个类指针,按理说应该是没有分配到空间呀,不过当然,类的成员函数已经绑定,就是说函数地址是确定有的,没有对象只是没有复制到用户的栈/堆空间里!

 

    int (C::*ppfn)(int) = &C::ShowName;

    C * p; //只有指针没有对象

    (p->*ppfn)(0);

 

6.完整程序

  1. #include<iostream>
  2. using namespace std;
  3. class C
  4. {
  5.     public:
  6.         C(){}
  7.         int ShowName(int);
  8. };
  9. int C::ShowName(int)
  10. {
  11.     cout<<"ShowName in class C!"<<endl;
  12. }
  13. int invokeShowName( int (C::*cfn)(int)  )
  14. {
  15.     C * p;
  16.     (p->*cfn)(0);
  17. }
  18. template<class AT, class BT>
  19. int delay( int ms, BT * target = NULL )
  20. {
  21.     BT * tar;
  22.     void (BT::*fn)(int);
  23.     tar->*fn(0);
  24.     return 0;
  25. }
  26. int main()
  27. {
  28.     //invokeShowName( C::ShowName );
  29.     //invokeShowName( pc->ShowName );
  30.     invokeShowName( &C::ShowName );
  31.     int (C::*ppfn)(int) = &C::ShowName;
  32.     C * p;
  33.     (p->*ppfn)(0);
  34.     return 0;
  35. }

 

 

 

原来这个是C++里的成员函数提针,呵算是又加深了对C++的理解,详细的分析可以看:

http://topic.youkuaiyun.com/u/20071011/10/D8693A60-B279-4794-AA0A-1E98731DFF79.html

http://dozb.bokee.com/1783751.html

 

测试环境:

ubuntu 7.04( 2.6.20 .17)

gcc 版本 4.1.2

<think>首先,用户的问题是关于“C++成员函数指针使用方法示例”。回顾之前的对话历史:-用户最初问的是关于“名访问静态成员变量”,我详细解释了声明、初始化、访问方法、注意事项和示例。-现在,用户说:“参考站内引用:”,并提供了几个引用,涉及成员函数指针的应用和示例:-引用[1]:展示了声明成员函数指针的示例。-引用[2]:讨论了C++中成员函数指针的使用区别。-引用[3]:展示了指向静态成员函数指针使用,指出它与普通函数指针似。-引用[4]:讨论了如何将的非静态成员函数用作回调函数。-用户明确表示:“我想了解成员函数指针的用法或解决相关问题”。系统指令:-使用LaTeX:行内用`$...$`,独立公式用`$$...$$`。-中文回答。-生成相关问题。-引用段落末尾自然地添加引用标识,例如[^1]。-回答结构清晰,逐步解决问题,保证真实可靠。-在末尾添加相关问题。从引用中提取关键点:-成员函数指针用于指向成员函数。-非静态成员函数指针需要额外的对象实例来调用。-静态成员函数指针可以像普通函数指针一样使用。-有陷阱,如"referencetonon-staticmemberfunctionmustbecalled"错误。我的回答需要:1.**结构清晰**:逐步解释使用方法。2.**提供示例**:包括代码示例。3.**区分静态和非静态**:因为引用[3]强调了区别。4.**添加引用**:自然地引用相关段落,基于用户提供的引用。5.**生成相关问题**:在回答末尾。大纲:-**引言**:简要介绍成员函数指针。-**声明和初始化**:解释如何声明成员函数指针,包括语法。-**使用示例**:提供代码示例:-非静态成员函数指针使用。-静态成员函数指针使用(因为它更简单)。-如何使用回调函数的示例(基于引用[4])。-**注意事项**:比如调用非静态成员函数指针时需要一个对象实例。-**结论**:总结关键点。-**相关问题**:生成2-3个相关问题。确保使用LaTeX:-在需要处添加数学表达式,但由于主题与数学无关,可能只用于强调变量或函数名。-例如,解释指针型时,可以用`$type(*ptr)(params)$`。参考用户提供的引用:-在回答中,自然地插入引用标识。-例如,在解释时引用[^1],[^2],[^3],[^4]。具体步骤:1.**介绍成员函数指针**:解释它用于指向成员函数。2.**语法声明**:使用用户引用[1]的代码作为基础。-示例:`void(MyClass::*ptr)(int)=&MyClass::MyFunction;`3.**非静态成员函数指针**:-调用时需要对象实例。-示例代码展示。4.**静态成员函数指针**:它像普通函数指针。-基于引用[3]的代码。5.**回调函数应用**:讨论如何用于回调(来自引用[4])。6.**注意事项**:-编译错误"referencetonon-staticmemberfunctionmustbecalled"(引用[2])。-确保权限控制(私有成员)。7.**代码示例**:用代码块展示。-使用C++代码。8.**生成相关问题**:-例如:"如何避免成员函数指针的常见错误?"、"静态vs非静态成员函数指针的区别?"、"在实际项目中如何使用成员函数指针?"完整响应结构:-**标题**:C++成员函数指针的使用方法及示例-**正文**:分步骤解释。-**代码示例**:用Markdown代码块。-**注意事项**:列出关键点。-**相关问题**:在结尾。保持中文回答。</think> ### C++ 成员函数指针的使用方法及示例 成员函数指针是C++中用于指向成员函数指针型。它分为**非静态成员函数指针**和**静态成员函数指针**,用法略有不同。以下我将逐步解释其使用方法、提供代码示例,并讨论注意事项。本内容基于C++标准,参考您提供的引用材料[^1][^2][^3][^4]。 --- ### 🔑 1. 声明和初始化成员函数指针 在C++中,声明成员函数指针需要使用特定语法:`ReturnType (ClassName::*ptr)(Params)`,其中 `ptr` 是指针名, `ReturnType` 是函数返回型, `Params` 是函数参数表。这与普通函数指针不同,因为它需要名限定作用域。 - **非静态成员函数指针**:需要绑定到对象实例上调用。声明时指定名,如 `void (MyClass::*ptr)(int)`。 - **静态成员函数指针**:与普通函数指针似,因为静态成员函数不依赖对象实例。声明时为 `ReturnType (*ptr)(Params)`,不需要名限定。 参考引用[^1]的代码: ```cpp class MyClass { public: void MyFunction(int x) { /* 非静态成员函数 */ } static void StaticFunction(int x) { /* 静态成员函数 */ } }; int main() { void (MyClass::*nonStaticPtr)(int) = &MyClass::MyFunction; // 声明非静态成员函数指针 void (*staticPtr)(int) = &MyClass::StaticFunction; // 声明静态成员函数指针 return 0; } ``` #### 要点: - 初始化时,使用 `&ClassName::FunctionName` 获取函数地址。静态成员函数指针可直接赋值,不需要名作用域。 - 编译错误常见于非静态指针的错误初始化,如未指定对象调用时会出现 "reference to non-static member function must be called"[^2]。 --- ### 🖥 2. 使用示例:非静态成员函数指针 调用非静态成员函数指针时,必须先有一个对象实例,并通过对象指针或对象引用结合特殊语法调用。格式为:`(object.*ptr)(args)` 或 `(objectPtr->*ptr)(args)`。 #### 完整代码示例 ```cpp #include <iostream> class Calculator { public: int add(int a, int b) { return a + b; } // 非静态成员函数 }; int main() { Calculator calc; // 创建对象实例 Calculator* calcPtr = &calc; // 对象指针 // 声明并初始化指针 int (Calculator::*funcPtr)(int, int) = &Calculator::add; // 调用:通过对象实例 int result1 = (calc.*funcPtr)(2, 3); // 输出: 5 // 调用:通过对象指针 int result2 = (calcPtr->*funcPtr)(4, 5); // 输出: 9 std::cout << "result1: " << result1 << ", result2: " << result2 << std::endl; return 0; } ``` #### 解释: - 这里 `funcPtr` 指向 `Calculator::add`,调用时必须使用 `.*` 或 `->*` 运算符与对象关联。 - 如果没有对象实例,试图调用 `funcPtr` 会导致编译错误,如引用[^2]所述。 --- ### 🖥 3. 使用示例:静态成员函数指针 静态成员函数指针似于普通函数指针,因为它不依赖对象实例。直接使用指针名调用即可:`ptr(args)`。 #### 完整代码示例(参考引用[^3]) ```cpp #include <iostream> class Math { public: static int multiply(int a, int b) { return a * b; } // 静态成员函数 }; int main() { // 声明并初始化指针 int (*funcPtr)(int, int) = &Math::multiply; // 直接调用,无需对象 int result = funcPtr(3, 4); // 等同于 Math::multiply(3, 4) std::cout << "乘法结果: " << result << std::endl; // 输出: 12 return 0; } ``` #### 解释: - 静态成员函数指针 `funcPtr` 的声明和调用方式与普通函数指针一致 ($funcPtr \equiv \text{普通函数指针}$)。 - 这指针常用于回调函数场景,因为不需要管理对象生命周期,适合解耦设计[^3]。 --- ### 🔄 4. 使用示例:成员函数指针作为回调函数 非静态成员函数指针需要特殊处理才能用于回调,因为标准函数指针型不能直接绑定非静态成员。解决方法是使用**包装器**或**lambda函数**。 #### 完整代码示例(基于引用[^4]) ```cpp #include <iostream> #include <functional> // 用于std::function和std::bind class EventHandler { public: void onEvent(int data) { std::cout << "事件处理: " << data << std::endl; } }; // 回调函数型定义 using Callback = std::function<void(int)>; void triggerEvent(Callback callback, int value) { callback(value); // 调用回调 } int main() { EventHandler handler; // 方法1: 使用std::bind包装非静态成员函数指针 Callback callback1 = std::bind(&EventHandler::onEvent, &handler, std::placeholders::_1); // 方法2: 使用lambda表达式捕获对象 Callback callback2 = [&handler](int data) { handler.onEvent(data); }; triggerEvent(callback1, 100); // 输出: 事件处理: 100 triggerEvent(callback2, 200); // 输出: 事件处理: 200 return 0; } ``` #### 解释: - `std::bind` 或 `lambda` 可以将对象实例与成员函数指针绑定,转换为可调用型 ($\text{Callback}$)。 - 这种方法避免了直接传递非静态指针的错误,适合事件驱动或接口编程[^4]。 --- ### ⚠️ 5. 注意事项和常见问题 1. **非静态指针调用要求**: 必须提供一个有效的对象实例,否则导致编译错误 "reference to non-static member function must be called"[^2]。 2. **访问权限**: 指针只能访问公有成员函数。私有函数无法通过指针外部访问 ($\text{ClassName::*ptr}$ 受权限约束)。 3. **性能影响**: 调用非静态指针可能引入间接开销,但在现代编译器中优化良好。 4. **静态 vs 非静态区别**: - 非静态指针:需要对象,指针型为 `ReturnType (ClassName::*)(Params)`。 - 静态指针:无需对象,指针型为 `ReturnType (*)(Params)`[^3]。 5. **多线程风险**: 在多线程环境使用对象指针时,需确保对象生命周期安全。 6. **编译器兼容性**: 所有现代C++编译器(GCC、Clang、MSVC)均支持,但初始化语法需准确。 --- ### 💎 总结 - **成员函数指针**提供灵活指向成员函数的能力,但必须区分静态与非静态型。 - 使用时: - 非静态指针:用 `(object.*ptr)` 语法调用。 - 静态指针:像普通函数指针调用。 - 回调场景:使用 `std::bind` 或 `lambda` 包装。 - 实际应用包括事件处理、插件系统等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值