原来使用父类和子类指针都指向子类对象,然后调用虚函数,但是在子类对象中加入一个int i;属性之后,宕机了。
这是因为,父类指针p和子类指针c(加入int i之后),两个类所占的内存大小也不一样了,指针的数据类型是他所指向的内存空间的数据类型,因此p指针自加1的大小为一个父类对象的内存大小,而c指针自加1的大小为一个子类对象的大小。
p与c的指针步长不一样。
此时仍想利用p指针实现多态,而p++之后,p类指针增加的内存大小(p指针的偏移量)是一个父类对象的大小,指向的是数组中第一个子类对象的新增成员变量int i;而不是VPTR指针,此时若使用指针p调用虚函数(来实现多态),则会宕机。
原理:指针作为一种数据类型,它的数据类型是由其指向的内存空间的数据类型决定的。
而在使用子类数组,用for循环,使得子类对象挨个实现多态是没有问题的。
但别用父类指针p指向该子类对象数组的首地址,再用p++,这样就因为指针步长不一样产生错误或者异常而宕机。
简而言之,不要用父类指针做辅助变量,去遍历基类的数组。
当子类和父类中,除了虚函数相同以外,还有各自的成员变量,其他函数时,这时候子类和父类占用的内存大小是不同的。因此,当使用父类指针实现多态时,要注意:当父类指针指向的是子类对象数组时,此时执行父类指针pBase++,指针移动的步长(及内存大小)仍然为父类的占用的字节大小,而子类中可能有新定义的成员变量和成员函数等,此时pBase指向的不一定是子类的虚函数的地址了,假设指向了子类的成员,这是用pBase指针调用虚函数时,就会宕机,报错。
#include "iostream"
using namespace std;
//指针也是一种数据类型,指针数据的数据类型是指,它所指的内存空间的数据类型
//最后一点引申 指针的步长 。。。c++
class Parent001
{
protected:
int i;
int j;
public:
virtual void f()
{
cout << "Parent001::f" << endl;
}
};
class Child01 : public Parent001
{
public:
int k;
public:
Child01(int i, int j)
{
printf("Child01:...do\n");
}
virtual void f()
{
printf("Child01::f()...do\n");
}
};
void howToF(Parent001 *pBase)
{
pBase->f();
}
//指针的步长 在c++领域仍然有效,父类指针的步长和子类指针的步长不一样
//多态是靠迟绑定实现的(vptr+函数指针实现)
int main006()
{
int i = 0;
Parent001* p = NULL;
Child01* c = NULL;
//不要把父类对象还有子类对象同事放在一个数组里面
Child01 ca[3] = { Child01(1, 2), Child01(3, 4), Child01(5, 6) };
//不要用父类指针做赋值指针变量,去遍历一个子类的数组。
p = ca;
c = ca;
p->f();
c->f(); //有多态发生
p++;
c++;
//p->f();//基类对象与子类对象的大小不一样,导致p++后指向的是子类中的成员变量k,而不是函数f()
c->f();
for (i = 0; i<3; i++)
{
howToF(&(ca[i]));
}
system("pause");
return 0;
}