目录
模板的一些特殊继承关系说
混入(Mixins)
- 是一种编程手法,用于描述类与类之间的一种关系。
- 这种关系类似于多重继承,看起来更象颠倒过来的继承。
传统的继承方式编程案例
//role角色类,代表玩家,包括攻击力,防御力,血量(生命值): class role { public: //构造函数: role() :m_attack(0.0), m_defence(0.0), m_life(100.0) {}//初始时攻击力防御力都为0,血量100; role(double att,double def,double life):m_attack(att), m_defence(def), m_life(life) {} public: double m_attack; //攻击力 double m_defence; //防御力 double m_life; //血量(生命值) //...... }; //怪物、NPC(非玩家角色)。 NPC分类:0:代表装饰游戏场景的这种NPC,1:代表商人,卖服装。2:代表把游戏任务派送给玩家。 自言自语的说话。 template<typename T> class family { public: //vector<role> m_members; vector<T> m_members; //....其他信息 }; //npc属性类 struct npcattr { int m_sort; //npc种类:0:代表装饰游戏场景的这种NPC,1:代表商人,卖服装。2:代表把游戏任务派送给玩家。 std::string m_lang; //记录自言自语的一句话 }; NPC类 class role_npc :public role { public: //构造函数 role_npc(): role(), m_strucattr{ 0,"" }{} role_npc(double att, double def, double life, int sort, std::string lang) :role(att, def, life), m_strucattr{ sort,lang } {} public: npcattr m_strucattr; }; //玩家角色属性系统,分为三种:力量,敏捷,体质。玩家每升一级,就能得到10个属性点。可以把属性点加到这三种属性上去 //最终目的就是提高玩家攻击力,防御力,血量:每加一点力量,攻击力提高1.2,每加一点敏捷,防御力提高1.5。每加一点体质,血量增加0.6。 //引入玩家属性类: struct playerattr { int m_strength; //力量 int m_agile; //敏捷 int m_constitution; //体质 }; //玩家类(真实玩家) class role_player :public role { public: role_player() : role(), m_strucattr{0,0,0}{} role_player(double att, double def, double life, int sth, int agi,int cons) :role(att, def, life), m_strucattr{ sth,agi,cons } {} public: playerattr m_strucattr; }; template <typename...T> class role : public T... //把传入的模板参数当做该类模板的父类 { public: role() : T()..., m_attack(0.0), m_defence(0.0), m_life(100.0) {}//初始时攻击力防御力都为0,血量100; 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; //血量(生命值) };
- 如果role类内部数据发生增减,那么继承该类的其他类的构造函数都需进行改变。
通过混入技术方便的组合,自由的装配各种功能
- 引入混入手段取代传统的继承,这种混入实现手段看起来更像是把某个或者某些类混合到当前类中凑成 一个更大的类
//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: role() : T()..., m_attack(0.0), m_defence(0.0), m_life(100.0) {}//初始时攻击力防御力都为0,血量100; 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; //....其他信息 }; using role_npc = role<npcattr>; using role_player = role<playerattr>; using role_mixnpc = role<npcattr,playerattr>; //通过混入技术方便的组合,自由的装配各种功能 using family_npc = family<npcattr>;
- 调用
family_npc myfamily; myfamily.m_members.push_back(mynpc);
- 【注】:role_npc的效果类似于:class role_npc :public role,public npcattr{...}
用参数化的方式表达成员函数的虚拟性
- 是一种设计理念,体现一种开发智慧。
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() //virtual { cout << "Derived::myfunc()执行了!" << endl; } }; class A { }; class AVir { public: virtual void myfunc() {} };
- 调用
Base<_nmsp2::A> *pb1 = new Derived<A>; //父类指针指向子类对象 pb1->myfunc(); //Base::myfunc()执行了! Base<AVir>* pb2 = new Derived<AVir>; //父类指针指向子类对象 pb2->myfunc(); //Derived::myfunc()执行了!
- A是Base的父类,没有虚函数,即不存在虚函数表,父类的指针或引用即使指向子类,调用的也是其静态类型,也就是该类自身的函数。
- AVir是Base类的父类,父类中是虚函数,则相应子类同名的函数是虚函数。
- 父类的指针或引用执向子类,如果父类中有虚函数,则此时的指针或引用的动态类型第子类,其调用的虚函数通过虚函数表执行,也就是执行子类的虚函数。
- Base的父类(A或AVir)里是否有虚函数,决定这Base与其同名的函数是否为虚函数。
- 一般来说,一个类如果做父类,那么它应该有析构函数并且这个析构函数都应该是一个虚函数。
- 什么情况下父类中可以没有析构函数或者析构函数可以不为虚函数:
- 1)子类并没有析构函数(不需要在析构函数中释放任何new出来的数据)。
- 2)代码中不会出现父类指针指向子类对象(多态)的情形。
- 为防止用父类指针new一个子类对象,可以把父类的析构函数用protected来修饰。
class A { protected: ~A() {} }; class B :public A {};
- 调用
A* pa = new _nmsp3::B(); delete pa; //编译出错
- delete报错,所以不能用new来创建一个对象。
- 可以参考Boost库中的noncopyable类。