回顾:
Review cpp day01
Review cpp day02
Review cpp day03
Review cpp day04
Review cpp day05
Review cpp day06
Review cpp day07
Review cpp day08
二十二、多态(Polymorphic)
1、虚函数覆盖(函数重写)、多态概念
- 如果将基类中的某个成员函数声明为虚函数,那么其子类中与该函数具有相同原型的成员函数也就是虚函数,并且对基类中的版本形成覆盖。这时,通过指向子类对象的基类指针,或者通过引用子类对象的基类引用,去调用虚函数,实际被执行的将是子类中的覆盖版本,而不是基类中的原始版本,这种语法现象被称为多态。
class A{
public:
virtual void func(void){}//虚函数
};
class B:public A{
public:
void func(void){}//自动变成虚函数
};
int main(void){
B b;
A* pa = &b;//pa指向子类对象的基类指针
A& ra = b;//ra引用子类对象的基类引用
pa->func();//子类中的func
ra.func();//子类中的func
}
eg:实现图形库,用于绘制各种图形
图形(位置、绘制)
/ \
矩形(宽和高、绘制) 圆形(半径、绘制)
05shape.cpp
#include <iostream>
using namespace std;
class Shape{
public:
Shape(int x, int y):m_x(x), m_y(y){}
virtual void draw(void){//虚函数******
cout << "绘制图形:" << m_x << "," << m_y << endl;
}
protected:
int m_x;//坐标
int m_y;
};
class Rect:public Shape{
public:
Rect(int x, int y, int w, int h):Shape(x, y), m_w(w), m_h(h){}
void draw(void){
cout << "绘制矩形:" << m_x << ',' << m_y << ',' <<m_w
<< ',' << m_h << endl;
}
private:
int m_w;
int m_h;
};
class Circle:public Shape{
public:
Circle(int x, int y, int r):Shape(x, y), m_r(r){}
void draw(void){
cout << "绘制圆形:" << m_x << ',' << m_y << ',' << m_r << endl;
}
private:
int m_r;
};
void render(Shape* buffer[]){
for(int i=0; buffer[i] != NULL; i++){
/*
*调用虚函数时,不再由指针本身类型决定调用哪个版本,
*而是由实际的目标对象类型决定
*/
buffer[i]->draw();
}
}
int main(void){
Shape* buffer[1024] = {NULL};
buffer[0] = new Rect(1, 2, 3, 4);
buffer[1] = new Circle(5, 6, 7);
buffer[2] = new Rect(1, 2, 3, 4);
buffer[3] = new Rect(1, 2, 3, 4);
buffer[4] = new Circle(5, 6, 7);
buffer[5] = new Circle(5, 6, 7);
//...
render(buffer);
return 0;
}
2、虚函数覆盖的条件
- 1)只有类中成员函数才能声明为虚函数,全局函数、静态成员函数、构造函数都不能声明为虚函数。
注: 析构函数可以虚(特殊情况后面讲) - 2)只有基类中以virtual关键字修饰的成员函数才能作为虚函数被子类覆盖,而与子类中的virtual关键字无关。
- 3)虚函数在子类中的版本必须和该函数在基类中的版本拥有相同的函数签名,即函数名、形参表和常属性一致。
- 4)如果基类中的虚函数返回基本类型的数据,那么该函数在子类中的覆盖版本必须返回相同的类型的数据。
- 5)如果基类中的虚函数返回类类型的指针(A*)或引用(&A), 那么允许子类的覆盖版本返回其子类类型的指针(B*)或引用(&B)。
class A{}; class B:public A{};
01poly.cpp
#include <iostream>
using namespace std;
class Base{
public:
void foo(void){
cout << "Base::foo" << endl;
}
virtual void bar(double d){
cout << "Base::bar" << endl;
}
virtual void hum(void){
cout << "Base::hum" << endl;
}
};
class Derived:public Base{
public:
//如果基类中该函数不加上virtual关键字,这个函数尝试覆盖,覆盖失败
void foo(void){
cout << "Derived::foo" << endl;
}
void bar(int d){
cout << "Derived::bar" << endl;
}
int hum(void){
cout << "Derived::hum" << endl;
}
};
int main(void){
Derived d;
Base* pb = &d;
pb->foo();//Base::foo
pb->bar();//不能构成多态,形参表不一样
pb->hum();//产生冲突,返回类型不一样
return 0;
}
3、多态的条件
- 1)多态特性除了需要满足虚函数的覆盖,还必须通过指针或引用去调用才能表现出来。
- 2)调用虚函数的指针也可能是this指针,当通过一个子类对象调用基类中的成员函数时,该函数里面的this指针就是一个指向子类对象的基类指针,再通过它去调用虚函数,同样可以表现多态的语法特性。
02poly.cpp
#include <iostream>
using namespace std;
class Base{
public:
virtual int cal(int x, int y){
return x + y;
}
//void func(Base* this = &d)
void func(void){
cout << this->cal(200, 300) << endl;
cout << cal(200, 300) << endl;
}
};
class Derived:public Base{
public:
int cal(int x, int y){
return x * y;
}
};
int main(void){
Derived d;
//Base b = d;
//cout << b.cal(100, 200) << endl;//300,没有产生多态
d.func();
return 0;
}
4、纯虚函数、抽象类、纯抽象类
- 1)纯虚函数:virtual 返回类型 函数名(形参表)[const] = 0;
- 2)抽象类:如果一个类中包含了纯虚函数,那么这类就是抽象类。
注: 不能使用抽象类创建对象。 - 3)纯抽象类(有名接口类):如果一个类中成员函数(不包含构造函数)都是纯虚函数,那么在该类就是纯抽象类。
03shape.cpp
#include <iostream>
using namespace std;
class Shape{//抽象类、纯抽象类
public:
Shape(int x, int y):m_x(x), m_y(y){}
virtual void draw(void) = 0;//纯虚函数
protected:
int m_x;//坐标
int m_y;
};
class Rect:public Shape{
public:
Rect(int x, int y, int w, int h):Shape(x, y), m_w(w), m_h(h){}
void draw(void){
cout << "绘制矩形:" << m_x << ',' << m_y << ',' <<m_w
<< ',' << m_h << endl;
}
private:
int m_w;
int m_h;
};
class Circle:public Shape{
public:
Circle(int x, int y, int r):Shape(x, y), m_r(r){}
void draw(void){
cout << "绘制圆形:" << m_x << ',' << m_y << ',' << m_r << endl;
}
private:
int m_r;
};
void render(Shape* buffer[]){
for(int i=0; buffer[i] != NULL; i++){
/*
*调用虚函数时,不再由指针本身类型决定调用哪个版本,
*而是由实际的目标对象类型决定
*/
buffer[i]->draw();
}
}
int main(void){
Shape* buffer[1024] = {NULL};
buffer[0] = new Rect(1, 2, 3, 4);
buffer[1] = new Circle(5, 6, 7);
buffer[2] = new Rect(1, 2, 3, 4);
buffer[3] = new Rect(1, 2, 3, 4);
buffer[4] = new Circle(5, 6, 7);
buffer[5] = new Circle(5, 6, 7);
//...
render(buffer);
//抽象类不能创建对象
//Shape s(10,50);//error
return 0;
}
04poly.cpp
//工厂方法模式
#include <iostream>
using namespace std;
class PDFParser{
public:
void parse(const char* pdffile){
cout << "解析出一个矩形" << endl;
onRect();
cout << "解析出了一个圆形" << endl;
onCircle();
cout << "解析出了一行文本" << endl;
onText();
cout << "解析出了一张图片" << endl;
onImag();
}
private:
virtual void onRect(void) = 0;
virtual void onCircle(void) = 0;
virtual void onText(void) = 0;
virtual void onImage(void) = 0;
};
class PDFRender:public PDFParse{
private:
void onRect(void){
cout << "绘制一个矩形" << endl;
}
void onCircle(void){
cout << "绘制一个圆形"<< endl;
}
void onText(void){
cout << "显式一行文本" << endl;
}
void onImage(void){
cout << "显式一张图片" << endl;
}
};
int main(void){
PDFRender render;
render.parse("xx.pdf");
return 0;
}
注意:当满足虚函数覆盖条件时,在基类中访问控制属性是public,而不管子类的访问控制属性是不是public能形成虚函数覆盖,即使子类的访问控制属性是private也能形成覆盖。因为访问控制属性只能影响编译期间,而运行期间是动态绑定的
class Base{
public:
virtual void foo(void){
cout << "Base::foo()void" << endl;
}
}
class Derived : public{
private://不写访问控制属性默认为private
void foo(void){
cout << "Derived::foo(void)" << endl;
}
}
int main(void){
Base* b = new Derived();
b.foo();//Derived::foo(void)//运行时动态绑定
delete b;
return 0;
}
5、多态实现原理//了解
- 通过虚函数表和动态绑定来是实现
- 1)虚函数的动态绑定会增加时间的开销
- 2)虚函数表的生成会增加内存的开销
注: 如果没有多态的语法要求,最好不要使用虚函数
class A{
public:
virtual void foo(void){}
virtual void bar(void){}
};
class B:public A{
public:
void foo(void){}
};
6、虚析构函数
- 1)基类的析构函数不会自动调用子类的析构函数。如果对一个指向子类对象的基类指针使用delete操作符,实际被执行的仅是基类的析构函数,子类的析构函数执行不到,有内存泄漏的风险。
class A{...};
class B:public A{...};
A* pa = new B;//pa: 指向子类的基类指针
delete pa;//内存泄漏
- 2)虚析构来解决
可以将基类的析构函数声明为虚函数,那么子类的析构函数就也是一个虚函数,并且对基类的虚构函数构成有效的覆盖,可以表现多态的语法特性。 这时delete一个指向子类对象的基类指针,实际被调用的将是子类的析构函数,子类的析构函数在执行结束以后又会自动调用基类的析构函数,避免了内存泄漏。
05poly.cpp
#include <iostream>
using namespace std;
class Base{
public:
Base(void){
cout << "Base的构造" << endl;
}
virtual ~Base(void){//虚析构函数
cout << "Base的析构" << endl;
}
};
class Derived:public Base{
public:
Derived(void){
cout << "Derived的构造" << endl;
}
~Derived(void){
cout << "Derived的析构" << endl;
}
};
int main(void){
Base* pb = new Derived;
//...
//1) pb->析构函数
//2) 释放内存
delete pb;
return 0;
}
Base的构造
Derived的构造
Derived的析构
Base的析构
扩展练习V7.0:实现企业员工管理系统
- 需求:
- 增加技术主管类,同时具有技术员和经理属性
- 绩效工资=(技术员绩效+经理绩效)/ 2
员工(Employee)
/ \
技术员 经理
\ /
技术主管
- 提示:
void printInfo(){
printBasic();//打印公有信息
printExtra();//打印特有信息
}
void calSalary(){
//总工资 = 基本工资 + 绩效工资
calBasic() + calMerit();
}
Employee.h
#ifndef __EMPLOYEE_H__
#define __EMPLOYEE_H__
#include <iostream>
#include <cstdio>
using namespace std;
class Employee{
public:
Employee(const string& name, double salary);
~Employee(void);
void printInfo(void)const;
protected:
void printInfo(void)const;//公有信息
void printExtra(void)const;//特有信息
public:
//计算工资
void claSalary(void);
protected:
double calBasic(void);//基本工资
double calMerit(void);//绩效工资
public:
void setId(int id);
void setName(const string& name);
void setSalary(double salary);
void saveInfo(void)const;
//根据员工对象的id和参数id比较,是否相等
bool operator==(int id)const;
bool operator!=(int id)const;
friend ostream& operator<<(ostream& os, const Employee& emp);
private:
//禁止拷贝构造函数和拷贝复制
Employee& operator = (const Employee&);
Employee(const Employee&);
private:
int m_id;//工号
string m_name;//姓名
double m_salary;//工资
FILE* file;//保存员工信息指针
double m_attend;//出勤率
public:
//获取员工人数
static const int& getEmployeeCount(void);
private:
static int m_count;//记录员工人数
};
#endif //__EMPLOYEE_H__
Employee.cpp
#include "Employee.h"
Employee::Employee(const string& name, double salary)
:m_name(name), m_saraly(salary){
//读取ID
FILE* fp = fopen("id.txt", "r+");
fscanf(fp, "%d", &m_id);
//将文件读写指针定位到文件头
fseek(fp, 0, SEEL_SET);
//ID+1, 再保存回id.txt
fprintf(fp, "%d", m_id+1);
//关闭id.txt
fclose(fp);
//将ID转换为字符串
char filename[20] = {0};
sprintf(filename, "%d", m_d);
//根据id创建文件保存当前员工信息
file = fopen(filename, "w");
//保存员工信息
saveInfo();
//记录员工人数
++m_count;
//保存与纳贡人数
FILE* fp2 = fopen("count.txt", "w");
fprintf(fp2, "%d", m_count);
fclose(fp2);
}
Employee::~Employee(void){
fclose(file);
file =NULL;
}
Employee::Employee(const Employee& that){
char filename[20] = {0};
sprintf(filename, "%d", that.m_id);
file = fopen(filename, "r+");
m_id = that.m_id;
m_name = that.m_name;
m_salary = that.m_salary;
}
void Employee::printInfo(void)const{
printBasic();
printExtra();
}
void Employee::printBase(void)const{
cout << "姓名:" << m_name << endl;
cout << "工号:" << m_id << endl;
cout << "基础工资:" << m_salary << endl;
}
void Employee::printExtra(void)const{
cout << "职位:普通员工" << endl;
}
void Eployee::calSalary(void){
double salary = calBasic() + calMerir();
cout << "总工资为:" << (basic + merit) << endl;
}
double Employee::calBasic(void){
cout << "请输入出勤天数:";
int days;
cin >> days;
m_attent = days/23.0;//计算出勤率
return m_salary * m_attend;
}
double Employee::calMerit(void){
return m_salary * m_attend / 2;
}
void Employee::setid(int){
if(id < 10000){
cout << "无效的工号" << endl;
}
else{
m_id = id;
}
}
void Employee::setName(const string& name){
if(name.size() > 20){
cout << "无效的姓名" << endl;
}
else{
m_name = name;
}
}
void Employee::setSalary(double){
if(salary < 0){
cout << "无效工资" << endl;
}
else{
m_salary = salary;
}
}
void Employee::saveInfo(void)const{
fseek(file, 0, SEEK_SET);
fprintf(file, "%d %s %g", m_id, m_name.c_str(), m_salary);
}
//静态成员需要在类的外部单独定义和初始化
int Employee::m_count = 0;
count int& Employee::getEmployeeCount(void){
7this FILE* fp = fopen("count.txt", "r");
//加载员工人数
fscanf(fp, "%d", &m_count);
fclose(fp);
return m_count;
}
//根据员工对象的id和参数id比较,是否相等
bool Employee::operator==(int id)const{
if(m_id ==id){
return true;
}
else{
return false;
}
}
bool Employee::operator!=(int id)const{
return !(*this == id);//函数复用
}
//全局函数
ostream& operator<<(ostream& os, const Employee& emp){
os << "Employee(" << emp.m_id << "," << emp.m_name << em.m_salary << ")";
return os;
}
Technician.h
#ifndef __TECHNICIAN_H__
#define __TECHNICIAN_H__
#include "Employee.h"
class Technician: public Employee{
public:
Technician(const string& name,double salary, double allow);
void printInfo(void)const;
void printExtra(void)const;
void calSalary(void);
double calMerit(void);
friend ostream& operator<<(ostream& os, const Technician& tech);
private:
double m_allow;//研发津贴
};
#endif //__TECHNICIAN_H__
Technician.cpp
#include <Technician.h>
Technician::Technician(const string& name, double salary, double allow):
Employee(name, salary), m_allow(allow){
fprintf(file, "%g", m_allow);
}
void Technician::printInfo(void)const{
printBasic();
printExtra();
}
void Technician::printExtra(void)const{
cout << "职位:技术员" << endl;
cout << "研发津贴:" << m_allow << endl;
}
void Technician::calSalary(void){
double salary = calBasic() + calMerit();
cout << "总工资为:" << salary << endl;
}
void Technician::calMerit(void)const{
cout << "请输入进度因数:";
double factor;
cin >> factor;
return 23 * 8 * m_attend * m_allow * factor;
}
ostream& operator<<(ostream& os, const Technician& tech){
os << "Technician(" << tech.name << "," << tech.m_id << ")";
}
Manager.h
#ifndef __MANAGER_H__
#define __MANAGER_H__
#include "Employee.h"
class Manager: public Employee{
public:
Manager(const string& name, double salary, double m_bonus);
void printInfo(void)const;
void printExtra(void)const;
void calSalary(void);
double calMerit(void);
private:
double m_bonus;//绩效奖金
};
#endif //__MANAGER_H__
Manager.cpp
#include "Manager.h"
Manager::Manager(const string& name, double salary, double bonus):
Employee(name, salary), m_bonus(bonus){
//保存经理的绩效工资
fprintf(file, "%g", m_bonus);
}
void Manager::printfInfo(void)const{
printBasic();
printExtra();
}
void Manager::printExtra(void)const{
cout << "职位:经理" << endl;
cout << "绩效工资:" << m_bonus << endl;
}
void Manager::calSalary(void){
double salary = calBasic() + calEerit();
cout << "总工资为:" << salary << endl;
}
void Manager::calEerit(void){
cout << "请输入j绩效因数:";
double factor;
cin >> factor;
return m_bonus * factor;
}
CTO.h
#ifndef __CTO_H__
#define __CTO_H__
#include "Maneger.h"
#include "Technician.h"
class CTO:public Technician, public Manager{
public:
CTO(const string& name, double salary, double allow, double bonus);
void printInfo(void)const;
void printExtra(void)const;
void calSalary(void);
double calMerit(void);
};
#endif //__CTO_H__
CTO.cpp
#include "CTO.h"
CTO::CTO(const string& name, double salary,double allow, double bonus):
Technician(name, salary, allow), Manager(name, salary, bonus),
Employee(name, salary){}
void CTO::printInfo(void)const{
printBasic();
printExtra();
}
void CTO::printExtra(void)const{
cout << "职位:技术主管" << endl;
cout << "研发津贴:" << m_allow << endl;
cout << "绩效奖金:" << m_bonus << endl;
}
void CTO::calSalary(void){
double salary = calSalary + calMerit();
cout << "总工资为:" << salary << endl;
}
double CTO::calMerit(void){
return (Technician::calMerit() + Manager::calMerit()) / 2;
}
main.cpp
#include "Employee.h"
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
void init(void){
if(access("id.txt", F_OK) == -1){
FILE* fp = fopen("id.txt", "w");//记录工号
if(fp == NULL){
perror("fopen"),exit(-1);
}
fprintf(fp, "%d", 10001);
fclose(fp);
}
if(access("count.txt", F_OK) == -1){
FILE* fp = fopen("count.txt", "w");//记录人数
if(fp == NULL){
perror("fopen"),exit(-1);
}
fprintf(fp, "%d", 0);
fclose(fp);
}
else{
//加载员工人数
Employee::getEmployeeCount();
}
}
int main(void){
init();
Employee emp("张三", 6600);
//emp.printInfo();
cout << emp << endl;
emp.calSalary();
cout << "======================" << endl;
Technician tech("孔明", 8800, 30);
//tech.printInfo();
cout << tech << endl;
tech.calSalary();
cout << "======================" << endl;
Manager manager("刘备", 12000, 5000);
manager.printInfo();
manager.calSalary();
cout << "======================" << endl;
CTO cto("司马懿", 16000, 30, 5000);
cto.printInfo();
cto.calSalary();
cout << "当前员工人数:" << Employee::getEmployeeCount() << endl;
cout << "======================" << endl;
return 0;
}