• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C++成员函数指针错误用法警示

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

      今天做一个成绩管理系统的并发引擎,用Qt做的,仿照QtConcurrent搞了个模板基类。这里为了隐藏细节,隔离变化,把并发的东西全部包含在模板基类中。子类只需注册需要并发执行的入口函数即可在单独线程中执行。最终目标是,继承的业务逻辑类外部调用时有两个接口可选,调用syncRun同步执行;调用由引擎自动生成的asyncRun就异步执行。最终自动生成asyncRun的模板基类没能实现,主要原因是mingw对this处理的太有问题了!!原本以为编译器问题,后来才知道成员函数指针和this指针如此特殊,对此篇文章反感者请移步文章末尾直接看好文。

/*!
\author LiuBao
\date 2011/3/8
\brief 汇集了各种成员函数指针的错误用法
*/
#include <iostream>
#include <conio.h>
 
using namespace std;
 
class B;
 
class A
{
public:
    A() {cout << "A::this->" << this << endl;}
 
    void runA()
    {
        /* 错误的类型转换,拜一篇烂文所赐用上的 */
        union
        {
            void *from;             //void*类型
            void (A::*to)(int);     //A的成员函数指针类型
            void (*to2)(A*, int);   //C的函数指针类型,第一个参数是A*类型,用于传this
        }ut;
 
        ut.from = childFuncPtr;
 
        (this->*ut.to)(0);          //错误调用,用父类指针,以成员函数指针方式调用子类的成员函数
 
        /* 微软运行时库检测到运行时错误 */
        ut.to2(this, 1);            //错误调用,用C语言风格的函数指针,直接把this作为第一个参数传入调用子类的成员函数
    }
 
    template<typename FromType>
    void saveFuncPtr(FromType addr)
    {
        /* 错误的类型转换,拜一篇烂文所赐用上的 */
        union
        {
            FromType from;          //任意成员函数指针类型
            void *to;               //void*类型
        }ut;
 
        /* 把地址转换为void*保存到childFuncPtr */
        ut.from = addr;
        childFuncPtr = ut.to;
    }
 
protected:
    void *childFuncPtr;             //错误使用,由于长度可能不同,void*不可以用来保存任意成员函数地址
};
 
class B : public A
{
public:
    B()
    {
        cout << "B::this->" << this << endl;    //打印B的this指针
        this->saveFuncPtr(&B::testThis);        //保存B::testThis地址到父类的void*成员变量
    }
 
    void runB()
    {
        /* 错误的类型转换,拜一篇烂文所赐用上的 */
        union
        {
            void *from;             //void*类型
            void (B::*to)(int);     //类B的成员函数指针类型
            void (*to2)(B*, int);   //C语言的函数指针类型,第一个参数是B*类型,用于传this
        }ut;
 
        ut.from = childFuncPtr;
 
        testThis(2);                //直接调用成员函数
        (this->*ut.to)(3);          //错误调用,用函数指针调用成员函数
 
        /* 微软运行时库检测到运行时错误 */
        ut.to2(this, 4);            //错误调用,用C语言风格的函数指针,直接把this作为第一个参数传入调用子类的成员函数
    }
 
    void testThis(int i) {cout << i << " -> " << this << endl;}
};
 
int main()
{
    B b;
    b.runA();
    b.runB();
 
    _getch();
 
    return 0;
}

本例旨在测试各编译器对this的处理情况,其中有错误用法,请勿在实际项目中仿照使用!测试平台Win7x64,各编译器使用默认参数

gcc version 4.4.0 (GCC) 左debug右release。可见用debug版中,函数指针方法调用成员函数,成员函数中的this指针是错的!

用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 16.00.30319.01 版左debug右release。debug版本有健全的运行时检查,so,release时就可以放心的给出异常值了。vs处理C语言风格调用成员函数给出运行时错误,这一点很令人赞赏!

这是把用C语言风格调用成员函数两处注释掉后vs编译运行结果,左debug右release。this指针完全正常。Intel(R) C++ Compiler XE for applications running on IA-32, Version 12.0.0.063 Build 20100721的运行结果与vs2010几乎完全一样,debug版本一样有运行时错误,可见是微软的运行时库在起作用。但是release版本直接崩溃,也许跟优化方式有关?微软自家编译器链自家库确实有优势,呵呵~同样,去掉两处C风格调用,this指针完全正常。

Embarcadero C++ 6.31左debug右release。所有调用均输出正确的this值!

 

总结:最费解的是mingw的结果(同学linux下用gcc测试结果一样)。父类与子类有同样的this值,同样的函数地址,父类指针直接调用子类成员函数居然可以离谱成这样!看来,奇技淫巧最终带来的后果是各种不确定,不要尝试用父类指针调用子类成员函数,更不要使用C语言的函数指针强制传递this指针!!

后续:现在才明白,我是试图用模板实现自动类型推导的委托-_-! 推荐3篇该方面的好文:

成员函数指针与高性能的C++委托(上篇)

成员函数指针与高性能的C++委托(中篇)

成员函数指针与高性能的C++委托(下篇)


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#检测DataSet/DataTable是否为空的方法发布时间:2022-07-13
下一篇:
C#多线程处理之AutoResetEvent和ManualResetEvent发布时间:2022-07-13
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap