封装可以使代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码复用
而多态的目的是为了接口重用
多态“一个接口,多种方法”,在运行时期才决定调用的函数
class Base
{
public:
void foo()
{
printf("1\n");
}
void fun()
{
printf("2\n");
}
};
class Derived :public Base
{
public:
void foo()
{
printf("3\n");
}
void fun()
{
printf("4\n");
}
};
int main()
{
Base a;
Derived b;
Base *ptr = &a;
ptr->foo(); //1
ptr->fun(); //2
ptr = &b;
ptr->foo(); //1
ptr->fun(); //2
}
class Base
{
public:
virtual void foo()
{
printf("1\n");
}
void fun()
{
printf("2\n");
}
};
class Derived :public Base
{
public:
void foo()
{
printf("3\n");
}
void fun()
{
printf("4\n");
}
};
int main()
{
Base a;
Derived b;
Base *ptr = &a;
ptr->foo(); //1
ptr->fun(); //2
ptr = &b;
ptr->foo(); //3
ptr->fun(); //2
return 0;
}
第一个本身就是基类指针,指的又是基类对象,所以ptr->foo(),ptr->fun()就是1,2
第二个ptr->foo(),ptr->fun()是基类指针指向子类对象,体现了多态的用法,ptr->foo()
是基类指针指向了一个虚函数,虚函数列表里面存放的是foo()虚函数的地址,指向的对象
不同,函数地址也就不同,这里将找到相应的子类foo()的地址,所以答案为3,ptr->fun()
由于ptr是一个基类指针,指向的是固定偏移量的函数,因此指向的还是基类里面的fun()
函数了,所以输出结果还为2
int main()
{
Base a;
Derived b;
Derived *ptr = (Derived *)&a;
ptr->foo(); //1
ptr->fun(); //4
return 0;
}
如果将测试代码改为Derived ptr = (Derived )&a;ptr->foo();ptr->fun();输出结果为1、4
这是一个子类指针去指向一个强制转换为子类地址的基类对象,ptr此时是子类指针,虽然被赋予了基类对象地址,但是在ptr->fun(),由于地址偏移量固定,偏移量是子类对象的偏移量,所以ptr->fun()输出为4,ptr->foo(),通过虚函数表的引用,找到了基类foo()函数地址,所以输出为1
C++的多态是通过虚函数来实现的
虚函数:成员方法的返回值前面,加了vitual关键字
只要有虚函数,就会生成虚函数表,当一个类有虚函数表,它编译的时候会产生一张vftable虚函数表,虚函数表里面存放的是虚函数的地址(运行起来放在数据段)
覆盖(重写):子类重新定义父类的方法
重写有两种:(1)直接重写成员函数
(2)重写虚函数(体现了C++多态性)
重载:(1)允许多个同名的函数
(2)允许函数的参数列表不同,参数个数不同,或者两者都不同
(3)作用域相同
编译器会根据这些函数的不同列表,将同名的函数的名称做修饰,从而生成一些不同名称的 预处理函数,来实现同名函数调用时的重载问题。但这并没有体现多态性。
隐藏:(必须出现在继承结构中)派生类的函数屏蔽了与其同名的基类函数
(1)如果派生类的函数与其基函数同名,但是参数不同。此时,不论有没有vitual关键字,基 类的函数将被隐藏(不能与重载混淆)
(2)如果派生类的函数与其基类的函数同名,参数列表也相同,但是基类没有vitual关键字, 此时基类的函数将被隐藏(不能与覆盖混淆)
多态与非多态的区别:函数的地址是早绑定还是晚绑定,(1)在编译时期就能确定函数的调用地址, 并生产代码,是静态的,早绑定
(2)在运行时期才能确定的,是晚绑定
常见用法:声明基类指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据子类的不同实现不同的方法
纯虚函数:是在基类中声明的虚函数,在基类中没有定义,但要求任何派生类都要定义自己的方法
vitual void fun() = 0;
场景:基类生成对象是不合理的,例如:图形是一个抽象概念,它不可以实现求面积方 法,但它派生出的子类(圆,长方形)就可以定义求面积的方法
抽象类:包含纯虚函数的类称为抽象类,由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象 类的对象