大家好,本篇文章主要讲的是C++类和对象之多态详解,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
多态基本概念和原理剖析
多态
:多态是C++面向对象的三大特性之一。多态分为静态多态和动态多态。
静态多态
:函数重载和运算符重载属于静态多态,复用函数名。
动态多态
:派生类和虚函数实现运行时多态。
区别:
静态多态的函数地址早绑定,编译阶段确定函数地址。
动态多态的函数地址晚绑定,运行阶段确定函数地址。
#include <iostream>
using namespace std;
//动态多态满足条件
//1、有继承关系
//2、子类重写父类的虚函数 重写是指返回值 函数名 参数完全一样 重载是指函数名相同
//动态多态使用
//父类的指针或引用,指向子类对象
//当子类重写父类的虚函数,子类中的虚函数表内部会替换成子类的虚函数地址,将&Animal::speak替换成&Cat::speak
//动物类
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;
}
};
//执行说话的函数
//地址早绑定,在编译阶段就确定了函数的地址
//虽然想让猫说话,但是这里输出了动物在说话 因为地址早绑定
//如果想让猫说话,这个函数的地址就不能提前绑定,需要在运行阶段进行绑定,也即地址晚绑定
void DoSpeak(Animal &animal) {
animal.speak();
}
void test01() {
Cat cat;
DoSpeak(cat); //虽然想让猫说话,但是这里输出了动物在说话,因为地址早绑定,若父类改为虚函数地址晚绑定,就是猫在说话
Dog dog;
DoSpeak(dog);
}
void test02() {
cout << "size of Animal = " << sizeof(Animal) << endl;
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
多态案例1 计算器类
分别使用普通写法和多态技术,设计实现两个操作数进行运算的计算器类。
多态优点
:
1、代码组织结构清晰
2、可读性强
3、便于前期和后期的扩展与维护
#include<iostream>
using namespace std;
//普通写法
class Calculate {
public:
int GetResult(string oper) {
if (oper == "+") {
return num1 + num2;
}
else if (oper == "-") {
return num1 - num2;
}
else if (oper == "*") {
return num1 * num2;
}
//如果想扩展新的功能,需要修改原码,在实际开发中,提倡开闭原则:对扩展进行开发,对修改进行关闭
}
int num1;
int num2;
};
void test01() {
//创建计算器对象
Calculate c;
c.num1 = 10;
c.num2 = 10;
cout << c.num1 << " + "<< c.num2 << " = " <<c.GetResult("+") << endl;
cout << c.num1 << " - "<< c.num2 << " = " <<c.GetResult("-") << endl;
cout << c.num1 << " * "<< c.num2 << " = " <<c.GetResult("*") << endl;
}
//利用多态实现计算器
//多态的优点
//1、组织结构清晰
//2、可读性强
//3、对于前期扩展和后期维护性能高
//实现计算器抽象类
class AbstractCalculate {
public:
virtual int Result() {
return 0;
}
int m_num1;
int m_num2;
};
//加法计算器类
class AddCalculate : public AbstractCalculate{
public:
int Result() {
return m_num1 + m_num2;
}
};
//减法计算器类
class SubCalculate : public AbstractCalculate {
public:
int Result() {
return m_num1 - m_num2;
}
};
//乘法计算器类
class MulCalculate : public AbstractCalculate {
public:
int Result() {
return m_num1 * m_num2;
}
};
void test02() {
//多态使用指针
//父类指针或者引用指向子类对象
//加法运算
AbstractCalculate* p = new AddCalculate;
p->m_num1 = 100;
p->m_num2 = 100;
cout << p->m_num1 << " + " << p->m_num2 << " = " << p->Result() <<endl;
//用完后释放新开辟的堆区数据
delete p;
//减法运算
p = new SubCalculate;
p->m_num1 = 100;
p->m_num2 = 100;
cout << p->m_num1 << " - " << p->m_num2 << " = " << p->Result() << endl;
delete p;
//乘法运算
p = new MulCalculate;
p->m_num1 = 100;
p->m_num2 = 100;
cout << p->m_num1 << " * " << p->m_num2 << " = " << p->Result() << endl;
delete p;
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
纯虚函数和抽象类
在多态中,通常父类函数的虚函数是无意义的,主要都是调用子类重写的内容。
因此可以将虚函数改为纯虚函数。
当类中有了纯虚函数,这个类也称为抽象类。
.
抽象类特点
:无法实例化对象;子类必须重新抽象类中的纯虚函数,否则也属于抽象类。
#include<iostream>
using namespace std;
//纯虚函数语法 virtual 返回值 函数名(参数列表) = 0;
//当类中有了纯虚函数,这个类也称为抽象类,抽象类不能实例化对象,子类必须重写抽象类里的纯虚函数,不然子类也属于抽象类
class Base {
public:
//纯虚函数 只要有纯虚函数这个类就是抽象类
virtual void func() = 0;
};
class Son :public Base {
public:
void func() {
cout << "Son类下的func函数调用" << endl;
}
};
void test01() {
/*抽象类是无法实例化对象的
Base b;
new Base;*/
Son s;
s.func();
//多态方式调用 父类的指针new一个子类
Base* p = new Son;
p->func();
delete p;
}
int main() {
test01();
system("pause");
return 0;
}
多态案例2 制作饮品
利用多态技术实现制作饮品的流程,提供抽象制作饮品基类,提供子类制作咖啡和茶。
这里使用指针多态而不是引用的原因在于,引用需先实例化对象,但是有纯虚函数的抽象类无法实例化对象。
#include<iostream>
using namespace std;
class AbstractDrinking {
public:
//煮水
virtual void Boil() = 0;
//冲泡
virtual void Brew() = 0;
//倒入杯中
virtual void DropInCup() = 0;
//加入辅料
virtual void AddSomething() = 0;
//制作饮品
void MakeDrink() {
Boil();
Brew();
DropInCup();
AddSomething();
}
};
//制作咖啡流程
class Coffee : public AbstractDrinking {
public:
//煮水
virtual void Boil() {
cout << "煮一壶沸水" << endl;
}
//冲泡
virtual void Brew() {
cout << "冲泡咖啡粉" << endl;
}
//倒入杯中
virtual void DropInCup() {
cout << "将咖啡倒入杯中" << endl;
}
//加入辅料
virtual void AddSomething() {
cout << "将牛奶和白糖加入咖啡中" << endl;
}
};
//制作茶水流程
class Tea : public AbstractDrinking {
public:
//煮水
virtual void Boil() {
cout << "煮一壶沸水" << endl;
}
//冲泡
virtual void Brew() {
cout << "冲泡西湖龙井" << endl;
}
//倒入杯中
virtual void DropInCup() {
cout << "将茶水倒入杯中" << endl;
}
//加入辅料
virtual void AddSomething() {
cout << "将枸杞加入茶水中" << endl;
}
};
//制作饮品函数
void DoWork(AbstractDrinking *p) { //这里相当于 AbstractDrinking *p = new Coffee;
//若这里不使用指针,改为引用,test01的函数就不能使用
//因为引用是先实例化对象再调用对象,抽象类无法实例化对象
p->MakeDrink();
delete p; //防止内存泄露,使用后记得释放内存
}
void test01() {
//想喝一杯咖啡
DoWork(new Coffee);
cout << "-----------------------------------" << endl;
//想喝一杯西湖龙井茶
DoWork(new Tea);
}
int main() {
test01();
system("pause");
return 0;
}
虚析构和纯虚析构
使用多态技术时,如果有子类开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。解决该问题的方法是
将父类中的析构函数改为虚析构或者纯虚析构
。
虚析构和纯虚析构共性
:
1、可以解决父类指针释放子类对象
2、都需要有具体的函数实现
虚析构和纯虚析构区别
:
如果是纯虚析构,该类属于抽象类,无法实例化对象。
#include<iostream>
#include<string>
using namespace std;
//动物类
class Animal {
public:
Animal() {
cout << "这是Animal的构造函数调用" << endl;
}
//纯虚函数
virtual void Speak() = 0;
//为了走子类的虚构函数,需要将父类的虚构函数改为虚析构
/*virtual ~Animal() {
cout << "这是Animal的析构函数调用" << endl;
}*/
//纯虚析构函数 父类的堆区可能也被开辟需要释放,因此父类的析构也必须需要声明,也需要实现
//有纯虚析构后这个类也属于抽象类,无法实例化对象
virtual ~Animal() = 0;
};
Animal:: ~Animal() {
cout << "这是Animal类的写在外部的纯虚析构函数调用" << endl;
}
//猫类
class Cat : public Animal{
public:
//Cat类的构造函数
Cat(string name) {
cout << "这是Cat类的构造函数" << endl;
m_name = new string(name);
}
void Speak() {
cout << *m_name<<"小猫的叫声" << endl;
}
~Cat() {
if (m_name != NULL) {
//父类指针在析构的时候,不会调用子类中的析构函数,导致子类中有堆区开辟的数据没有被释放干净导致内存泄露
cout << "这是Cat类的析构函数" << endl;
delete m_name;
m_name = NULL;
}
}
string *m_name;
};
void test01() {
Animal* p = new Cat("Tom"); //如果子类中没有堆区数据,可以不写虚析构或纯虚析构函数
p->Speak();
delete p; //堆区开辟以后,释放指针
}
int main() {
test01();
system("pause");
return 0;
}
多态案例3 电脑组装
电脑主要部件为CPU,显卡和内存条。将每个零件封装出抽象基类,并提供不同的厂商生产不同的零件。创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口。测试时组装三台不同的电脑进行工作。
#include<iostream>
using namespace std;
//每个零件都封装出抽象的基类
//CPU类
class Cpu {
public:
//抽象的计算函数
virtual void Calculate() = 0;
//virtual ~Cpu() = 0;
};
//Cpu::~Cpu() {
// cout << "Cpu类写在外部的纯虚析构函数调用" << endl;
/
本文标题为:C++类和对象之多态详解
基础教程推荐
- 如何定义双括号/双迭代器运算符,类似于向量的向量? 2022-01-01
- 什么是T&&(双与号)在 C++11 中是什么意思? 2022-11-04
- C++ 程序在执行 std::string 分配时总是崩溃 2022-01-01
- C++,'if' 表达式中的变量声明 2021-01-01
- 调用std::Package_TASK::Get_Future()时可能出现争用情况 2022-12-17
- 如何在 C++ 中处理或避免堆栈溢出 2022-01-01
- 运算符重载的基本规则和习语是什么? 2022-10-31
- C++ 标准:取消引用 NULL 指针以获取引用? 2021-01-01
- 设计字符串本地化的最佳方法 2022-01-01
- 您如何将 CreateThread 用于属于类成员的函数? 2021-01-01