一、多态
1. 什么是多态
多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。为了更为详细的说明多态,此处我们划分为**静态多态 **和 **动态多态 ** 两种状态来讲解
- 静态多态
静态多态是编译器在编译期间完成的,编译器会根据实参类型来选择调用合适的函数,如果有合适的函数可以调用就调,没有的话就会发出警告或者报错 。 该种方式的出现有两处地方: **函数重载 ** 和 泛型编程| 函数模板
int Add(int a, int b){
return a + b;
};
double Add(double a, double b)
{
return a + b;
};
int main()
{
Add(10, 20);
Add(10.0,20.0); //正常代码
return 0;
}
- 动态多态
它是在程序运行时根据父类的引用(指针)指向的对象来确定自己具体该调用哪一个类的虚函数。
A *a = new A();
Father * f = new Fahter();
Father * f2 = new Child();
动态多态的必须满足两个条件:
- 父类中必须包含虚函数,并且子类中一定要对父类中的虚函数进行重写。
- 通过父类对象的指针或者引用调用虚函数。
2. 父类指针
通常情况下,如果要把一个引用或者指针绑定到一个对象身上,那么要求引用或者指针必须和对象的类型一致。 不过在继承关系下,父类的引用或指针可以绑定到子类的对象,这种现象具有欺骗性,因为在使用这个引用或者指针的时候,并不清楚它所绑定的具体类型,无法明确是父类的对象还是子类的对象。
int *p = new int(3);
继承关系:
father *p = new father();
child *c = new child();
//继承的时候,有一种特殊情况:
father *p2 = new child():
1. 静态类型和动态类型
只有在继承关系下,才需要考虑静态和动态类型,这里仅仅是强调类型而已。所谓的
静态类型
指的是,在编译时就已经知道它的变量声明时对应的类型是什么。而动态类型
则是运行的时候,数据的类型才得以确定。只有在
引用
或者指针
场景下,才需要考虑 静态或者动态类型。因为非引用或者非指针状态下,实际上发生了一次拷贝动作。father *f2 = new son();
//静态类型:不需要运行,编译状态下,即可知晓 a,b的类型。
int a = 3;
string b = "abc";
//动态类型:
//f在编译时,类型是Father ,但在运行时,真正的类型由getObj来决定。目前不能明确getObj返回的是Father的对象还是Child的对象。
Child getObj(){
Child c ;
return c;
};
Father &f = getObj();
- 父类指针(引用)指向子类对象
父类的引用或指针可以绑定到子类的对象 , 那么在访问同名函数时,常常出现意想不到的效果。
class father{
public:
void show(){
cout << "father show" << endl;
}
};
class children : public father{
public:
void show(){
cout << "children show" << endl;
}
};
int main(){
father f = children();
f.show(); // 打印father show
}
2. 静态联编和动态联编
程序调用函数时,到底执行哪一个代码块,由编译器来负责回答这个问题。将源码中的函数调用解释为执行特定的函数代码,称之为
函数名联编
。 在C语言里面,每个函数名都对应一个不同的函数。但是由于C++里面存在重载的缘故,编译器必须查看函数参数以及函数名才能确定使用哪个函数,编译器可以在编译阶段完成这种联编,在编译阶段即可完成联编也被称为:静态联编 | 早期联编
。 程序在运行期间才决定执行哪个函数,这种称之为动态联编 | 晚期联编
class WashMachine{
public:
void wash(){
cout << "洗衣机在洗衣服" << endl;
}
};
class SmartWashMachine : public WashMachine{
public:
void wash(){
cout << "智能洗衣机在洗衣服" << endl;
}
};
int main(){
WashMachine *w1= new WashMachine(); //父类指针指向父类对象 打印:洗衣机在洗衣服
w1->wash();
SmartWashMachine *s = new SmartWashMachine(); //子类指针指向子类对象 打印: 智能洗衣机...
s->wash();
WashMachine *w2 = new SmartWashMachine(); //父类指针指向子类对象 打印:洗衣机在洗衣服
w2->wash();
return 0 ;
}
3. 为什么要区分两种联编
动态联编在处理子类重新定义父类函数的场景下,确实比静态联编好,静态联编只会无脑的执行父类函数。但是不能因此就否定静态联编的作用。动态联编状态下,为了能够让指针顺利访问到子类函数,需要对指针进行跟踪、这需要额外的开销。但是并不是所有的函数都处于继承状态下,那么此时静态联编更优秀些。
编写c++代码时,不能保证全部是继承体系的父类和子类,