引例:
- #include
- using namespace std;
- class Animal
- {
- public:
- void speak()
- {
- cout<<"动物在说话"<
- }
- };
- class Cat:public Animal
- {
- public:
- void speak()
- {
- cout<<"小猫在说话"<
- }
- };
- void DoSpeak(Animal &animal)
- {
- animal.speak();
- }
- void test01()
- {
- Cat cat;
- DoSpeak(cat);
- }
- int main()
- {
- test01();
- }
这段代码会显示动物在说话,但函数中的本意是想显示小猫在说话。
因为
void DoSpeak(Animal &animal)
的地址在编译阶段已经被绑定了,
如果想执行小猫在说话,那么就要使用动态多态的技术,使函数的地址在运行时绑定。
零、什么是多态?
多态是面向对象编程中的一个概念,指同一个方法或操作可以被不同的对象调用,产生不同的结果。也可以理解为同一个接口,不同的实现方式。
在多态的概念中,通过继承,子类可以重写父类的方法,从而实现多态。例如,一个父类有一个某方法,子类可以继承该父类,并重写该方法,从而实现不同的行为。
多态的好处在于,可以增强代码的灵活性和可扩展性,让代码更加面向对象。
一、满足动态多态的条件:
1.有继承关系
2.子类重写父类的虚函数(重写:函数除了函数体,函数头一模一样)
二 、动态多态的使用:
父类的指针或引用 执行子类对象
父类中的被重写函数前面加上virtual,变成虚函数。
例如:void DoSpeak(Animal &animal) 中的 Animal &animal
- #include
- using namespace std;
- class Animal
- {
- public:
- virtual void speak()//虚函数
- {
- cout<<"动物在说话"<
- }
- };
- class Cat:public Animal
- {
- public:
- void speak()
- {
- cout<<"小猫在说话"<
- }
- };
- void DoSpeak(Animal &animal)
- {
- animal.speak();
- }
- void test01()
- {
- Cat cat;
- DoSpeak(cat);
- }
- int main()
- {
- test01();
- }
三、多态的原理
- #include
- using namespace std;
- class Animal
- {
- public:
- void speak()
- {
- cout<<"动物在说话"<
- }
- };
- class Cat:public Animal
- {
- public:
- void speak()
- {
- cout<<"小猫在说话"<
- }
- };
- void DoSpeak(Animal &animal)
- {
- animal.speak();
- }
- void test01()
- {
- Cat cat;
- DoSpeak(cat);
- }
- void test02()
- {
- cout<<"sizeof(Animal) = "<<sizeof(Animal)<
- }
- int main()
- {
- //test01();
- test02();
- }
父类中的speak没有变成虚函数前,父类的大小时1字节,也就是一个空类
- #include
- using namespace std;
- class Animal
- {
- public:
- void speak()
- {
- cout<<"动物在说话"<
- }
- };
- class Cat:public Animal
- {
- public:
- void speak()
- {
- cout<<"小猫在说话"<
- }
- };
- void DoSpeak(Animal &animal)
- {
- animal.speak();
- }
- void test01()
- {
- Cat cat;
- DoSpeak(cat);
- }
- void test02()
- {
- cout<<"sizeof(Animal) = "<<sizeof(Animal)<
- }
- int main()
- {
- //test01();
- test02();
- }
加了virtual变成虚函数后,父类大小是8字节,多了一个指针。
- #include
- using namespace std;
- class Animal
- {
- public:
- virtual void speak()
- {
- cout<<"动物在说话"<
- }
- };
- class Cat:public Animal
- {
- public:
- void speak()
- {
- cout<<"小猫在说话"<
- }
- };
- void DoSpeak(Animal &animal)
- {
- animal.speak();
- }
- void test01()
- {
- Cat cat;
- DoSpeak(cat);
- }
- void test02()
- {
- cout<<"sizeof(Animal) = "<<sizeof(Animal)<
- }
- int main()
- {
- //test01();
- test02();
- }
有虚函数的类会包含一个虚函数指针vfptr
vfptr会指向一个vftable(虚函数表),vftalble中会存放该虚函数的地址。子类中重写虚函数时会将子类的vftable中的地址覆盖为子类中的虚函数地址。

四、多态案例(计算器)
不用多态的版本:
- #include
- using namespace std;
- class Calculator
- {
- public:
- int getResult(string oper)
- {
- if(oper=="+")
- return m_Nums1+m_Nums2;
- else if(oper=="-")
- return m_Nums1-m_Nums2;
- else if(oper=="*")
- return m_Nums1*m_Nums2;
- }
- int m_Nums1;
- int m_Nums2;
- };
-
- void test01()
- {
- Calculator c;
- c.m_Nums1=10;
- c.m_Nums2=10;
- cout<
"+"<"="<getResult("+")< - cout<
"-"<"="<getResult("-")< - cout<
"*"<"="<getResult("*")< - }
- int main()
- {
- test01();
- }
如果想要对计算器的操作方式有拓展,需要修改源码。
真正开发中提倡 “开闭原则”
对拓展进行开放,对修改进行关闭
用多态的版本:
- #include
- using namespace std;
- class AbstractCalculator
- {
- public:
- virtual int getResult()
- {
- return 0;
- }
- int m_Nums1;
- int m_Nums2;
- };
- class AddCalculator:public AbstractCalculator
- {
- public:
- int getResult()
- {
- return m_Nums1+m_Nums2;
- }
- };
- class SubCalculator:public AbstractCalculator
- {
- public:
- int getResult()
- {
- return m_Nums1-m_Nums2;
- }
- };
- class MulCalculator:public AbstractCalculator
- {
- public:
- int getResult()
- {
- return m_Nums1*m_Nums2;
- }
- };
- void test01()
- {
- AbstractCalculator *p=new AddCalculator;
- p->m_Nums1=100;
- p->m_Nums2=100;
- cout<
m_Nums1<<"+"<m_Nums2<<"="<getResult()< - delete p;
- p=new SubCalculator;
- p->m_Nums1=100;
- p->m_Nums2=100;
- cout<
m_Nums1<<"*"<m_Nums2<<"="<getResult()< - delete p;
- p=new MulCalculator;
- p->m_Nums1=100;
- p->m_Nums2=100;
- cout<
m_Nums1<<"*"<m_Nums2<<"="<getResult()< - delete p;
- }
- int main()
- {
- test01();
- }
五、纯虚函数与抽象类

六、多态案例(制作饮品)
- #include
- using namespace std;
- class AbstractDrinking
- {
- public:
- //煮水
- virtual void Boil()=0;
- //冲泡
- virtual void Brew()=0;
- //倒入杯中
- virtual void PourInCup()=0;
- //加入辅料
- virtual void PutSomething()=0;
- //制作饮品
- void makeDrink()
- {
- Boil();
- Brew();
- PourInCup();
- PutSomething();
- }
- };
- class coffee:public AbstractDrinking
- {
- public:
- //煮水
- virtual void Boil()
- {
- cout<<"煮农夫山泉"<
- }
- //冲泡
- virtual void Brew()
- {
- cout<<"冲泡咖啡"<
- }
- //倒入杯中
- virtual void PourInCup()
- {
- cout<<"倒入杯中"<
- }
- //加入辅料
- virtual void PutSomething()
- {
- cout<<"加入糖与牛奶"<
- }
- };
- class tea:public AbstractDrinking
- {
- public:
- //煮水
- virtual void Boil()
- {
- cout<<"煮矿泉水"<
- }
- //冲泡
- virtual void Brew()
- {
- cout<<"冲茶叶"<
- }
- //倒入杯中
- virtual void PourInCup()
- {
- cout<<"倒入杯中"<
- }
- //加入辅料
- virtual void PutSomething()
- {
- cout<<"加入枸杞"<
- }
- };
- void doWork(AbstractDrinking *abs)
- {
- abs->makeDrink();
- delete abs;
- }
- void test01()
- {
- doWork(new coffee);
- cout<<"------------------"<
- doWork(new tea);
- }
- int main()
- {
- test01();
- }
七、虚析构与纯虚析构
父类指针在析构时候,不会调用子类中的析构函数,导致子类如果右堆区属性,出现内存泄露
- #include
- using namespace std;
- class Animal
- {
- public:
- Animal()
- {
- cout<<"Animal的构造函数调用"<
- }
- virtual void speak()=0;
- ~Animal()
- {
- cout<<"Animal的析构函数调用"<
- }
- };
- class Cat:public Animal
- {
- public:
- Cat(string name)
- {
- cout<<"Cat的构造函数调用"<
- m_Name=new string(name);
- }
- void speak()
- {
- cout<<*m_Name<<"小猫在说话"<
- }
- ~Cat()
- {
- if(m_Name!=nullptr)
- {
- cout<<"Cat的析构函数调用"<
- delete m_Name;
- m_Name=nullptr;
- }
- }
- string *m_Name;
- };
- void test01()
- {
- Animal *animal=new Cat("tom");
- animal->speak();
- delete animal;
- }
- int main()
- {
- test01();
- }
在父类的析构函数前加上virtual,就可以解决问题。
纯虚析构:virtual ~Animal()=0;
类外
Animal::~Animal()
{
cout<<"Animal纯虚析构函数调用"< }
纯虚析构必须要有具体的函数实现
- #include
- using namespace std;
- class Animal
- {
- public:
- Animal()
- {
- cout<<"Animal的构造函数调用"<
- }
- virtual void speak()=0;
- /*virtual~Animal()
- {
- cout<<"Animal的虚析构函数调用"<
- }
- */
- virtual~Animal()=0;
- };
- Animal::~Animal()
- {
- cout<<"Animal纯虚析构函数调用"<
- }
- class Cat:public Animal
- {
- public:
- Cat(string name)
- {
- cout<<"Cat的构造函数调用"<
- m_Name=new string(name);
- }
- void speak()
- {
- cout<<*m_Name<<"小猫在说话"<
- }
- ~Cat()
- {
- if(m_Name!=nullptr)
- {
- cout<<"Cat的析构函数调用"<
- delete m_Name;
- m_Name=nullptr;
- }
- }
- string *m_Name;
- };
- void test01()
- {
- Animal *animal=new Cat("tom");
- animal->speak();
- delete animal;
- }
- int main()
- {
- test01();
- }
-
相关阅读:
嵌入式系统日志轮转:实现与性能考量
vs code 添加vue3代码模板方法
redis主从
面向切面编程的一些概念
linux内存空间深度清理
Springboot建筑造价师资格考试应试网站毕业设计源码260839
Selenium——利用input标签上传文件
【Pytorch学习】Transforms
WSL+vscode配置miniob环境
仿真测试断开服务器公网连接
-
原文地址:https://blog.csdn.net/ghblk/article/details/132699598