2011-08-26 68 views
6

对于复杂的标题很抱歉。我有这样的事情:使用子类对象初始化的多态基类对象的数组

class Base 
{ 
public: 
    int SomeMember; 
    Base() : SomeMember(42) {} 
    virtual int Get() { return SomeMember; } 
}; 

class ChildA : public Base 
{ 
public: 
    virtual int Get() { return SomeMember*2; } 
}; 

class ChildB : public Base 
{ 
public: 
    virtual int Get() { return SomeMember/2; } 
}; 

class ChildC : public Base 
{ 
public: 
    virtual int Get() { return SomeMember+2; } 
}; 

Base ar[] = { ChildA(), ChildB(), ChildC() }; 

for (int i=0; i<sizeof(ar)/sizeof(Base); i++) 
{ 
    Base* ptr = &ar[i]; 
    printf("El %i: %i\n", i, ptr->Get()); 
} 

,输出:

El 0: 42 
El 1: 42 
El 2: 42 

这是正确的行为(在VC++ 2005)?说实话,我希望这个代码不要编译,但是它确实,但是它并没有给我我需要的结果。这是可能吗?

回答

8

是的,这是正确的行为。原因是

Base ar[] = { ChildA(), ChildB(), ChildC() }; 

通过复制的三个不同类的对象到的class Base对象初始化数组元素和其产生的class Base对象,因此,你从阵列中的每个元素的观察行为class Base

如果要存储不同类别的对象,则必须使用new分配它们并存储指向它们的指针。

+5

*对象切片*,因为它是已知的。 – john

+0

因此,它复制对象而不复制它们的VTables? – GhassanPL

+0

@Kronikarz:它通过将其他类的内容复制到它们来初始化类Derived的对象。当然,它不会复制vtables - 它们是不可变的。 – sharptooth

2

要实现多态行为你的预期,你应该使用指针数组Base并通过new创建对象:

Base* ar[] = { new ChildA(), new ChildB(), new ChildC() }; 
+2

你正在泄漏记忆,坏小子。 –

0

什么是实际发生的是:

  1. 由于类型ar []是Base,将3 * sizeof(Base)内存量分配给ar。

  2. 由于您尚未声明Base的显式拷贝构造函数,因此base的默认拷贝构造函数被调用,它只是将ChildA,ChildB和ChildC对象的“Base”部分按位复制到数组ar中包含的Base对象中(默认的复制构造函数足够智能,不会将Child对象的虚拟指针按位复制到Base虚拟指针中)。

  3. ar [0],ar [1]和ar [2]的虚拟指针指向Base :: Get,因此调用Base :: Get。

这里需要注意的是,在执行之前,对象的虚拟指针指向的函数总是已知的。

在这种情况下,运行时间事先知道arr是由“Base”对象组成的,所以它设置它们的vptr以便在它们被分配内存时立即指向Base :: Get。