注意C++中对象指针,慎用void*

        由于不同C++编译器对C++对象模型的结构可能是不一样的。比如说,VC++系列是将虚函数表指针放在对象首地址,而GCC系列的编译器则是将虚函数表指针放在对象地址尾部。

        那么下面我将详细讲讲GCC编译器下的对象模型(含虚函数表指针)。

        先看下面这个例子:

 

#include  < iostream >
using   namespace  std;


class  A
{
private:

    
int a;
    
public:

    A(
void) : a(0)
    
{
        
    }


    
virtual void Hello(void)
    
{
        cout 
<< "Hello, world!" << endl;
    }

}
;

class  B :  public  A
{
private:

    
int b;
    
public:

    B(
void) : b(1)
    
{
        
    }


    
virtual void Hello(void)
    
{
        cout 
<< "B!" << endl;
    }

}
;

         上面的示例中,A对象的地址中存放两个4字节的数据,一个是变量a,另一个是虚函数表指针。变量a放在低地址;虚函数表指针放在高地址,那么方便起见,我这样表示——A  objA = { a,  vptr  }。而B的对象首先是存放类A域的所有成员,然后是B自己的数据成员——B  objB = { a, vptr, b }。

        那么这样的安排就有助于编译器处理这样的情况了——

 

B b;
* =   & b;
b
-> Hello();

 

        那么如果是多继承或是虚继承会怎样呢?

        碰到这种情况,GCC或其兼容C++编译器会同时判断左操作数的类型以及有操作数的类型。根据左操作数的类型来判断左操作数的指针指向有操作数对象的哪个偏移地址。如果是其他类型的指针(如void*),则指向对象的首地址。

        下面请看一下详细的测试代码:

 

#include  < iostream >
using   namespace  std;


class  A
{
private:

    
int a;
    
public:

    A(
void) : a(0)
    
{
        
    }


    
virtual void Hello(void)
    
{
        cout 
<< "Hello, world!" << endl;
    }

}
;


class  AA
{
private:

    
int aa;
    
public:

    AA(
void) : aa(100)
    
{
        
    }

    
    
virtual void Hi(void)
    
{
        cout 
<< "Hi, therte!" << endl;
    }

}
;


class  B :  public  A
{
private:

    
int b;
    
public:

    B(
void) : b(1)
    
{
        
    }


    
virtual void Hello(void)
    
{
        cout 
<< "B!" << endl;
    }

}
;


class  C :  virtual   public  A
{
private:

    
int c, d;
    
public :

    C(
void) : c(2), d(3)
    
{
        
    }

    
    
void Hello(void)
    
{
        cout 
<< "C!" << endl;
    }

}
;


class  E :  public  A,  public  AA
{
private:

    
int e;
    
public:

    E(
void) : e(8)
    
{
        
    }

    
    
void Hello(void)
    
{
        cout 
<< "E!" << endl;
    }

    
    
void Hi(void)
    
{
        cout 
<< "Hi!" << endl;
    }

}
;


extern   " C "   void  test( void );


int  main( void )
{
    A a;
    B b;
    C c;
    E e;
    
    unsigned 
long s[10];
    
    
int i=0;
    
    
for(; i<sizeof(a) >> 2; i++)
        s[i] 
= ((unsigned long*)&a)[i];
        
    a.Hello();
    
    
for(i=0; i<sizeof(b) >> 2; i++)
        s[i] 
= ((unsigned long*)&b)[i];
        
    A 
*= &b;
    
    p
->Hello();
    
    
for(i=0; i<sizeof(c) >> 2; i++)
        s[i] 
= ((unsigned long*)&c)[i];
        
    p 
= &c;
    
    
void* q = (C*)&c;
    
    p
->Hello();
    
    
for(i=0; i<sizeof(e) >> 2; i++)
        s[i] 
= ((unsigned long*)&e)[i];
        
    AA 
*pp = &e;
    p 
= &e;
    q 
= &e;
    
    
return 0;
}

 

        在main函数中,上面的p和q两个指针的值是不同的,尽管它们指向同一个对象。p指向了对象c的A类域的偏移处;而q则是指向了p 的首地址。那么下面的pp和p及q也是不同,这里的p和q都是指向首地址,因为类A域在对象e的起始处,而pp则是指向了e的AA类域的偏移处。

        大家可以利用以上代码进行调试,有个感性认识。

        所以在C++中,甚用void*指针指向一个对象,否则当再次进行类型转换时,调用相关函数可能会发生意想不到的情况。那么这个时候还是利用模板,通过范型来解决类型问题,这样做更安全,而且更优美。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值