C++面向对象编程——多态
·
多态的基本概念
多态是C++面向对象三大特性之一
1、多态分为两类
①静态多态:函数重载和运算符重载属于静态多态,复用函数名
②动态多态:派生类和虚函数实现运行时多态
2、静态多态和动态多态区别
①静态多态的函数地址早绑定 - 编译阶段确定函数地址
②动态多态的函数地址晚绑定 - 运行阶段确定函数地址
3、多态满足条件
①有继承关系
②子类重写父类中的虚函数
4、多态使用条件
①父类指针或引用指向子类对象
5、重写
函数返回值类型 函数名 参数列表 完全一致称为重写

class Animal{
public:
//虚函数
virtual void speak(){
cout <<"动物在说话" <<endl;.
}
};
class Cat:public Animal{
public:
//重写:函数返回值类型 函数名 参数列表完全相同
void speak(){
cout <<"小猫在说话"<<endl;
}
};
class Dog:public Animal{
public:
void speak(){
cout <<"小狗在说话"<<endl;
}
};
//执行说话函数
//地址早绑定 在编译阶段确定函数地址
//如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定,地址晚绑定
//动态多态满足条件
//1、有继承关系
//2、子类重写父类的虚函数
//动态多态使用
//父类指针或者引用 执行子类对象
void doSpeak(Animal &animal){
animal.speak();
}
void test01(){
Cat cat;
doSpeak(cat);
Dog dog;
doSpeak(dog);
}
void test02(){
cout <<"sizeof(Animal):"<<sizeof(Animal)<<endl;
}
多态案例—计算器类
分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类
多态的优点:
①代码组织结构清晰
②可读性强
③利于前期和后期的扩展以及维护
//普通写法
class Calculator{
public:
int getResult(string oper){
if(oper=="+"){
return m_Num1+m_Num2;
}
else if(oper=="-"){
return m_NUm1-m_Num2;
}
else if(oper=="*"){
return m_NUm1*m_Num2;
}
//如果想扩展新的功能,需要修改源码
}
int m_Num1;
int m_Num2;
};
void test01(){
Calculator c;
c.m_Num1 =10;
c.m_Num2 =10;
cout<< c.m Num1<<"+"<<c.m Num2<<"=" <<c.getResult("+")<< endl;
cout<< c.m Num1<<"-"<<c.m Num2<<"=" <<c.getResult("-")<< endl;
cout<< c.m Num1<<"*"<<c.m Num2<<"=" <<c.getResult("*")<< endl;
}
//利用多态实现计算器
//实现计数器抽象类
class AbstructCalculator{
public:
virtual int getResult(){
return 0;
}
int m_Num1;
int m_Num2;
};
class AddCalculator:public AbstructCalculator{
public:
int getResult(){
return m_Num1+m_Num2;
}
};
class SubCalculator:public AbstructCalculator{
public:
int getResult(){
return m_Num1-m_Num2;
}
};
class MulCalculator:public AbstructCalculator{
public:
int getResult(){
return m_Num1*m_Num2;
}
};
void test02(){
//多态使用条件
//父类指针或者引用指向子类对象
//加法运算
AbstructCalculator *abc =new AddCalculator;
abc->m_Num1 =10;
abc->m_Num2 =10;
cout<< abc->m Num1<<"+"<<abc->m Num2<<"=" <<abc->getResult()<< endl;
//用完后记得销毁
delete abc;
}
纯虚函数和抽象类
1、在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写内容
因此可以把虚函数改为纯虚函数
2、纯虚函数语法:virtual 返回值类型 函数名(参数列表)=0;
3、当类中有了纯虚函数,这个类也称为抽象类
4、抽象类特点:
①无法实例化对象
②子类必须重写抽象类中的纯虚函数,否则也属于抽象类
class Base{
public:
virtual void func()=0;
};
class Son:public Base{
public:
};
class Son1:public Base{
public:
virtual void func(){};
};
void test01(){
Base b;//抽象类无法实例化对象
new Base;//抽象类无法实例化对象
Son s;//子类必须重写抽象类中的纯虚函数,否则也属于抽象类
Son1 s;
Base *base =new Son;
base->func();
}

虚析构和纯虚析构
1、多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构函数
解决方法:将父类中的析构函数改为虚析构或者纯虚析构
2、虚析构和纯虚析构共性
①可以解决父类指针释放子类对象
②都需要有具体的函数实现
3、虚析构和纯虚析构区别
①如果是纯虚析构,该类属于抽象类,无法实例化对象
4、虚析构语法:virtual ~类名(){}
5、纯虚析构语法:virtual ~类名()=0 类名::~类名(){}
6、总结
①虚析构或纯虚析构就是用来解决通过父指针释放子类对象
②如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
③拥有纯虚析构函数的类也属于抽象类
class Animal{
public:
Animal(){
cout<<"Animal构造函数" <<endl;
}
//利用虚析构可以解决父类指针释放子类对象时不干净的问题
virtual ~Animal(){
cout<<"Animal虚析构函数" <<endl;
}
//纯虚析构 需要声明也需要有实现 纯虚析构,该类属于抽象类,无法实例化对象
virtual void speak()=0;
virtual void speak()=0;
};
Animal::~Animal(){
cout<<"Animal纯虚析构函数" <<endl;
}
class Cat:public Animal{
public:
Cat(String name){
cout<<"小猫构造函数" <<endl;
m_Name=new string(name);
}
~Cat(){
if(m_Name!=NULL){
cout<<"小猫析构函数" <<endl;
delete m_Name;
m_Name=NULL:
}
};
virtual void speak(){
cout <<*m_Name<<"小猫在说话" <<endl;
};
string *m_Name;
};
void test01(){
Animal* animal =new Cat("Tom");
animal->speak();
//父亲指针在析构时候 不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏
delect animal;
};更多推荐


所有评论(0)