C++:多态公用继承(第十三章)

可能会遇到这样的问题:希望派生类和基类在同一个方法上的行为是不同的。我们称这种行为是多态(多种形态)。有两种机制可以实现多态公有继承。

  • 派生类中重新定义基类的方法
  • 虚方法

写一个班级类,记录每个同学的成绩以及记录班级的平均成绩;
写一个班级类的派生类,特有数据是最高分和最低分,平均成绩的算法是剔除最高分和最低分

/类声明
class Class
{
    enum{MAX=5};
    private:
        double score[MAX];
        double average;
        
    public:
        Class();
        Class(const double arr[]);
        double max();
        double min();
        double aver() {return average;}
        virtual void show_s() const;
        virtual void show_a() ;
        virtual ~Class(){}
 } ;
 
class Pclass : public Class
{
        double max;
        double min;
    public:
        Pclass();
        Pclass(Class& c);
        Pclass(const double d[]);
        
        virtual void show_s() const;
        virtual void show_a() ;
};

程序解析:

  1. 首先score数组不能直接写5为参数,必须枚举或静态常量
  2. 基类和派生类有同名原型(比如show_s)表明将有2个独立方法定义。基类版本限定名:Class::show_s(),派生类的限定名:Pclass::show_s(),程序将根据使用的对象类型来确定使用哪个方法。
  3. 也可以在基类方法声明前写上virtual,派生类自动生成virtual,所以派生类中可写可不写。将根据引用或指针指向的对象类型来确定使用哪种方法。
    如果没有virtual,将根据引用或指针类型选择方法。
  4. 上面是三种选择方法
  • 直接写同名方法的(不过有没有virtual),根据对象类型
    比如Class c1是Class对象,Pclass c2是Pclass对象
  • 直接写同名方法的(没有virtual),根据指针或引用类型
    比如
Class c3 , Pclass c4;
Class& p1 = c3;  Class& p2 = c4;
p1.show_s()  /将使用Class::show_s()
p2.show_s() /将使用Class::show_s()

因为指针和引用都是Class类型

  • 方法前带有virtual的,根据指针或引用所指对象(代码接上)
p1.show_s()  //将使用Class::show_s()
p2.show_s() //将使用Pclass::show_s()

因为p1是Class方法,p2是Pclass方法

  1. 在基类里还要写上返回最大值、最小值、平均值的函数,因为派生类不能直接访问基类的私有数据,必须使用基类的共有方法才能访问数据,访问方式取决于方法。
  2. 派生类构造函数在初始化基类数据时,使用成员初始化列表语句,就是在成员初始化列表里调用基类的构造函数.
  3. 非构造函数不能使用成员初始化列表语句,派生类方法可以调用公有的基类方法,如果是多态方法,要加上作用域解析运算符,如果不加的话程序会误以为调用的方法可能是本类的,会出现递归的错误。如果不是多态方法,可以不使用。
/源代码文件
#include"class.h"
#include<iostream> 
Class::Class():average(0)
{
    for(int i=0;i<5;i++)
        score[i]=0;
}

Class::Class(const double arr[])
{
    double total=0;
    for(int i=0;i<5;i++)
    {
        score[i]=arr[i];
        total+=score[i];
    }
    average = total/5;
 }
 
 double Class::max() 
 {
    double m=0;
    for(int i=0;i<5;i++)
    {
        if(score[i]>m)
            m=score[i];
    }
    return m;
  } 
  
double Class::min()
{
    double m=100;
    for(int i=0;i<5;i++)
    {
        if(score[i]<m)
            m=score[i];
    }
    return m;
}
 
 void Class::show_s() const{
    std::cout<<"班级的分数:\n";
    for(int i=0;i<5;i++)
    {
        std::cout<<i+1<<": "<<score[i]<<"\n";
     }
 }
 
 void Class::show_a() 
 {
    std::cout<<"班级平均分数(未剔除最高最低分):\n"<<average<<"\n";
  } 
 
 Pclass::Pclass(Class& c):Class(c)
 {
    max = c.max();
    min = c.min();
 }
 
Pclass::Pclass(const double d[]):Class(d)
{
    max = Class::max();
    min = Class::min();
}
 
 Pclass::Pclass():Class()
 {
    max=min=0;
 }
 
 void Pclass::show_s() const 
 {
    using std::cout;
    Class::show_s();
    cout<<"最高分:\n"<<max;
    cout<<"\n最低分:\n"<<min<<"\n";
 }
 
 void Pclass::show_a() 
 {
    using std::cout;
    double aver_edit,temp ;
    temp = Class::aver();
    aver_edit = (temp*5 - max - min)/3;
    cout<<"平均分数(剔除最高最低分):\n"<<aver_edit<<"\n";
 }

程序解析:

  1. Class类就不说了,都没什么难度
  2. Pclass参数为Class引用的构造函数:
  • 成员初始化Class(c)将调用Class::Class(const Class&)复制构造函数,由于没有动态内存分配,所以使用默认复制构造函数就OK
  • max和min赋值为c.max() , c.min()的返回值,这里的c是一个对象,跟下面的基类方法访问,对象访问方法理解开来!
  1. Pclass参数为const double数组的构造函数:
  • 成员初始化Class(d)将调用Class::Class(const double d[])来初始化基类私有数据。
  • max和min赋值为Class::max() , Class::min()的返回值,访问基类方法记得作用域解析运算符。
  1. Pclass默认构造函数可以调用Class默认构造函数
  2. Pclass的show_s()会调用Class::show_s(),这里也是派生类方法可以调用基类方法的一个例子。然后自己在添加内容,就能达到不一样的行为的目的。
  3. Pclass的show_a()不在函数名后加const,别问我为啥,现在是深夜凌晨1点半,我的大脑好混乱。
/main.cpp
#include"class.h"
#include<iostream>

int main()
{
    using std::cout;
    using std::endl;
    using std::cin; 
    double d1[5]{100,90,80,60,80};
    Class c1(d1);
    Pclass c2(c1);
    c1.show_s();
    c1.show_a();
    c2.show_s();
    c2.show_a();
    
    Class* ptr [3];
    for(int i=0;i<3;i++)
    {
        double d2[5]={0};
        cout<<"Enter 5 scores: ";
        for(int j=0;j<5;j++)
        {
            cin>>d2[j];
        }
        int tap;
        cout<<"enter 1 to use Class, enter 2 to use Pclass: ";
        while(cin>>tap && (tap != 1 && tap != 2))
            cout<<"You can enter 1 or 2 only\n";
        if(tap==1)
            ptr[i] = new Class(d2);
        else
            ptr[i] = new Pclass(d2);
        while(cin.get()!='\n' )
            continue;
     } 
    for(int i=0;i<3;i++)
    {
        ptr[i]->show_s();
        ptr[i]->show_a();
     } 
     for(int i=0;i<3;i++)
    {
        delete  ptr[i];
    }
     cout<<"Over!\n";
    
    return 0;
}

程序解析:

  1. 首先方法是根据类对象的类型判断使用哪种方法
  2. 声明一个成员为Class指针的数组,后面的for循环,让用户选择使用哪种方法,1就是Class方法,将Class指针指向Class内,2就是Pclass方法,将Class指针指向Pclass类。然后体会一下virtual根据指针或引用所指对象类型来选择方法的这个逻辑。
  3. 最后要delete占用的内存,不是数组,所以上面那种方法。

结果1:(根据对象类型判断)


image.png

结果2:(根据指针或引用所指的对象类型判断,其实没啥区别..)


image.png

为何需要虚析构函数
如果析构函数不是虚的,则将只调用对应指针类型的析构函数。但如果是虚析构函数,则将调用相对应类型的析构函数。虚析构函数可以确保正确的析构函数序列被调用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355