考虑以下计划。
#include<iostream>
using namespace std;
class base
{
public:
int _bval;
base():_bval(0){}
};
class derived:public base
{
public:
int _dval;
derived():base(),_dval(1){}
};
int main()
{
derived d[5];
base *p;
p=d;
for(int i=0;i<5;++i,++p)
cout<<p->_bval;
}
上述程序的输出为01010 我认为输出将是00000,因为_bval的值被基类构造函数初始化为0(每次)。
但为什么输出与00000不同?
我错过了什么?
答案 0 :(得分:11)
p[i]
为sizeof(base) * i
后的p
字节提供值。所以p[1]
不会给你d
的第二个元素,它会给你第一个元素的后半部分。
换句话说:如果使用指向基类的指针迭代派生类的数组,如果派生类的大小比基类大,则会得到错误的结果,因为它会逐步迭代sizeof(baseclass)
个字节。
答案 1 :(得分:7)
简短回答:在C ++中,值的数组永远不会是多态的,即使它们的内容是,也不能这样处理。也就是说,您无法将derived ad[N]
视为base ab[N]
。
答案很长:其原因深深植根于C的指针算法中。如果你有int* pi
并将其递增++pi
,它将不会简单地递增到下一个内存地址。如果确实如此,则不会指向下一个int
,因为它不会从下一个地址开始。因此,将sizeof(int)
个字节添加到指针中。 (一个具体的例子可能会有所帮助:在8位char
类型的架构中 - char
,根据定义,C和C ++考虑架构的字节大小 - 以及32位int
类型,{{1}因此,int
将向指针地址添加4,以便指向下一个++pi
。)相同的算法适用于所有其他指针操作。因此,例如,使用int
时,int* pi2=pi+1
会将pi2
字节指向sizeof(int)
,但pi
将产生1。
所以,假设你理解了最后一段,让我们回到数组。如果您有一个数组pi2-pi
,则derived ad[N]
的地址比ad[1]
的地址大sizeof(derived)
个字节。 (这是为了不使问题进一步复杂化而忽略对齐。)但是,如果ad[0]
指向base* pb
,则递增它将使其指向ad[0]
后面的地址。第一个元素 - 如果(在您的示例中为sizeof(base)
,则不 sizeof(base) < sizeof(derived)
的地址,但在ad[1]
的中间位置。
唯一可以将数组内容视为所有基类的方法是使用ad[0]
迭代数组并将此指针转换为<{1}} / em>循环:
derived*
(请注意,我还更改了您的代码以使用C ++'惯用的开始/结束迭代器。)
答案 2 :(得分:3)
想想d数组的内存布局。
D-&GT; 0101010101
每对01对应一个派生对象。
现在让我指出它:
对 - &GT; 0101010101
由于基础对象的大小是一个int的大小。该内存段被认为是10个基础对象:第一个使用_bval 0,第二个使用_bval 1,等等。
答案 3 :(得分:1)
除了sepp2k所说的,你没有在派生类构造函数中初始化_bval
。您应该使用base
构造函数初始化它。