首页 > 编程语言 >C/C++ 泛型 -- 继承与多态

C/C++ 泛型 -- 继承与多态

时间:2024-05-09 20:56:30浏览次数:19  
标签:-- 基类 多态 role 派生类 泛型 public 模板

【1】继承与多态 -- 多态在模板中的应用

#include <iostream>
 
using namespace std;
 
namespace _nmsp1
{
    // 多态
    // 人类
    class Human
    {
    public:
        virtual void eat()
        {
            cout << "人类以吃米饭和面食为主!" << endl;
        }
        virtual ~Human() {} //作为父类时一般应该有一个虚析构函数,在《C++设计模式》中给大家讲解
    };
    // 男人
    class Men :public Human
    {
    public:
        virtual void eat()
        {
            cout << "男人喜欢吃面食!" << endl;
        }
    };
    // 女人
    class Women :public Human
    {
    public:
        virtual void eat()
        {
            cout << "女人喜欢吃米饭!" << endl;
        }
    };
}
 
namespace _nmsp2
{
    // 模板中的多态不需要父类
    // 男人
    class Men
    {
    public:
        void eat()
        {
            cout << "男人喜欢吃面食!" << endl;
        }
    };
 
    // 女人
    class Women
    {
    public:
        void eat()
        {
            cout << "女人喜欢吃米饭!" << endl;
        }
    };
 
    // 函数模板
    template<typename T>
    void eatTmpl(T& obj)
    {
        obj.eat();
    }
}
 
// 多态在模板中的应用
// 多态的概念
// 1. 有父类有子类(存在继承关系),父类中必须含有虚函数,子类重写父类中的虚函数
// 2. 父类指针指向子类对象或者父类引用绑定(指向)子类对象
// 3. 当以父类的指针或者引用调用子类中重写了的虚函数时,便表现出多态,因为调用的是子类实现的虚函数
 
// 模板中的多态并不需要用到父类以及继承的概念,子类也不需要虚函数
// (压根就不存在父类指针指向子类对象或者父类引用绑定子类对象这种概念)
// 编译期间内,编译器会实例化出eatTmpl<Men>和eatTmpl<Women>这两个函数
 
// 总结
// 传统多态,也叫动态多态(运行时多态),因为要访问虚函数表指针,所以对执行期间的性能会有一些影响
// 模板多态,也叫静态多态,编译期间就确定了具体调用对象,就不存在执行期间的性能问题
int main()
{
    _nmsp1::Men objmen;
    _nmsp1::Women objwomen;
 
    // 父类引用绑定(指向)子类对象,以表现多态
    _nmsp1::Human& yinbase1 = objmen;
    _nmsp1::Human& yinbase2 = objwomen;
 
    yinbase1.eat();
    yinbase2.eat();
 
 
    _nmsp2::Men objmen;
    _nmsp2::Women objwomen;
 
    _nmsp2::eatTmpl(objmen);    // 男人喜欢吃面食!
    _nmsp2::eatTmpl(objwomen);  // 女人喜欢吃米饭!
 
    return 0;
}

【2】继承与多态 -- 模板的一些特殊继承关系

 【2.1】在基类中使用派生类对象

#include <iostream>
 
using namespace std;
 
namespace _nmsp1
{
    template <typename T>       // T代表的就是派生类
    class Base                  // Base是类模板
    {
    public:
        void asDerived()
        {
            // 实现在基类中使用派生类对象
            T& derived = static_cast<T&>(*this);    // 派生类对象也是基类对象所以这种静态类型转换没问题
                                                    // 将基类对象转换为派生类对象
            derived.myfunc();                       // 调用派生类的成员函数
        }
 
    private:
        Base() {};                                  // 基类构造函数是私有的
        friend T;                                   // T派生类变成了友元类
    };
 
    class Derived1 : public Base<Derived1>          // Derived1是个普通类
    {
    public:
        void myfunc()
        {
            cout << "Derived1::myfunc()执行了" << endl;
        }
    };
 
    template <typename T>
    class Derived2 : public Base<Derived2<T>>       // Derived2是个类模板
    {
    public:
        void myfunc()
        {
            cout << "Derived2::myfunc()执行了" << endl;
        }
    };
 
    class Derived3 : public Base<Derived3>          // Derived3是个普通类
    {
    public:
        void myfunc()
        {
            cout << "Derived3::myfunc()执行了" << endl;
        }
    };
}
 
// 模板的一些特殊继承关系说
// 奇异(奇特)的递归模板模式(CRTP),Curiously Recurring  Template Pattern
//      一种模板编程手法,把派生类作为基类的模板参数
// 1. 在基类中使用派生类对象
int main()
{
    _nmsp1::Derived1 myd;
    myd.asDerived();        // 调用基类的成员函数
 
    _nmsp1::Derived3 myd3;
    myd3.asDerived();       // 调用基类的成员函数
 
    return 0;
}

【2.2】减少派生类的代码量

#include <iostream>
 
using namespace std;
 
namespace _nmsp2
{
    template<typename T>
    struct shape
    {
        // 把派生类对象是否相等的判断放在了基类中
        // (使用了在类模板中定义友元函数的手段把全局的operator==放到基类中)
        // 也可以不在基类中实现
        friend bool operator==(const shape<T>& obj1, const shape<T>& obj2) // 在类模板中定义友元
        {
            const T& objtmp1 = static_cast<const T&>(obj1);     // 派生类对象也是基类对象所以这种静态类型转换没问题
            const T& objtmp2 = static_cast<const T&>(obj2);
            if (!(objtmp1 < objtmp2) && !(objtmp2 < objtmp1))
                return true;
            return false;
        }
    };
 
    // 派生类
    struct square : public shape<square>
    {
        int sidelength; // 边长
    };
 
    // 类外运算符重载
    bool operator<(square const& obj1, square const& obj2)
    {
        if (obj1.sidelength < obj2.sidelength)
        {
            return true;
        }
        return false;
    }
 
    // 在此实现operator==而不是在基类中实现
    /*template<typename T>
    bool operator==(const shape<T>& obj1, const shape<T>& obj2)
    {
        const T& objtmp1 = static_cast<const T&>(obj1);
        const T& objtmp2 = static_cast<const T&>(obj2);
        if (!(objtmp1 < objtmp2) && !(objtmp2 < objtmp1))
            return true;
        return false;
    }*/
 
}
 
// 模板的一些特殊继承关系说
// 奇异(奇特)的递归模板模式(CRTP),Curiously Recurring  Template Pattern
//      一种模板编程手法,把派生类作为基类的模板参数
// 2. 基于减少派生类中代码量的考虑,
//      出发点是尽可能把一些代码挪到基类中,从而有效的减少派生类中的代码量;
int main()
{
    _nmsp2::square objsq1; // 派生类对象
    objsq1.sidelength = 15;
    _nmsp2::square objsq2;
    objsq2.sidelength = 21;
    if (!(objsq1 == objsq2))
    {
        cout << "objsq1和objsq2不相等!" << endl;
    }
    else
    {
        cout << "objsq1和objsq2相等!" << endl;
    }
 
    return 0;
}

【2.3】基类调用派生类的接口与多态的体现(静态多态编程技术)

 

#include <iostream>
 
using namespace std;
 
namespace _nmsp3
{
    // 基类模板
    template <typename T>
    class Human
    {
    public:
        T& toChild()
        {
            return static_cast<T&>(*this);
        }
        void parenteat()
        {
            toChild().eat(); // 派生类给基类提供了调用接口
        }
 
    private:
        Human() {};
        friend T;           // T派生类变成了友元类        
    };
 
    // 子类
    class Men :public Human<Men>
    {
    public:
        void eat()
        {
            cout << "男人喜欢吃面食!" << endl;
        }
    };
    
    // 子类
    class Women :public Human<Women>
    {
    public:
        void eat()
        {
            cout << "女人喜欢吃米饭!" << endl;
        }
    };
 
    template<typename T>
    void myHumanFuncTest(Human<T>& tmpobj)
    {
        tmpobj.parenteat();
    }
}
 
// 模板的一些特殊继承关系说
// 奇异(奇特)的递归模板模式(CRTP),Curiously Recurring  Template Pattern
//      一种模板编程手法,把派生类作为基类的模板参数
// 3. 基类调用派生类的接口与多态的体现(静态多态编程技术)
int main()
{
 
    _nmsp3::Men mymen;
    _nmsp3::Women mywomen;
 
    mymen.parenteat();
    mywomen.parenteat();
    cout << "---------------------" << endl;
    _nmsp3::myHumanFuncTest(mymen);
    _nmsp3::myHumanFuncTest(mywomen);
 
    return 0;
}

【2.4】混入

【2.4.1】概念

#include <iostream>
#include <vector>
 
using namespace std;
 
namespace _nmsp1
{
 
    // npc属性类
    struct npcattr
    {
        int m_sort;         // npc种类,0:代表装饰游戏场景的NPC,1:代表商人,2:代表把游戏任务派送给玩家
        std::string m_lang; // 记录自言自语的一句话
    };
 
    // 玩家角色属性系统,分为三种:力量,敏捷,体质;
    // 玩家每升一级,就能得到10个属性点,可以把属性点加到这三种属性上去;
    // 最终目的就是提高玩家攻击力,防御力,血量;
    //      每加一点力量,攻击力提高1.2,每加一点敏捷,防御力提高1.5,每加一点体质,血量增加0.6;
    // 引入玩家属性类
    struct playerattr
    {
        int m_strength;     // 力量
        int m_agile;        // 敏捷
        int m_constitution; // 体质
    };
    
    template <typename...T>
    class role : public T...    // 把传入的模板参数当做该类模板的父类
    {
    public:
        // 初始时攻击力防御力都为0,血量100;
        role() : T()..., m_attack(0.0), m_defence(0.0), m_life(100.0) {}
        role(double att, double def, double life) : T()..., m_attack(att), m_defence(def), m_life(life) {}
    public:
        double m_attack;    // 攻击力
        double m_defence;   // 防御力
        double m_life;      // 血量(生命值)
    };
 
    template <typename...T>
    class family
    {
    public:
        vector< role<T...> > m_members;
        //....其他信息
    };
 
    // role_npc的效果类似于,class role_npc :public role, public npcattr{...}
    using role_npc = role<npcattr>;
    using role_player = role<playerattr>;
    // 通过混入技术组合,从而自由的装配各种功能
    using role_mixnpc = role<npcattr,playerattr>;
 
    using family_npc = family<npcattr>;
}
 
// 模板的一些特殊继承关系
// 混入(Mixins)是一种编程手法,用于描述类与类之间的一种关系;
//      这种关系类似于多重继承,看起来更类似颠倒过来的继承;
// 混入的实现手段,把传入的模板参数当做该类模板的父类
// 1. 常规范例
//      引入混入手段取代传统的继承,这种混入实现手段看起来更像是把某个或者某些类混合到当前类中凑成一个更大的类
int main()
{
    _nmsp1::role_npc mynpc;
    mynpc.m_attack = 15;            // 攻击
    mynpc.m_defence = 10;           // 防御
    mynpc.m_life = 120;             // 血量
    mynpc.m_sort = 1;               // npc种类
    mynpc.m_lang = "Are You OK?";   // NPC自言自语时玩家能看到的所说的话
 
    _nmsp1::family_npc myfamily;
    myfamily.m_members.push_back(mynpc);
    
    return 0;
}

【2.4.2】用参数化的方式表达成员函数的虚拟性

#include <iostream>
#include <vector>
 
using namespace std;
 
namespace _nmsp2
{
    template <typename ... T>
    class Base :public T...
    {
    public:
        void myfunc()
        {
            cout << "Base::myfunc()执行了!" << endl;
        }
    };
 
    template <typename ... T>
    class Derived :public Base<T...>
    {
    public:
        void myfunc()
        {
            cout << "Derived::myfunc()执行了!" << endl;
        }
    };
 
    class A
    {
    };
 
    class AVir
    {
    public:
        virtual void myfunc() {}
    };
}
 
// 模板的一些特殊继承关系
// 用参数化的方式表达成员函数的虚拟性,是一种设计理念,体现一种开发智慧;
int main()
{
    // Base类的void myfunc()的虚拟性可以由指定的T的类型决定
    // 父类指针指向子类对象
    _nmsp2::Base<_nmsp2::A> *pb1 = new _nmsp2::Derived<_nmsp2::A>;
    // Base::myfunc()执行了!
    pb1->myfunc();
 
    // 父类指针指向子类对象
    _nmsp2::Base<_nmsp2::AVir>* pb2 = new _nmsp2::Derived<_nmsp2::AVir>;
    // Derived::myfunc()执行了!
    pb2->myfunc();
    
    return 0;
}

 

参考:

https://coppersun.blog.csdn.net/article/details/115250663

标签:--,基类,多态,role,派生类,泛型,public,模板
From: https://www.cnblogs.com/bwbfight/p/18183052

相关文章

  • python教程6.6-发送邮件smtplib
    实现步骤: Python对SMTP⽀持有smtplib和email两个模块,email负责构造邮件,smtplib负责发送邮件,它对smtp协议进⾏了简单的封装。简单代码示例:发送html格式的邮件:在html中插入图片: ......
  • PCI-Express-Technology(二)
    第一代PCIe(称为Gen1或者PCIe协议规范版本1.x)中,比特率为2.5GT/s,将它除以10即可得知一个通道的速率将可以达到0.25GB/s。因为链路可以在同一时刻进行发送和接收,因此聚合带宽可以达到这个数值的两倍,即每个通道达到0.5GB/s。第二代PCIe(称为Gen2或者PCIe2.x)中将总线频......
  • 文件IO常用的函数接口
    本文归纳整理了常用的文件IO常见的函数接口及其用法,以供读者查阅目录打开文件fopen关闭文件fclose数据读取字符读取:fgetc、getc、getchar按行读取:fgets、gets按块读取:fread写入文件字符写入:fputc、putc、putchar按行写入:fputs、puts按块写入:fwrite文件位置(光标位置)设置位移:fse......
  • 洛谷 P1012 [NOIP1998 提高组] 拼数 题解
    题目简述设有$n$个正整数$a_1\dotsa_n$,将它们联接成一排,相邻数字首尾相接,组成一个最大的整数。题目分析定义设$X$为数字$x$的字符串形式。$A+B$表示字符串$A$和字符串$B$相连组成的字符串。思路既然要构造最优解,显然如果有不优的情况的话,就需要对序列进行......
  • CSDN复制登录复制问题
    不能复制主要有两个问题,第一个问题是选中问题,第二个问题主要指copy也就是复制时会弹出登录页面1.解决选中问题:按F12打开html代码=》上边栏点击  </>元素 ->head->第四个style->右键,删除元素 2. 第二个问题主要指copy也就是复制时会弹出登录页面在不能......
  • 程设2022期末
    A.最长下坡#include<cstdio>inta[1000];intmain(){ intn,ans=0,now=0;scanf("%d",&n); for(inti=1;i<=n;++i){ intx;scanf("%d",&x);a[i]=a[n+i]=x; } for(inti=2;i<=n*2;++i){ if(a[i......
  • CH57x/CH58X/CH59X/CH32F/V208OTA使用说明
    目前提供了两种OTA升级方式,方式一:带库升级;每次升级可以带着库一起进行升级(带库升级适用于flash较大的芯片)方式二:固定库升级;升级时库不会随着升级而升级(适用于flash不够用时)方式一:升级时需要同时烧录这三个固件:(可以使用isp工具同时烧录也可以使用合并工具将三个工程合并后再烧......
  • 若依-扩展为多租户版本
    SELECTTABLE_NAME,COLUMN_NAMEFROMINFORMATION_SCHEMA.COLUMNSWHERETABLE_SCHEMA='ruoyi-vue-pro'andtable_namelike'%system_%'andcolumn_namelike'%tenant_id%'andtable_namenotin('system_oauth2_access_token&#......
  • 继承介绍、经典类和新式类、继承和抽象、封装和派生、组合
    【一】继承介绍继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。子类会“遗传”父类的属性,从而解决代码重用问题(去掉冗余的代码)继承:单继承:继承一个父类的子类多继承:继承多个父类的子类c......
  • 2024.5.10家长会发言
    大家好,我是初三四班的学习委员包赟瑞,接下来由我来对半个学期的班级学习情况做一个总结。整体来看,全班的学习态度有一定进步,布置的打卡任务大多能按时完成。但是不能仅仅满足于此,像政治这种需要背诵的学科,许多同学在课下不爱背、不愿背,没有老师监督便开始摆烂,类似这样的不自律行为......