分类
静态多态:函数重载和运算符重载,编译阶段确定函数地址(地址早绑定)
动态多态:派生类和虚函数实现运行时多态,运行阶段确定函数地址(地址晚绑定)
动态多态
地址早绑定:
class A{
public:
void test(){
cout<<"A"<<endl;
}
};
class aSon:public A{
public:
void test(){
cout<<"aSon"<<endl;
}
};
void test1(A &a){
a.test();
}
.
.
.
aSon s;
test1(s);
因为test1是地址早绑定,所以在编译时就确定了test1会调用父类的test函数。在调用test1时将子类类型转换为了父类。
地址晚绑定:
class Base{
public:
//虚函数
virtual void test(){
cout<<"Base"<<endl;
}
};
class Son:public Base{
public:
virtual void test(){
cout<<"Son"<<endl;
}
};
class secSon:public Base{
public:
//子类的virtual可以省略
virtual void test(){
cout<<"secSon"<<endl;
}
};
void test0(Base &base){
base.test();
}
.
.
.
Son s;
test0(s);
secSon sec;
test0(sec);
此时test0是地址晚绑定,在运行时确定函数地址,当传入的是Son时,调用Son的test函数,当传入的是secSon时,调用secSon的test函数。
满足条件
- 有继承关系
- 子类要重写父类的虚函数
重写:子类函数名、参数列表和返回值要与父类相同
重载:函数名相同,参数列表必须有所不同
调用方式
父类的指针或者引用执行子类对象
实质
当一个父类有一个虚函数时,他的内存空间就增加了一个虚函数指针vfptr,这个指针指向一个虚函数表格vftable,这个表格记录了所属父类的虚函数的地址。
当一个子类继承了这样一个父类时,他会继承其虚函数指针,并将虚函数表中的地址换成其重写的函数的地址。
使用父类的指针或者引用执行子类对象时,就会调用子类自己重写的函数。
存在的问题
父类指针在析构时,不会调用子类中的析构函数,导致子类如果有堆区属性,会出现内存泄漏的问题。
class Base{
public:
Base(){
cout<<"Base构造函数"<<endl;
}
~Base(){
cout<<"Base析构函数"<<endl;
}
virtual void test()=0;
};
class Son:public Base{
public:
string name;
int *age;
Son(string _name,int _age){
name=_name;
age=new int(_age);
cout<<"Son构造函数"<<endl;
}
void test(){
cout<<name<<"--"<<*age<<endl;
}
~Son(){
cout<<"Son析构函数"<<endl;
if(age!=NULL){
delete age;
age=NULL;
}
}
};
int main(){
Base *b=new Son("killer",23);
delete b;
return 0;
}
运行结果:
解决方法:
将父类中的析构函数写成虚析构。
class Base{
public:
Base(){
cout<<"Base构造函数"<<endl;
}
//虚析构
virtual ~Base(){
cout<<"Base析构函数"<<endl;
}
virtual void test()=0;
};
class Son:public Base{
public:
string name;
int *age;
Son(string _name,int _age){
name=_name;
age=new int(_age);
cout<<"Son构造函数"<<endl;
}
void test(){
cout<<name<<"--"<<*age<<endl;
}
~Son(){
cout<<"Son析构函数"<<endl;
if(age!=NULL){
delete age;
age=NULL;
}
}
};
int main(){
Base *b=new Son("killer",23);
delete b;
return 0;
}
意义
- 组织结构清晰
- 可读性强
- 利于前期和后期的扩展和维护
抽象类
有纯虚函数的类叫抽象类,有纯虚析构的类也属于抽象类
纯虚函数
语法:virtual 返回值类型 函数名 (参数列表)= 0 ;
抽象类特点
- 无法实例化
- 抽象类的子类必须重写抽象类中的纯虚函数,否则依然是抽象类/font>