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

【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

posted @ 2024-05-09 20:49  白伟碧一些小心得  阅读(44)  评论(0编辑  收藏  举报