文章目录
4 类和对象
4.7 多态
4.7.1 多态的基本介绍
1.多态的基本语法
C++ 重写:virtual 函数值返回类型 函数名 参数列表
2.多态满足条件
- 有继承关系
- 子类重写父类中的虚函数
3.多态使用条件
- 父类指针或引用指向子类对象
4.多态的优点
- 代码组织结构清晰
- 可读性强
- 利于前期和后期的扩展以及维护
例4.7.1 class animal
#include <iostream>
using namespace std;
// 多态
// 动物类
class Animal
{
public:
// 虚函数
virtual void speak()
{
cout << "动物在说话" << endl;
}
};
// 猫类
class Cat :public Animal
{
public:
// 重写 函数返回值类型 函数名 参数列表 完全相同
virtual void speak()
{
cout << "小猫在说话" << endl;
}
};
// 狗类
class Dog :public Animal
{
public:
// 重写 函数返回值类型 函数名 参数列表 完全相同
virtual void speak()
{
cout << "小狗在说话" << endl;
}
};
// 执行说话的函数
// 地址早已绑定 在编译阶段确定函数地址
// 如果想执行让猫说话,那么这个函数不能提前绑定,要在运行阶段进行绑定,地址晚绑定
void doSpeak(Animal &animal) // Animal & animal == cat;
{
animal.speak();
}
void test01()
{
Cat cat;
doSpeak(cat);
Dog dog;
doSpeak(dog);
}
int main()
{
test01();
return 0;
}
结果如下:
小猫在说话
小狗在说话
Process returned 0 (0x0) execution time : 0.085 s
Press any key to continue.
4.7.2 纯虚函数和抽象类
在多态中,通常父类中的虚函数的实现是没有意义的,主要是调用子类重写的内容,因此可以将虚函数改为纯虚函数。
纯虚函数的语法:
virtual 返回值类型 函数名 (形参列表) = 0;
当类中有了纯虚函数,这个类也称为抽象类。
抽象类的特点
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
例4.7.2
#include <iostream>
using namespace std;
// 纯虚函数和抽象类
class Base
{
public:
// 纯虚函数
// 只要有一个纯虚函数,这个类称为抽象类
// 抽象类的特点:
// 1.无法实例化对象
// 2.抽象类的子类 必须重写父类中的纯虚函数,否则也属于抽象类
virtual void func() = 0;
};
class Son :public Base
{
public:
virtual void func()
{
cout << "output the son result" << endl;
}
};
void test01()
{
// Base b; // 抽象类无法实例化对象
// new Base; // 抽象类无法实例化对象
Son s; // 子类必须重写父类中的纯虚函数,否则无法实现实例化对象。
Base * base = new Son;
base->func();
}
int main()
{
test01();
return 0;
}
结果如下:
output the son result
Process returned 0 (0x0) execution time : 0.083 s
Press any key to continue.
4.7.3 虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放的时候无法调用到子类的析构代码。
解决方式:将父类中的析构函数改为虚析构或者纯虚析构。
虚析构和纯虚析构的共性:
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
纯虚析构和虚析构的区别:
如果是是纯虚析构函数,该类属于抽象类,无法实例化对象。
虚析构语法:
c++ virtual ~类名() { ... }
纯虚析构的语法:
C++ virtual ~类名() = 0;
类名::类名() { ... }
例4.7.3.1 调用虚析构函数
#include <iostream>
#include <string>
using namespace std;
// 虚析构和纯虚析构
class Animal
{
public:
Animal()
{
cout << "Animal的构造函数调用" << endl;
}
// 利用虚析构函数可以解决父类指针释放子类对象时不干净的问题
virtual ~Animal()
{
cout << "Animal的虚析构函数调用" << endl;
}
// 纯虚析构 需要声明 需要实现
//virtual ~Animal() = 0;
// 纯虚函数
virtual void speak() = 0;
};
/*Animal::~Animal()
{
cout << "Animal的纯虚析构函数调用" << endl;
}*/
class Cat :public Animal
{
public:
Cat(string name)
{
cout << "Cat的构造函数调用" << endl;
m_Name = new string (name);
}
virtual 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 * animal = new Cat("Tom");
animal->speak();
// 父类指针在析构的时候 不会调用子类中的析构函数,导致子类如果有堆区属性,出现内存泄露
delete animal;
}
int main()
{
test01();
return 0;
}
结果如下:
Animal的构造函数调用
Cat的构造函数调用
Tom小猫在说话
Cat的虚析构函数调用
Animal的虚析构函数调用
Process returned 0 (0x0) execution time : 0.113 s
Press any key to continue.
例4.7.3.2 调用纯虚析构函数
#include <iostream>
#include <string>
using namespace std;
// 虚析构和纯虚析构
class Animal
{
public:
Animal()
{
cout << "Animal的构造函数调用" << endl;
}
// 利用虚析构函数可以解决父类指针释放子类对象时不干净的问题
/*virtual ~Animal()
{
cout << "Animal的虚析构函数调用" << endl;
}*/
// 纯虚析构 需要声明 需要实现
virtual ~Animal() = 0;
// 纯虚函数
virtual void speak() = 0;
};
Animal::~Animal()
{
cout << "Animal的纯虚析构函数调用" << endl;
}
class Cat :public Animal
{
public:
Cat(string name)
{
cout << "Cat的构造函数调用" << endl;
m_Name = new string (name);
}
virtual 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 * animal = new Cat("Tom");
animal->speak();
// 父类指针在析构的时候 不会调用子类中的析构函数,导致子类如果有堆区属性,出现内存泄露
delete animal;
}
int main()
{
test01();
return 0;
}
结果如下:
Animal的构造函数调用
Cat的构造函数调用
Tom小猫在说话
Cat的虚析构函数调用
Animal的纯虚析构函数调用
Process returned 0 (0x0) execution time : 0.029 s
Press any key to continue.
4.7.4 多态案例一-计算器类
例4.7.4 class calculator
#include <iostream>
#include <string>
using namespace std;
// 分别利用基本写法和多态技术实现计算器
// 普通写法
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;
}
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;
cout << c.m_Num1 << " / " << c.m_Num2 << " = " << c.getResult("/") << endl;
}
// 利用多态实现计算器
// 实现计算器抽象类
class AbstractCalculator
{
public:
virtual int getResult()
{
return 0;
}
int m_Num1;
int m_Num2;
};
// 加法计算器类
class Add :public AbstractCalculator
{
virtual int getResult()
{
return m_Num1 + m_Num2;
}
};
// 减法计算器类
class Sub :public AbstractCalculator
{
virtual int getResult()
{
return m_Num1 - m_Num2;
}
};
// 乘法计算器类
class Mul :public AbstractCalculator
{
virtual int getResult()
{
return m_Num1 * m_Num2;
}
};
// 除法计算器类
class Div :public AbstractCalculator
{
virtual int getResult()
{
return m_Num1 / m_Num2;
}
};
void test02()
{
// 多态使用条件
// 父类指针或者引用指向子类对象
// 加法运算
AbstractCalculator * abc = new Add;
abc->m_Num1 = 20;
abc->m_Num2 = 20;
cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
// 减法运算
abc = new Sub;
abc->m_Num1 = 20;
abc->m_Num2 = 20;
cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
// 乘法运算
abc = new Mul;
abc->m_Num1 = 20;
abc->m_Num2 = 20;
cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
// 除法运算
abc = new Div;
abc->m_Num1 = 20;
abc->m_Num2 = 20;
cout << abc->m_Num1 << " / " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
}
int main()
{
cout << "test01" << endl;
test01();
cout << endl;
cout << "test02" << endl;
test02();
return 0;
}
结果如下:
test01
10 + 10 = 20
10 - 10 = 0
10 * 10 = 100
10 / 10 = 1
test02
20 + 20 = 40
20 - 20 = 0
20 * 20 = 400
20 / 20 = 1
Process returned 0 (0x0) execution time : 0.028 s
Press any key to continue.
4.7.5 多态案例二-制作饮品
例4.7.5 make drinking
#include <iostream>
using namespace std;
// make drinking
class BaseDrinking
{
public:
virtual ~BaseDrinking()
{
// 基类析构函数实现
}
// 煮水
virtual void Boil() = 0;
// 冲泡
virtual void Brew() = 0;
// 倒入杯中
virtual void PourInCup() = 0;
// 加入辅料
virtual void AddSomethings() = 0;
// 制作饮品
void MakeDrink()
{
Boil();
Brew();
PourInCup();
AddSomethings();
}
};
// 制作咖啡
class Coffee :public BaseDrinking
{
public:
// 煮水
virtual void Boil()
{
cout << "煮农夫山泉" << endl;
}
// 冲泡
virtual void Brew()
{
cout << "冲泡咖啡" << endl;
}
// 倒入杯中
virtual void PourInCup()
{
cout << "倒入咖啡杯中" << endl;
}
// 加入辅料
virtual void AddSomethings()
{
cout << "加入糖和牛奶" << endl;
}
};
// 制作茶
class Tea :public BaseDrinking
{
public:
// 煮水
virtual void Boil()
{
cout << "煮娃哈哈" << endl;
}
// 冲泡
virtual void Brew()
{
cout << "冲泡茶" << endl;
}
// 倒入杯中
virtual void PourInCup()
{
cout << "倒入茶杯中" << endl;
}
// 加入辅料
virtual void AddSomethings()
{
cout << "加入柠檬" << endl;
}
};
// 制作函数
void doWork(BaseDrinking * base)
{
base->MakeDrink();
delete base; // 释放
}
void test01()
{
// 制作咖啡
doWork(new Coffee);
cout << "---------------------" << endl;
// 制作茶
doWork(new Tea);
}
int main()
{
test01();
return 0;
}
结果如下:
煮农夫山泉
冲泡咖啡
倒入咖啡杯中
加入糖和牛奶
---------------------
煮娃哈哈
冲泡茶
倒入茶杯中
加入柠檬
Process returned 0 (0x0) execution time : 0.078 s
Press any key to continue.
4.7.6 多态案例三-电脑组装
例4.7.6 assembly computer
#include <iostream>
using namespace std;
// 组装电脑
// 抽象不同的零件类
// 抽象CPU类
class CPU
{
public:
virtual ~CPU()
{
}
// 抽象的计算函数
virtual void calculate() = 0;
};
// 抽象显卡类
class VideoCard
{
public:
virtual ~VideoCard()
{
}
// 抽象的显示函数
virtual void display() = 0;
};
// 抽象内存条类
class Memory
{
public:
virtual ~Memory()
{
}
// 抽象的存储函数
virtual void storage() = 0;
};
class Computer
{
public:
Computer(CPU * cpu, VideoCard * vc, Memory *mem)
{
m_cpu = cpu;
m_vc = vc;
m_mem = mem;
}
// 提供工作的函数
void work()
{
// 让零件工作起来,调用接口
m_cpu->calculate();
m_vc->display();
m_mem->storage();
}
// 提供析构函数 释放3个电脑零件
virtual ~Computer()
{
// 释放CPU零件
if (m_cpu != NULL)
{
delete m_cpu;
m_cpu = NULL;
}
// 释放显卡零件
if (m_vc != NULL)
{
delete m_vc;
m_vc = NULL;
}
// 释放内存条零件
if (m_mem != NULL)
{
delete m_mem;
m_mem = NULL;
}
}
private:
CPU * m_cpu; // CPU的零件指针
VideoCard * m_vc; // 显卡零件指针
Memory * m_mem; // 内存条零件指针
};
// 具体厂商
// intel厂商
class IntelCPU :public CPU
{
public:
virtual void calculate()
{
cout << "Intel的CPU开始计算了" << endl;
}
};
class IntelVideoCard :public VideoCard
{
public:
virtual void display()
{
cout << "Intel的显卡开始显示了" << endl;
}
};
class IntelMemory :public Memory
{
public:
virtual void storage()
{
cout << "Intel的内存条开始存储了" << endl;
}
};
// AMD厂商
class AMDCPU :public CPU
{
public:
virtual void calculate()
{
cout << "AMD的CPU开始计算了" << endl;
}
};
class AMDVideoCard :public VideoCard
{
public:
virtual void display()
{
cout << "AMD的显卡开始显示了" << endl;
}
};
class AMDMemory :public Memory
{
public:
virtual void storage()
{
cout << "AMD的内存条开始存储了" << endl;
}
};
void test01()
{
// 第一台电脑零件
CPU * intelcpu = new IntelCPU;
VideoCard * intelCard = new IntelVideoCard;
Memory * intelMem = new IntelMemory;
cout << "第一台电脑开始工作:" << endl;
// 创建第一台电脑
Computer * Computer1 = new Computer(intelcpu, intelCard, intelMem);
Computer1->work();
delete Computer1;
cout << "----------------------" << endl;
cout << "第二台电脑开始工作:" << endl;
// 第二台电脑组装
Computer * Computer2 = new Computer(new AMDCPU, new AMDVideoCard, new AMDMemory);
Computer2->work();
delete Computer2;
}
int main()
{
test01();
return 0;
}
结果如下:
第一台电脑开始工作:
Intel的CPU开始计算了
Intel的显卡开始显示了
Intel的内存条开始存储了
----------------------
第二台电脑开始工作:
AMD的CPU开始计算了
AMD的显卡开始显示了
AMD的内存条开始存储了
Process returned 0 (0x0) execution time : 0.072 s
Press any key to continue.
4.7.7 继承与多态案例
题目如下:
上机实验五 继承與多态,请沿用实验三的实验结果, 以公开(public)继承 Person 类产生一个 Student 类(class)。
以及 Teacher 类。注意: Person 类的 print()成员已经设为虚函数(virtual function)。
将类定义(class definition)写在 Person.h, Date.h, Student.h 及 Teacher.h 中。 将成员函数的定义(member function definition)写在person.cpp, Date.cpp, Student.cpp 及Teacher.cpp 中。在主程序中实例化二个 Person对象,二个 Student 对象,一个 Teacher 对象。并在主程序中定义一个 Person 的指针数组,将数组中的指针分别指向上述五个对象。 利用 for 及指针数组调用数组指针指向的每个对象的 print 成员函数顺序打印对象的内容。
Person
-Name : string
-ID : int
-Address : string
-Birthday: Date
<<constructor>> +Person(name : string = “”, id : int = 0,
address : string = “”, Date = Date())
<<desctructor>> +~Person()
+setName(name : string)
+getName() : string
+setID(id : int)
+getID() : int
+setAddress(address : string)
+getAddress() : string
+setBirthday(d : Date)
+getBirthday() : Date
+virtual print() : void
Date
<<constructor>>+Date(y : int = 0,
m : int = 0, d : int = 0)
<<destructor>>+~Date()
+setYear(y : int) : void
+setMonth(m : int) : void
+setDay(d : int) : void
+getYear() : int
+getMonth() : int
+getDay() : int
+print() : void
-Year : int
-Month : int
-Day : int
Student
-Department:string
-studentID:int
<<constructor>> +Student (name:string=””,
id:int=0, address:string=””, Date=Date(),
Dep:string=””, sID:int=0)
<<destructor>> +~Student()
+setDepartment(Dep:string):void
+setStudentID(sID:int):void
+getDepartment():string
+getStudentID():int
+print():void
Teacher
-Department:string
-Salary:double
<<constructor>> +Teacher(name:string,
id:int=0, address:string=””, Date=Date(),
Dep:string=””,sal:double=0.0)
<<destructor>> +~Teacher()
+setDepartment(Dep:string):void
+setSalary(sal:duble):void
+getDepartment():string
+getSalary():double
+print():void
解答如下:
main.cpp
#include <iostream>
#include "Person.h"
#include "Student.h"
#include "Teacher.h"
using namespace std;
int main()
{
Person p1("Alice", 1, "123 Street");
Person p2("Bob", 2, "456 Avenue");
Student s1("Charlie", 3, "789 Boulevard", Date(2000, 3, 1), "Math", 1001);
Student s2("David", 4, "101 Lane", Date(2001, 7, 22), "Physics", 1002);
Teacher t1("Eve", 5, "202 Road", Date(1975, 1, 1), "Biology", 50000);
Person* people[] = {
&p1, &p2, &s1, &s2, &t1}; // 容器
for (const auto& person : people) // output
{
person->print();
}
return 0;
}
Person.h
#ifndef PERSON_H
#define PERSON_H
#include <iostream>
#include "Date.h"
using namespace std;
class Person
{
public:
Person();
Person(string name, int id, string address);
~Person();
void setPerson(string name, int id, string address);
void setName(string name);
void setID(int id);
void setAddress(string address);
void setBirthday(Date d);
string getName();
int getID();
string getAddress();
Date getBirthday();
virtual void print(); // outPutResult
private:
string Name;
int ID;
string Address;
Date Birthday;
};
#endif // PERSON_H
Student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <iostream>
#include "Person.h"
#include "Date.h"
using namespace std;
class Student :public Person
{
public:
Student();
Student(string name, int id, string address, Date date, string department, int studentID);
~Student();
void setDepartment(string department);
void setStudentID(int studentID);
string getDepartment();
int getStudentID();
virtual void print(); // outPutResult
private:
string Department;
int StudentID;
};
#endif // STUDENT_H
Teacher.h
#ifndef TEACHER_H
#define TEACHER_H
#include "Person.h"
#include "Date.h"
using namespace std;
class Teacher :public Person
{
public:
Teacher();
Teacher(string name, int id, string address, Date date, string department, double salary);
~Teacher();
void setDepartment(string department);
void setSalary(double salary);
string getDepartment();
double getSalary();
virtual void print(); // outPutResult
private:
string Department;
double Salary;
};
#endif // TEACHER_H
Person.cpp
#include "Person.h"
#include <iostream>
using namespace std;
Person::Person()
{
Name = "S.M.Wang";
ID = 070145;
Address = "莲花路200号";
}
Person::Person(string name, int id, string address)
{
setPerson(name, id, address);
}
Person::~Person()
{
//cout << "object Destructor is called" << endl;
}
void Person::setPerson(string name, int id, string address)
{
Name = name;
ID = id;
Address = address;
}
void Person::setName(string name)
{
Name = name;
}
void Person::setID(int id)
{
ID = id;
}
void Person::setAddress(string address)
{
Address = address;
}
string Person::getName()
{
return Name;
}
int Person::getID()
{
return ID;
}
string Person::getAddress()
{
return Address;
}
void Person::setBirthday(Date d) // 调用的形参是类
{
Birthday = d;
}
Date Person::getBirthday() // 返回的是类
{
return Birthday;
}
void Person::print()
{
cout << "Name:" << getName() << endl;
cout << "ID:" << getID() << endl;
cout << "Address:" << getAddress() << endl;
cout << "Birthday:" << getBirthday().getYear(); // getBirthday()返回的是类,再调用类中的子函数满足cout的返回值。
cout << " " << getBirthday().getMonth();
cout << " " << getBirthday().getDay() << endl;
cout << endl;
}
Student.cpp
#include "Student.h"
#include <iostream>
using namespace std;
Student::Student()
{
}
Student::Student(string name, int id, string address, Date date, string department, int studentID)
{
setPerson(name, id, address);
setBirthday(date);
setDepartment(department);
setStudentID(studentID);
}
Student::~Student()
{
//cout << "object Destructor is called" << endl;
}
void Student::setDepartment(string department)
{
Department = department;
}
string Student::getDepartment()
{
return Department;
}
void Student::setStudentID(int studentID)
{
StudentID = studentID;
}
int Student::getStudentID()
{
return StudentID;
}
void Student::print()
{
cout << "Name:" << getName() << endl;
cout << "ID:" << getID() << endl;
cout << "Address:" << getAddress() << endl;
cout << "Birthday:" << getBirthday().getYear(); // getBirthday()返回的是类,再调用类中的子函数满足cout的返回值。
cout << " " << getBirthday().getMonth();
cout << " " << getBirthday().getDay() << endl;
cout << "Department:" << getDepartment() << endl;
cout << "StudentID:" << getStudentID() << endl;
cout << endl;
}
Teacher.cpp
#include "Teacher.h"
#include <iostream>
using namespace std;
Teacher::Teacher()
{
}
Teacher::Teacher(string name, int id, string address, Date date, string department, double salary)
{
setPerson(name, id, address);
setBirthday(date);
setDepartment(department);
setSalary(salary);
}
Teacher::~Teacher()
{
//cout << "object Destructor is called" << endl;
}
void Teacher::setDepartment(string department)
{
Department = department;
}
string Teacher::getDepartment()
{
return Department;
}
void Teacher::setSalary(double salary)
{
Salary = salary;
}
double Teacher::getSalary()
{
return Salary;
}
void Teacher::print()
{
cout << "Name:" << getName() << endl;
cout << "ID:" << getID() << endl;
cout << "Address:" << getAddress() << endl;
cout << "Birthday:" << getBirthday().getYear(); // getBirthday()返回的是类,再调用类中的子函数满足cout的返回值。
cout << " " << getBirthday().getMonth();
cout << " " << getBirthday().getDay() << endl;
cout << "Department:" << getDepartment() << endl;
cout << "Salary:" << getSalary() << endl;
cout << endl;
}
结果如下:
Name:Bob
ID:2
Address:456 Avenue
Birthday:0 0 0
Name:Charlie
ID:3
Address:789 Boulevard
Birthday:2000 3 1
Department:Math
StudentID:1001
Name:David
ID:4
Address:101 Lane
Birthday:2001 7 22
Department:Physics
StudentID:1002
Name:Eve
ID:5
Address:202 Road
Birthday:1975 1 1
Department:Biology
Salary:50000
Process returned 0 (0x0) execution time : 0.011 s
Press any key to continue.