考虑以下程序.
#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;
}
Run Code Online (Sandbox Code Playgroud)
上面程序
的输出是01010. 我认为输出将是00000,因为_bval的值被基类构造函数初始化为0(每次).
但为什么输出与00000不同?
我错过了什么?
sep*_*p2k 11
p[i]给出sizeof(base) * i后面的字节值p.所以p[1]不会给你第二个元素d,它会给你第一个元素的后半部分.
换句话说:如果使用指向基类的指针迭代派生类的数组,如果派生类的大小比基类大,则会得到错误的结果,因为它将以sizeof(baseclass)字节为单位进行迭代.
简短回答:在C++中,值的数组永远不会是多态的,即使它们的内容是,也不能这样处理.也就是说,你不能把它derived ad[N]视为一个base ab[N].
答案很长:其原因深深植根于C的指针算法中.如果你有一个int* pi并递增它++pi,它不会简单地增加到下一个内存地址.如果确实如此,它就不会指向下一个,int因为它不会从下一个地址开始.因此,将sizeof(int)字节添加到指针.(一个具体的例子可以帮助:在与8位架构char类型- char幸福,顾名思义什么C和C++考虑架构的字节大小-和32位int类型int.有4个字节的大小.因此,++pi将增加4到指针地址,以便它指向下一个int.)相同的算法适用于所有其他指针操作.因此,例如,with int* pi2=pi+1,pi2将指向sizeof(int)后面的字节pi,虽然pi2-pi会产生1.
所以,假设你理解了最后一段,让我们回到数组.如果你有一个数组derived ad[N],地址ad[1]就是sizeof(derived)字节比的地址大ad[0].(这是无视对齐,以不使问题变得更加复杂化.)但是,如果你有一个base* pb指向ad[0],增加它将使指向sizeof(base)第一个元素的地址后面-这,如果(像在您的示例的情况下)sizeof(base) < sizeof(derived),不是地址ad[1],而是在中间的某个地方ad[0].
你可以做治疗阵列的内容,如果它是所有基类的唯一的事情,是使用在阵列上进行迭代derived*,并施放此指针base* 内循环:
derived d[5];
derived* begin = d;
const derived* end = d + sizeof(d)/sizeof(d[0]); // points one beyond the last element
while(begin != end)
{
base* pb = begin;
cout<< pb->_bval;
++begin;
}
Run Code Online (Sandbox Code Playgroud)
(请注意,我还更改了您的代码以使用C++'惯用的开始/结束迭代器.)