{C++系列} C++基础 day10 多态,虚函数,纯虚函数,虚析构函数,运行时类型信息,动态类型转换
in C/C++ with 0 comment

{C++系列} C++基础 day10 多态,虚函数,纯虚函数,虚析构函数,运行时类型信息,动态类型转换

in C/C++ with 0 comment

复习:

1# 继承

2# 多重继承

3# 钻石继承

   A(int m_data)
  / \
 B   C
  \ /
   D

二十二# 多态(Polymorphic)

1# 函数重写(虚函数覆盖)、多态概念

如果将基类的某个成员函数声明为虚函数,那么其子类中与该函数具有相同原型的成员函数就也是虚函数,并且对基类中版本形成覆盖。
这时,通过指向子类对象基类指针,或者引用子类对象的基类引用,调用该虚函数,实际被执行的将是子类的覆盖版本,而不再是基类的原始版本,这种语法现象称为多态

eg:
  class Base{
  public:
      virtual void func(void){...}
  };
  class Derived:public Base{
  public:
      void func(void){...}
  };
  Derived d;
  Base* pb = &d;//pb:指向子类对象基类指针
  Base& rb = d;//rb:引用子类对象的基类引用
  pb->func();//Derived::func
  rb.func();//Derived::func

#举例

多态举例

#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;//x坐标
    int m_y;//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];i++){
        //调用成员函数不再由指针的类型决定,而是
        //由实际的目标对象类型决定
        buffer[i]->draw();
    }
}
int main(void)
{
    Shape* buffer[256] = {NULL};
    buffer[0] = new Rect(1,2,3,4);
    buffer[1] = new Circle(5,6,7);
    buffer[2] = new Rect(3,4,8,9);
    buffer[3] = new Rect(10,20,13,14);
    buffer[4] = new Circle(6,7,10);
    buffer[5] = new Circle(16,17,20);
    render(buffer);
    return 0;
}

2# 函数重写的要求(虚函数覆盖条件)

class A{};
class B:public A{};

#举例

函数重写举例

#include <iostream>
using namespace std;
class A{};
class B:public A{};
class Base{
public:
    virtual void foo(int a=0)const{
        cout << "Base::foo" << endl;
    }
    virtual A* hum(void){
        cout << "Base::hum" << endl;
    }
};
class Derived:public Base{
public:
    B* hum(void){
        cout << "Derived::hum" << endl;
    }
    /*virtual*/ void foo(int x=0)const{
        cout << "Derived::foo" << endl;
    }
};
int main(void)
{
    Derived d;
    Base* pb = &d;
    pb->foo();
    pb->hum();
    return 0;
}

3# 多态条件

#举例

this指针构成的重载

#include <iostream>
using namespace std;

class Base{
public:
    virtual int cal(int x,int y)const{
        return x+y;
    }
    //如果调用func函数的对象是子类对象,这时
    //func函数的this指针就是一个指向子类对象
    //的基类指针,通过它去调用虚函数,也有多
    //态的特性
    //void func(Base* this=&d)
    void func(void){
        cout << cal(100,200) << endl;
    }
};
class Derived:public Base{
public:
    int cal(int a,int b)const{
        return a*b;
    }
};
int main(void)
{
    Derived d;
    Base b = d;
    //通过对象调用虚函数,没有多态特性
    cout << b.cal(100,200) << endl;
    d.func();//func(&d)
    return 0;
}

4# 纯虚函数、抽象类、纯抽象类

注:抽象类不能创建对象

#举例

纯虚函数,抽象类,纯抽象类

#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;//x坐标
    int m_y;//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];i++){
        //调用成员函数不再由指针的类型决定,而是
        //由实际的目标对象类型决定
        buffer[i]->draw();
    }
}
int main(void)
{
    Shape* buffer[256] = {NULL};
    buffer[0] = new Rect(1,2,3,4);
    buffer[1] = new Circle(5,6,7);
    buffer[2] = new Rect(3,4,8,9);
    buffer[3] = new Rect(10,20,13,14);
    buffer[4] = new Circle(6,7,10);
    buffer[5] = new Circle(16,17,20);
    render(buffer);

    Shape s(1,2);

    return 0;
}

#举例

工厂方法模式

//工厂方法模式
#include <iostream>
using namespace std;

class PDFParser{
public:
    void prase(const char* pdfFile){
        cout << "解析出一个矩形" << endl;
        onRect();
        cout << "解析出一个圆形" << endl;
        onCircle();
    }
private:
    virtual void onRect(void) = 0;
    virtual void onCircle(void) = 0;
};
class PDFRender:public PDFParser{
private:
    void onRect(void){
        cout << "绘制一个矩形..." << endl;
    }
    void onCircle(void){
        cout << "绘制一个圆形..." << endl;
    }
};
int main(void)
{
    PDFRender render;
    render.prase("xxx.pdf");
    return 0;
}

5# 多态原理

通过虚函数表和动态绑定实现的,参看poly.bmp
注:动态绑定会增加时间开销,虚函数表也会增加内存的开销,所以如果没有多态的语法要求,最好不要使用虚函数。

6# 虚析构函数

这时delete一个指向子类对象的基类指针,实际被执行的将是子类的析构函数,子类的析构函数又会自动调用基类的析构函数,确保动态资源都可以正确的释放,避免内存泄露。

#举例

虚析构函数举例

#include <iostream>
using namespace std;

class Base{
public:
    Base(void){
        cout << "Base::Base(void)" << endl;
    }
    virtual ~Base(void){//虚析构函数
        cout << "Base::~Base(void)" << endl;
    }
};
class Derived:public Base{
public:
    Derived(void){
        cout << "子类动态资源分配..." << endl;
    }
    ~Derived(void){
        cout << "子类动态资源释放..." << endl;
    }
};
int main(void)
{
    Base* pb = new Derived;
    //...
    delete pb;//pb->析构函数
    return 0;
}

==========================

二十三# 运行时类型信息

1# typeid运算符

#include <typeinfo>
typeid(类型/对象);返回一个typeinfo对象

typeinfo类中包含一个name()成员函数,用于返回描述类型信息的字符串。
注:tpyeinfo支持“==”`"!="操作符,通过它们可以直接比较类型信息是否相等,如果类型之间存在多态的继承关系,typeid`还可以利用多态的特性确定实际对象的类型。

#举例

typeid举例

#include <iostream>
#include <typeinfo>
using namespace std;
class X{virtual void foo(void){} };
class Y:public X{void foo(void){} };
class Z:public X{void foo(void){} };

void func(X& x){
    if(typeid(x) == typeid(X)){
        cout << "针对X对象的处理..." << endl;
    }
    else if(typeid(x) == typeid(Y)){
        cout << "针对Y对象的处理..." << endl;
    }
    else if(typeid(x) == typeid(Z)){
        cout << "针对Z对象的处理..." << endl;
    }
}

int main(void)
{
    cout << typeid(int).name() << endl;//i
    cout << typeid(int* [5]).name() 
        << endl;//A5_Pi
    cout << typeid(int(*)[5]).name() 
        << endl;//PA5_i
    cout << typeid(int(*[5])(int)).name()
        << endl;//A5_PFiiE
    X x;
    func(x);
    Y y;
    func(y);
    Z z;
    func(z);
    return 0;

}

2# 动态类型转换

语法:
目标类型变量 = dynamic_cast<目标类型>(源类型变量);
使用场景:用于具有多态继承关系的父子类指针或引用之间的显示转换。

注:在转换过程中,会检查目标对象和期望转换的对象类型是否一致,如果一致则转换成员,不一致则转换失败,如果转换的是指针,返回0表示失败,如果转换的是引用,则抛出异常“bad_cast”异常表示失败。

#举例

动态类型转换举例

#include <iostream>
using namespace std;

class A{ virtual void foo(void){} };
class B:public A{ void foo(void){} };
class C:public A{ void foo(void){} };

int main(void)
{
    B b;
    A* pa = &b;
    //B* pb = static_cast<B*>(pa);
    B* pb = dynamic_cast<B*>(pa);
    //C* pc = static_cast<C*>(pa);
    C* pc = dynamic_cast<C*>(pa);
    cout << "pa = " << pa << endl;
    cout << "pb = " << pb << endl;
    cout << "pc = " << pc << endl;

    A& ra = b;
    C& rc = dynamic_cast<C&>(ra);

    return 0;

}

============================================

#练习

c++语法综合练习

《薪资计算》

        普通员工
    /     |        \
技术员     经理     销售员
    \    /   \   /
    技术主管    销售主管      

所有员工:姓名、工号、职位级别
技术员:研发津贴(元/小时)
经理:绩效奖金(元/每月)
销售员:提成比率(百分比)

薪资=基本工资+绩效工资
基本工资=等级额度*出勤率(输入)
绩效工资计算因职位而异

普通员工绩效工资:基本工资一半
技术员绩效工资:

 研发津贴*工作小时数*进度因数(输入)

经理的绩效工资:绩效奖金*绩效因数(输入)
销售员绩效工资:销售额度(输入)*提成比率

技术主管绩效工资:

 (技术员绩效工资+经理的绩效工资)/2

销售主管绩效工资:
(销售员绩效工资+经理的绩效工资)/2

结果:打印员工信息,输入必要数据,计算工资

==========================

 class 员工{
    void calSalary(){
       基本工资()+绩效工资()
    }
    virutal void 绩效工资(){..}
 }
 class 技术员:public 员工{
      void 绩效工资(){..}
 };
 int main(void){
     员工.calSalary();
     技术员.calSalary();
 }

#代码

#include <iostream>
using namespace std;

//普通员工
class Employee{
public:
    Employee(const string& name,int id,int grade)
        :m_name(name),m_id(id),m_grade(grade){}
    //打印员工信息
    void printInfo(void){
        printBasic();//公有信息
        printExtra();//特有信息
    }
    //计算工资
    void paySalary(void){
        //工资:基本工资+绩效工资
        double salary = calBasic() + calMerit();
        cout << "工资为:" << salary << endl;
    }
private:
    double calBasic(void){
        cout << "输入出勤天数:" << endl;
        int attend;
        cin >> attend;
        m_attend = attend / 23.0;
        m_basic = s_grades[m_grade-1] * m_attend;
        return m_basic;
    }
    virtual double calMerit(void){
        return m_basic/2;
    }
private:
    void printBasic(void){
        cout << "姓名:" << m_name << endl;
        cout << "工号:" << m_id << endl;
        cout << "职位级别:" << m_grade << endl;
    }
    virtual void printExtra(void){
        cout << "职位:普通员工" << endl; 
    }
protected:
    double m_attend;//出勤率
private:
    string m_name;//姓名
    int m_id;//工号
    int m_grade;//职位级别
    double m_basic;//基本工资
    static double s_grades[6];//职位级别薪资表
};
double Employee::s_grades[] = {
        4000,5000,6000,8000,10000,12000};
//技术员
class Technician:virtual public Employee{
public:
    Technician(const string& name,int id,
        int grade,double allow)
        :Employee(name,id,grade),m_allow(allow){}
protected:
    void printExtra(void){
        cout << "职位:技术员" << endl;
        cout << "研发津贴:" << m_allow << endl;
    }
    double calMerit(void){
        //小时数*研发津贴*进度因数
        cout << "输入进度因数:" << endl;
        double factor;
        cin >> factor;
        return 8*23*m_attend * m_allow * factor;
    }
private:
    double m_allow;//研发津贴
};
//经理
class Manager:virtual public Employee{
public:
    Manager(const string& name,int id,int grade,
        double bonus):Employee(name,id,grade),
            m_bonus(bonus){}
protected:
    void printExtra(void){
        cout << "职位:经理" << endl;
        cout << "绩效奖金:" << m_bonus << endl;
    }
    double calMerit(void){
        //绩效奖金*绩效因数
        cout << "输入绩效因数:" << endl;
        double factor;
        cin >> factor;
        return m_bonus * factor;
    }
private:
    double m_bonus;//绩效奖金
};
//技术主管
class TechMngr:public Technician,public Manager{
public:
    TechMngr(const string& name,int id,int grade,
        double allow,double bonus):
            Technician(name,id,grade,allow),
            Manager(name,id,grade,bonus),
            Employee(name,id,grade){}
private:
    void printExtra(void){
        Technician::printExtra();
        Manager::printExtra();
    }
    double calMerit(void){
        //技术员绩效工资+经理绩效工资/2
        return (Technician::calMerit() +
                    Manager::calMerit()) / 2;
    }
};

int main(void)
{
   /* Employee emp("老王",10086,3);
    emp.printInfo();
    emp.paySalary();*/
   /* Technician tech("老杨",10011,4,20);
    tech.printInfo();
    tech.paySalary();*/
   /* Manager mngr("王老板",10010,5,5000);
    mngr.printInfo();
    mngr.paySalary();*/
    TechMngr techMngr("杨老板",10000,6,25,6000);
    techMngr.printInfo();
    techMngr.paySalary();
    return 0;
}
Responses