reinterpret_cast和static_cast

reinterpret_cast和static_cast

最近写一个使用完成端口的应用时, 居然在最简单的类型转换上栽了一个跟头, 写出来与大家分享,以避免犯和我类似的错误。
为了能尽量统一的处理每一个io操作, 我定义了下面这个类:
class  CIoPacket :  public  OVERLAPPED
{
public:
    
virtual void OnIoComplete( ULONG_PTR key, DWORD dwBytes ) = 0;
    
virtual void OnIoFailure( ULONG_PTR key, DWORD dwBytes ) = 0;
}
;
其中的两个虚函数分别在io操作成功完成或失败后被调用. 它的派生类将用于记录每一次io的相关信息. 为了从网络上接收数据, 我有从它派生出了一个类:
class  CNetPkt :  public  CIoPacket
{
protected:
    
// 定义一些成员变量
public:
    
virtual void OnIoComplete( ULONG_PTR key, DWORD dwBytes )
    
{
        
// 执行一些操作
    }

    
virtual void OnIoFailure( ULONG_PTR key, DWORD dwBytes )
    
{
        
// 执行一些操作
    }

}
;

下面的代码启动了一个接受数据的操作:
//
SOCKET sck;
//  初始化sck并绑定到一个完成端口
CNetPkt *  pPkt  =   new  CNetPkt();
//  设置pPkt的相关字段
::WSARecv( sck,  & wsabuf,  1 & dwBytes,  & dwFlags, pPkt, NULL );                 //  (0)
//
下面是完成端口线程中的代码:
DWORD dwBytes  =   0 ;
ULONG_PTR key 
=   0 ;
LPOVERLAPPED pol 
=  NULL;

if ( ::GetQueuedCompletionStatus(g_hIocp,  & dwBytes,  & key,  & pol, INFINITE) )
{
    
if( key != 0 )
        reinterpret_cast
<CIoPacket*>(pol)->OnIoComplete( key, dwBytes );    // (1)
}

else
{
    
if( pol != NULL )
        reinterpret_cast
<CIoPacket*>(pol)->OnIoFailure( key, dwBytes );        // (2)
}

    自我感觉实现的既灵活又漂亮还健壮、高效(嗯?!哪里来的西红柿?味道还不错!). 但是程序每次运行到(1)或(2)时就会出现非法操作, 令我百思不得其解. 首先仔细检查程序, 没发现错误; 又使出十八般调试功夫, 还是没有找到问题所在. 正在头大之时, 突然发现(1)处的pol和(0)处的pPkt的值并不一样, pol比pPkt大了4, 进一步通过反汇编发现(0)处实际传给WSARecv的就是((LPBYTE)pPkt)+4. 两个值不一样, 总出错也就不奇怪了.
    可编译器为什么要给指针加上4呢? 难道是编译器把CIoPacket的vfptr放到了OVERLAPPED的前面? 可是我记得vc应该是把它放在后面的呀(具体不敢确定了, 但好像VC6就是放在后面). 一番测试证实了我的猜测, vc7.1就是会把vfptr放到类结构的最前面, 该死的微软居然偷偷改了这么重要的编译细节. 但也不能光骂微软, 自己的错误也要检讨一下, 上面的程序中我应该用static_cast, 而不是reinterpret_cast, 因为static_cast能正确调整基类和派生类的指针, 而reinterpret_cast从汇编的角度看是什么也不干的. 如果vfptr放在后面, static_cast和reinterpret_cast的结果是一样的, 但当vfptr放在前面的时候它们就不同了. 由于C++标准没有规定vfptr的放置位置, 所以大家进行类型转换时一定要注意选择正确方式, 避免出现我这样的、隐蔽的可移植性问题.

你可能感兴趣的:(reinterpret_cast和static_cast)