在学习完虚函数后,最深的影响就是根据所指对象的类型,判断调用基类还是派生类中的函数,所以又产生了一个困惑。
看代码:
class A1
{
public:
virtual void print(void)
{
cout<<"A::print()"<<endl;
}
};
class B:public A1
{
public:
virtual void print(void)
{
cout<<"B::print()"<<endl;
}};
class C:public B
{
public:
virtual void print(void)
{
cout<<"C::print()"<<endl;
}
};
void print(A1 *a)
{
a->print();
}
void print1(A1 a)
{
a.print();
}
在main函数中执行:
A1 a, *pa,*pb,*pc;
B b;
C c;
pa=&a;
pb=&b;
pc=&c;
a.print();
b.print();
c.print();
pa->print();
pb->print();
pc->print();
A1 d = c;
d.print();
print1(a);
print1(b);
print1(c);
大家可以先不运行,自己思考下结果。我所疑问的就是最后下面的5行代码,为什么执行结果都是A::print().
原因:和普通函数一样,虚函数一样可以通过对象名来调用,此时编译器采用的是静态联编。
通过对象名访问虚函数时, 调用哪个类的函数取决于定义对象名的类型。对象类型是基类时,就调用基类的函数;对象类型是子类时,就调用子类的函数。
参考链接:http://see.xidian.edu.cn/cpp/biancheng/view/239.html
原理解释:
基类与派生类对象之间有赋值兼容关系,由于派生类中包含从基类继承的成员,因此可以将派生类的值赋给基类对象,在用到基类对象的时候可以用其子类对象代替。
1) 派生类对象可以向基类对象赋值
可以用子类(即公用派生类)对象对其基类对象赋值。如
A a1; //定义基类A对象a1
B b1; //定义类A的公用派生类B的对象b1
a1=b1; //用派生类B对象b1对基类对象a1赋值
在赋值时舍弃派生类自己的成员。也就是“大材小用”,如图11.26所示。
实际上,所谓赋值只是对数据成员赋值,对成员函数不存在赋值问题。
2) 如果函数的参数是基类对象或基类对象的引用,相应的实参可以用子类对象。
如有一函数:
fun: void fun(A& r) //形参是类A的对象的引用变量
{
cout<<r.num<<endl;
} //输出该引用变量的数据成员num
函数的形参是类A的对象的引用变量,本来实参应该为A类的对象。由于子类对象与派生类对象赋值兼容,派生类对象能自动转换类型,在调用fun函数时可以用派生类B的对象b1作实参:
fun(b1);
输出类B的对象b1的基类数据成员num的值。
与前相同,在fun函数中只能输出派生类中基类成员的值。
这也就能解释上面的输出结果了。