多线程下vc2003,vc2005对虚函数表处理的BUG?

用python将dycom .dcm格式图像转换为nifti .nii 因为发现目前搜索到的多数中文教程并不能正确将FMRI图像正确转换,例如很多用simpleITk的seriesreader的,所以写了一下自己正确转换的方法。因为FMRI是一个4D数据,有时间轴,所以用seriesreader读写出来的图像把时间轴维度给降维了。当然你用SPM12工具的dycom import也是能正确转换的,但是我还是喜欢python这样的,代码直接简单。 阅读详情
考虑一下多线程代码,在设计上,App为了获取更多的功能,从Window派生,而App同时为了获取
某个模块的回调(所谓的Listener),App同时派生Listener,并将自己的指针交给另一个模块,
另一个模块通过该指针多态回调到App的实现(对Listener规定的接口的implemention)。设计上
只是一个很简单的Listener回调,在单线程模式下一切都很正常(后面我会罗列代码),但是换到
多线程下,编译器似乎就对语言机制的支持不够了:

///
/// to demonstrate the fucking bug.
///

#include  < iostream >
#include 
< process.h >
#include 
< windows.h >

class  Window
{
public:
    
/// 
    virtual void wrong()
    
{
        std::cout 
<< "wrong" << std::endl;
    }


    
virtual ~Window()
    
{
        std::cout 
<< "~Window" << std::endl;
    }

}
;


class  Listener
{
public:
    
/// as most listener class, it only put some interface here
    virtual void show() {}
}
;

class  Game
{
public:
    Game() : _listener( 
0 ) { }

    
void init( Listener *listener )
    
{
        _listener 
= listener;
        
/// it will call Window::wrong function but not App::show.
        _listener->show();
    }


private:
    Listener 
*_listener;
}
;

Game gGame;

static  unsigned  int  __stdcall ThreadFunc(  void   * p )
{
    Listener 
*listener = (Listener*) p;
    gGame.init( listener );

    
while( true )
    
{
        std::cout 
<< ".";
        Sleep( 
100 );
    }


    _endthreadex( 
0 );
    
return 0;
}


class  App :  public  Window,  public  Listener
{
public:
    
void init()
    
{
        
// create the game thread
        _game_thread = (HANDLE)_beginthreadex( NULL, 0, ThreadFunc, this, 0, NULL );
    }


    
/// implement the interface
    void show()
    
{
        std::cout 
<< "App::show" << std::endl;
    }


    
/// exit
    void exit()
    
{
        
/// just for testing purpose
        ::TerminateThread( _game_thread, 1 );
        ::CloseHandle( _game_thread );
    }


private:
    HANDLE _game_thread;
}
;


App gApp;


int  main()
{
    gApp.init();

    std::cout 
<< "Press enter key to exit!" << std::endl;
    std::cin.
get();

    gApp.exit();
    
return 0;
}

 

App多重继承Window和Listener,在Game里回调App::show时,却调用到了Window::wrong函数。看上去,传给
Game的Listener指针所指向的虚函数表错误了(vtable指针错了)。App先继承Listener后继承Window时,情况
就正确了。(因为使用了_beginthreadex,程序需要链接多线程的运行时库)

单线程情况下:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
/// to demonstrate the fucking bug.
///
/// OK, even it links the multi-thread crt.

#include  < iostream >
#include 
" kl_thread.h "

class  Window
{
public:
    
/// 
    virtual ~Window()
    
{
        std::cout 
<< "~Window" << std::endl;
    }

}
;


class  Listener
{
public:
    
/// as most listener class, it only put some interface here
    virtual void show() {}
}
;

/// 
Listener  * gListener;

class  App :  public  Window,  public  Listener
// class App : public Listener, public Base
{
public:
    
void init()
    
{
        gListener 
= this;
    }


    
/// implement the interface
    void show()
    
{
        std::cout 
<< "App::show" << std::endl;
    }

}
;


App gApp;


int  main()
{
    gApp.init();

    gListener
->show();
    
return 0;
}

 

无论Listener, Window的顺序如何,一切都很正常。这起码说明了,在语言层次,我的做法是正确的。
而这个时候即使链接了多线程的运行时库,结果也是正确的。

那么错误可以归结于多线程,可能是在多线程下编译器对虚函数表初始化不正确所致。这是否真的是
VC2003、VC2005的BUG?

 

posted on 2008-04-24 14:40 Kevin Lynx 阅读(866) 评论(10)  编辑 收藏 引用 所属分类: C++

评论

 

还是写出来看的清楚:static unsigned int __stdcall ThreadFunc( void *p )
{
Listener *listener = (Listener*) p;


应该是
Listener *listener = (App*) p;
  

回复  更多评论   
# re: 多线程下vc2003,vc2005对虚函数表处理的BUG? 2008-04-24 17:34 giscn

或者这样
_game_thread = (HANDLE)_beginthreadex( NULL, 0, ThreadFunc, (Listener*)this, 0, NULL );  回复  更多评论   

# re: 多线程下vc2003,vc2005对虚函数表处理的BUG? 2008-04-24 17:40 giscn

错误由void* 指针转换引起,与多线程无关,C++ 的指针转换不同于C, 如果是多集继承,参数同样是 this, 其实际值不一定相同,取决于参数类型  回复  更多评论   

# re: 多线程下vc2003,vc2005对虚函数表处理的BUG? 2008-04-24 17:40 eXile

楼上的正解。在使用多重继承时要注意对象的布局。  回复  更多评论   

# re: 多线程下vc2003,vc2005对虚函数表处理的BUG? 2008-04-24 18:09 亨德列克

Listener *listener = (Listener*) p; 是这一行错了,这个错误应该很多人都会犯……  回复  更多评论   

# re: 多线程下vc2003,vc2005对虚函数表处理的BUG? 2008-04-24 19:18 Kevin Lynx

@亨德列克
不是那一行错了,App和Game两个类分属不同模块,为了不让两个模块耦合,这里使用Listener *listener = (Listener*) p,而不是(App*)p。

giscn和eXile (他删除了他的第二条评论:) )的方法是正确的。可以被采用,再次表示感谢。

这让我意识到,void*在C++里缺乏安全性。  回复  更多评论   

# re: 多线程下vc2003,vc2005对虚函数表处理的BUG? 2008-04-24 22:49 饭中淹

这不是多线程的问题

当你先继承window后继承Listener的时候,App的内存结构如下:

class App
vt of Window
data of Window
vt of Listener
data of Listener
data of App

_beginthreadex的参数是void*,你把this传递进去,相当于传递CApp* this。其实隐含的就是Window*this,那么里面调用Listener->Show,自然就会去Window的vt里面查找对应索引的函数,就会调用错函数。

而第二个,因为你显式的=this,所以,编译器会进行转换,从而把正确的Listener地址赋值给那个全局指针,这时,无论继承顺序如何,都是正确的结果。

这其实是因为对象指针转换不准确导致的,不是vc的bug,也不是多线程的问题。
  回复  更多评论   

# re: 多线程下vc2003,vc2005对虚函数表处理的BUG? 2008-04-25 08:32 FongLuo

收藏,收藏,^_^  回复  更多评论   

# re: 多线程下vc2003,vc2005对虚函数表处理的BUG? 2008-04-25 17:03 #Ant

多继承下还有这样的问题,学习了。。。  回复  更多评论   

# re: 多线程下vc2003,vc2005对虚函数表处理的BUG?[未登录] 2008-04-26 00:49 杨粼波

指针偏移,多线程会发生这样滴问题。。。。  

#  re: 多线程下vc2003,vc2005对虚函数表处理的BUG? 2008-04-24 16:09 Fox
 
EM4095低频RFID曼彻斯特解码实战:从原理图到MCU代码实现 本文详细介绍了EM4095低频RFID读卡器的设计与实现,从芯片工作原理、天线设计到曼彻斯特解码算法。重点讲解了基于STM32的MCU代码实现,包括定时器捕获、数字滤波和错误处理,为开发125kHz RFID系统提供实用指南。 阅读详情

相关推荐

vfsglobal预约系统登录问题全解析(by quqi99)

本文全面解析VFS Global预约系统登录常见问题,包括IP限制、验证码验证失败及账号自动删除等。提供详细的解决方案,如DNS配置、浏览器设置及邮箱选择建议,帮助用户顺利完成签证预约。特别针对中国用户遇到的IP限制问题,给出了实用技巧。

gamma的博客 1342

【转】使用面向对象编程封装Thread虚函数和函数对象

C++11提供了thread,但是过于复杂,我们还是倾向于在项目中编写自己的Thread。 Posix Thread的使用这里不再赘述。 重点是这个函数: #include &amp;lt;pthread.h&amp;gt; int pthread_create(pthread_t *thread, const pthread_attr_t *attr, voi...

路漫漫其修远兮 748

基于知识图谱的电影推荐系统——Neo4j&Python

也可直接从这里下载:链接: https://pan.baidu.com/s/1l6wjwcUzy5G_dIlVDbCkpw 提取码: pkq6。修改main.py中的driver,输入自己数据库的用户名与密码。系统会询问是否需要重新加载并创建知识图谱,在第一次时输入1。

weixin_46351593的博客 6357

当多线程下的析构遇上虚函数

今天遇到的一个教训:有这样的一个基类class service{public:    void start();    void stop();    virtual void svc(); ...    virtual ~service();};service::~service(){ stop();}service是一个服务线程的基类,svc是线程的入口。在析构函数中调用stop确保线程优雅终

不亦快斋 2308

从零构建NVENC编码器:一个游戏开发者的实战手册

本文详细介绍了如何从零构建NVENC编码器,特别针对游戏开发者提供实战指南。内容涵盖NVENC基础概念、DirectX纹理捕获、异步编码优化及高级特性配置,帮助开发者高效集成硬件编码技术到游戏引擎中,实现高性能游戏录制和直播功能。重点解析了NVIDIA专用编码芯片的优势与API使用技巧。

weixin_29292431的博客 366

STM32CubeIDE串口打印浮点数踩坑实录:5分钟搞定printf输出小数(附完整配置截图)

本文针对STM32CubeIDE中串口无法打印浮点数的常见问题,提供了详细的解决方案。核心在于通过添加“-u _printf_float”链接器参数,启用标准库的浮点数格式化支持。文章附有完整的配置截图和步骤,并提供了系统性的排查清单,帮助开发者快速解决printf输出小数的问题。

weixin_29163797的博客 344

运行时多态和虚函数

class A { public: virtual std::string tostring(){ return "A";} } class A{ public: virtual std::string tostring(){ return "A"; } }; class B:public A{ public: std::string tostring(){ ...

不停 206

esxi8.0.3如何添加USB外置硬盘

本文介绍了在ESXi系统中配置外置USB硬盘作为数据存储的完整步骤:1)通过SSH禁用USB仲裁服务并关闭自启;2)识别已连接的USB硬盘设备路径;3)使用partedUtil创建GPT分区表并计算扇区范围;4)设置VMFS6分区并格式化。关键命令包括停止usbarbitrator服务、partedUtil分区操作以及vmkfstools格式化命令。整个过程需通过SSH执行,最终将USB硬盘配置为名为"USB_Datastore"的VMFS6存储设备。

qq_41683719的博客 1548

代码从VS2003升级到VS2005的编译链接问题及解决方法

最近做了一个代码从VS2003升级到VS2005的工作,出现了不少问题,在参考了一些网上的资料和朋友的帮助后,再加上自己的分析调查,到目前为止出现的问题都解决了。由于发现网上这方面的资料似乎比较零散,为了使同样要做VS2003(或VC6)程序向VS2005(或VS2008)转化的朋友能够少走一点弯路,整理如下,也欢迎各位网友提出宝贵意见或建议。

dhm2000的专栏 1749

【WorkBuddy专栏68】HY3绕路部署vsDS4直击根因——一次真实踩坑揭露两个AI模型的推理差距

【WorkBuddy专栏68】HY3绕路部署vsDS4直击根因——一次真实踩坑揭露两个AI模型的推理差距

AI人人懂 566

Simulink仿真状态空间中状态变量时域响应

起因 今天老师留了一道关于状态空间变量响应的题,于是想用simulink仿真,一开始使用state-space模块,但是发现它只能输出y,没办法显示状态变量的变化情况。然而状态变量之间的关系都可以表示为一阶微分方程组,因此我们考虑在Simulink中搭建微分方程组来求解这个问题。 微分方程组 x1˙=x2x2˙=−4x1−5x2+1(1)\dot{x_1}=x_2\\ \dot{x_2}=-4x_...

ZYunfei的博客 7467

VC6.0与VC.net的具体区别

对于VC6.0和VC.net本人有几点疑问。 1.首先,vc.net开发的程序是否依然需要.net框架的支持?是否可以独立运行? 2.对与VC.net开发出来的程序在运行效率上和VC6.0开发的差距有多大? 3.现在大部分做MFC开发的用的是VC6.0还是VC.net? 4.VC.net有哪些平台优越性能?比如各种技术支持上? 希望有开发经验的达人能解开我的迷惑。网友回复:我用vc6.0,下一

痞子龙3D编程 2554

数据分析案例-2024年裁员数据集可视化分析

本实验数据集来源于Kaggle,原始数据集为2020-2024年裁员数据,共有3577条,12个变量,该数据集提供了不同公司的裁员信息。它包括以下几栏:Company:裁员发生的公司名称。Location_HQ:公司总部位置。Industry:公司所属的行业或部门。Laid_Off_Count:公司解雇的员工数量。Percentage:公司员工被解雇的百分比。Date:裁员发生的日期。Source:信息来源网址。Funds_Raised:公司募集资金的信息。

m0_64336780的博客 1万+

收集的VC线程和多线程开发资料

VC中利用多线程技术实现线程之间的通信  刘涛|天极网|2006-02-06 10:23本实例针对多线程技术在应用中经常遇到的问题,如线程间的通信、同步等,分别进行探讨 利用API在Windows下创建进程和线程  EmilMatthew|BLOG|2006-01-23 08:55谈到在Windows创建线程的例子,在网上的很

ark1111的专栏 7330

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

vc6到vc8移植问题及处理

最近由于工作需要,把项目从vc6升级到vc7(vc8同样适用)。升级过程遇到些问题,记录于此。1. 编译时出现:WINVER not defined. Defaulting to 0×0501 (Windows XP and Windows .NET Server)这个问题是因为没有指定工程要使用的平台SDK的版本。 Minim

dishening的专栏 1612

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

OpenMP ,VS2005 programming

本文转自 :http://hi.baidu.com/lampjinn/blog/item/cce5853559315a1091ef3911.htmlVC 2005 下的OpenMP编程2007年05月25日 星期五 17:09用着双核的机器,跑着单进程的应用,忍不了它的缓慢,于是想找找怎么简单提升速度的方法。下面这个文章是MSDN上讲怎么在VC++ 2005 里面用O

normallife的专栏 1401

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

vc2005 - 编译时应注意的几点

1       LNK2005错误——重复定义错误 声明全局变量要注意以下3点: (1)声明必须使用extern关键字;(2)不能给变量赋初值 (3)     全局变量重复定义 否则就会出现 LNK2005错误 2.头文件的包含重复。往往需要包含的头文件中含有变量、函数、

_Max 622

error link2005问题原因与解决方法

遇到讨厌的error link 2005链接错误,虽然知道是重复定义产生的错误,但实在找不出来。试了一下下面的方法,程序可以运行了,但不知道会不会有后遗症,先将就着用吧。      在vs.net 2003平台下      项目->属性->链接器->命令行->附加选项中加 /force      这个方法我在VC2008里面也试过,可以用,但是会出警告信息。 最后将VC++的链接错误LN

Java技术博文 2505

CxImage 在VC2005 DLL方式简单配置

<br />最近接到一个身份证识别OCR软件项目,使用微软OFFCIE2003自带的OCR处理模块就可以很好识别,为了提高识别率打算把身份证图片的花纹去掉。在网上找到CxImage可以很好的处理图片。配置过程记录如下:<br />CxImage <br />最新版本:cximage600_full.7z<br />下载地址:http://www.xdp.it/cximage/600/cximage600_full.7z<br />测试项目:CxImageTest (在本地d:/CxImageTest)<br

requestImage的专栏 860

VC 运行时库 /MD、/MDd 和 /MT、/MTd(转)

这里总结下他们的区别,后面的那个'd'是代表DEBUG版本,没有'd'的就是RELEASE版本了。 首先说/MT /MT是 "multithread, static version ” 意思是多线程静态的版本,定义了它后,编译器把LIBCMT.lib 安置到OBJ文件中,让链接器使用LIBCMT.lib 处理外部符号。 /MD是 "multithread- and DLL-specific version” ,意思是多线程DLL版本,定义了它后,编译器把 MSVCRT.lib 安置到OBJ文件中,它.

dragoo1的专栏 1402

VC2005程序的一个运行错误 由于应用程序配置不正确,应用程序未能启动。重新安装应用程序可能会纠正这个问题。...

VC2005程序的一个运行错误 由于应用程序配置不正确,应用程序未能启动。重新安装应用程序可能会纠正这个问题。 方法一: 在C:/Program Files/Microsoft Visual Studio 8/VC/redist/Debug_NonRedist/x86/Microsoft.VC80.DebugCRT 下找到了下列文件: msvcm80d.dll msvcp80d.dll ...

luotuoass 111

Jiangsheng的优快云 Digest (Dec 2005)

优快云 讨论总结系列:Jiangsheng的优快云 Digest (Dec 2005)(http://blog.youkuaiyun.com/jiangsheng/archive/2005/12/24/561501.aspx) Jiangsheng的优快云 Digest (Oct 2005)(http://blog.youkuaiyun.com/jiangsheng/archive/2005/12/27/56327

蒋晟的专栏 6万+

转:VC6.0与VC.net的具体区别

原文:http://www.itzhe.cn/article/20080302/98267.html 对于VC6.0和VC.net本人有几点疑问。 1.首先,vc.net开发的程序是否依然需要.net框架的支持?是否可以独立运行? 2.对与VC.net开发出来的程序在运行效率上和VC6.0开发的差距有多大? 3.现在大部分做MFC开发的用的是VC6.0还是VC.net? 4.VC...

weixin_30868855的博客 201

VC 运行时库 /MD、/MDd 和 /MT、/MTd

这里总结下他们的区别,后面的那个'd'是代表DEBUG版本,没有'd'的就是RELEASE版本了。 首先说/MT /MT是 "multithread, static version ” 意思是多线程静态的版本,定义了它后,编译器把LIBCMT.lib 安置到OBJ文件中,让链接器使用LIBCMT.lib 处理外部符号。 /MD是 "multithread- and DLL-spec...

dinaoju5134的博客 156
上一篇: 调试经验总结-VC下的错误对话框
下一篇: 今天发现自己的array类太慢
suacker
博客等级 码龄24年 46粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值