大胖的部落格

Just a note

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  112 随笔 :: 0 文章 :: 3 评论 :: 0 Trackbacks
当用父类指针指向子类对象数组时,用父类类型的指针对数组进行操作是不安全的。
因为数组里是连续分布的子类对象,父类和子类的大小不同,所以当用父类指针对数组进行操作时,会对数组空间重新按照父类大小进行切割。

#include"stdafx.h"
#include
<iostream>
#include
<Windows.h>

#define        CLASS_NUM    (5)    

using namespace std;


class Base
{
public:
    Base()
    
{
        iBase 
= ++i;
    }


    
virtual ~Base(){}

    
void TestNonVirtual()
    
{
        cout
<<"Base::NonVirtual:"<<iBase<<endl;
    }


    
virtual void TestVirtual()
    
{
        cout
<<"Base::Virtual:"<<iBase<<endl;
    }


    
static int i;
    
int iBase;        //mark the object
}
;

int Base::i = 0;

class Derived: public Base
{
public:
    Derived()
    
{
        iDerived 
= iBase*10;
    }


    
void TestNonVirtual()
    
{
        cout
<<"Derived::NonVirtual:"<<iBase<<endl;
    }


    
void TestVirtual()
    
{
        cout
<<"Derived::Virtual:"<<iBase<<endl;
    }


    
long    lDerived;
    
int        iDerived;
}
;

//传入父类型数组
void TestArray(Base b[])
{
    
int i = 0;
    
while(i<CLASS_NUM)
    
{
        
//按父类型大小裁剪连续分布的子类型对象数组空间
        b[i].TestNonVirtual();
        
++i;
    }

}


//main
int main(int argc, char *argv[])
try
{
    
//输出类的size
    cout<<"Size Of Base: "<<sizeof(Base)<<endl;            
    cout
<<"Size Of Derived: "<<sizeof(Derived)<<endl;    

    
//父类型指针指向子类型对象数组
    Base *pBase = new Derived[CLASS_NUM];

    
int i = 0;
    
while(i<CLASS_NUM)
    
{    
        
//按父类型大小裁剪连续分布的子类型对象数组空间
        pBase[i].TestNonVirtual();
        
++i;
    }


    TestArray(pBase);
    delete []pBase;
    
    
return 0;
}

catch(exception &e)
{
    cout
<<e.what()<<endl;
}


输出:
Size Of Base: 8
Size Of Derived: 16

Base::NonVirtual:1
Base::NonVirtual:10
Base::NonVirtual:2
Base::NonVirtual:20
Base::NonVirtual:3

Base::NonVirtual:1
Base::NonVirtual:10
Base::NonVirtual:2
Base::NonVirtual:20
Base::NonVirtual:3
*********************************

连续5个Derived对象大小为16*5的空间,被当作5个Base的大小8*5来切割,
所以,每个Derived对象被解析成为两个Base对象,切割后的第二个Base实际上还是在第一个Derived里面,
第一个Derived的前8个字节是父类Base,后8个字节是lDerived和iDerived,其中iDerived被解析成为第2个Base的iBase,
因为调用的是非虚函数,所以直接调用Base::TestNonVirtual(),但取到的iBase的值确是第一个Derived的iDerived。

虚函数的情况:
int i = 0;
while(i<CLASS_NUM)
{    
    
//按父类型大小裁剪连续分布的子类型对象数组空间
    pBase[i].TestVirtual();
    
++i;
}
输出:
Size Of Base: 8
Size Of Derived: 16
Derived::Virtual:1
程序崩溃!

因为是虚函数,所以首先根据对象的vPointer找到虚函数表中对应的函数,再调用,
本来Base的虚函数表中应该对应的是Base的TestVirtual,
但这里将第一个Derived的前两个字节解析成了Base,
所以第一个Derived的vPointer被当作了Base的vPointer,
然后从虚函数表中找到对应的子类所override的函数,并输出iBase,
接着将第一个Derived对象的第3个字节被错误地解析成为第2个Base的vPointer,
调用函数时,程序崩溃。

posted on 2009-05-14 21:40 大胖 阅读(353) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理