提要:
1.0 声不声明成虚函数,决定了编译器是根据指针类型调用函数,还是根据对象类型调用函数;
指针类型:对应静态联编;
对象类型:对应动态联编--在运行时确定调用哪一个函数。
2.0 静态联编效率更高,因为动态联编需要跟踪基类 指针或者引用指向的对象类型,这增加了额外的处理开销。
1.0 继承中的同名隐藏问题:派生类中的同名函数,会隐藏基类中的同名函数;
(可以用obj.Shape::toString()访问基类的成员函数)
2.0 重定义函数: The functions have the same signature (函数特征相同)
(1) Name (同名)
(2) Parameters (including type, number and sequence) (同参数:类型,数量和顺序)
(3) Return type (返回值类型)
2.1 Defined in base class and derived class, respectively (在基类和派生类中分别定义)
3.0 为什么要引进重定义函数?
基类中的toString() 只能输出(基类)Shape类的信息;
而无法对派生类Circle中的数据成员进行操作。
4.0 子类型多态:不同的对象调用同名重定义函数,表现出不同的行为。
(因为同名会隐藏,所以可以使用重定义函数
--重定义,定义,是指的实现,也就是对于同一个声明,重新书写实现)
5.0 【注意】继承的特征之一:基类的引用/指针在不进行显式类型转换的情况下 可以 引用/指向派生类对象
(单向的,反之不可以)
但是:基类指针和基类引用:只能调用基类的方法。
class A
{
virtual int f()
{
return 1;
}
};
class B: public A { virtual int f() {return 8;} };
A a; B b;
A* p = &b; // 基类的指针,指向派生类的对象。说明通过基类可以访问派生类的数据成员。
a.f() // call A::f()
b.f() // call B::f()
p->f(); // call B::f()
6.0 如果方法没有使用关键字virtual,那么程序将根据引用/指针的类型 来决定调用哪一个函数。
7.0 如果方法使用了关键字virtual, 那么程序将根据引用/指针指向的对象的类型 来选择
调用哪一个函数。+ 基类指针/引用 可以指向派生类对象,就是说,你形参变成基类指针/引用,
但是,调用哪一个函数,却是可以向下变化的。父类对象可以传过去,派生类也可以。
8.0 这样会形成一种比较方便的函数,这个函数的形参使用基类指针/引用类型,
那么,所有的派生类在调用这个函数时都会调用自己本身的函数。例如:
void print(Shape* temp);
Circle objC;
Shape* cPtr = &objC;
那么 print(cPtr) 实际上操作的是 objC;