【模板的特殊继承关系】混入

混入 ( M i x i n s ) (Mixins) (Mixins)这个概念,是一种编程手法,用于描述类与类之间的一种关系,这种关系比较类似于多重继承,看起来更像颠倒过来的继承(基类继承派生类)。

混入的实现手段是把传入的模板参数当做该类模板的父亲。

一、常规范例

如果我们设计一些类,比如我们这里以一个大型网络游戏为例,游戏中每个玩家代表一个角色,多个角色可以组成一个家族,家族与家族之间可以进行战斗。

因此,我们需要设计一个 r o l e role role类,代表玩家,具有相应的属性,代码如下:

//角色类
class role {

private:
	double m_attack;
	double m_defence;
	double m_life;

public:
	role()
		:m_attack(0.0), m_defence(0.0), m_life(100) {}

	role(double att, double def, double life)
		:m_attack(att), m_defence(def), m_life(life) {};

};

然后,我们需要设计一个家族类,多个角色可以组成一个家族:

//家族类,若干个角色构成一个家族
template<typename T>
class family {
public:
	std::vector<T>m_members;
};

这里省略一些细节,只需要知道大体结构即可。

当然,一个游戏肯定少不了 N P C NPC NPC,那么我们再来引入一个 N P C NPC NPC类,为了方便,我们单独给 N P C NPC NPC的属性创建一个类:

//NPC属性
struct npcattr {
	int m_sort; //npc类型

	std::string m_lang; //npc的旁白
};

因为 N P C NPC NPC也具有角色的所有属性,因此让 N P C NPC NPC继承自 r o l e role role类。

//role的派生类,role_npc
class role_npc :public role {
public:
	npcattr m_strucattr; //npc属性

	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 } {}

};

紧接着,引入玩家类 r o l e _ p l a y e r role\_player role_player,同样继承与 r o l e role role类,然后我们也为其单独写一个玩家属性类:代码如下:

//玩家属性
struct playerattr {
	int m_strength;
	int m_agile;
	int m_constitution;
};

//玩家角色类
class role_player :public role {
public:
	playerattr m_strucattr; //玩家属性

	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 } {}

};

以上就是全部的代码了 ,现在我们看看使用混入这个技术如何实现。

二、使用混入的技术

使用混入可以减少代码量,不需要每次引新的类通过繁琐的继承来实现,而是通过泛型模板来实现。
具体如下:
同样的, N P C NPC NPC属性和玩家属性是必要的:

//NPC属性
struct npcattr {
	int m_sort; //npc类型
	std::string m_lang; //npc的旁白
};

//玩家属性
struct playerattr {
	int m_strength; //力量
	int m_agile; //敏捷
	int m_constitution;//体质

};

注意看下面的 r o l e role role类,我们使用模板,继承可变参基类包,这样后续就可以直接多继承了。
对于基类参数包的展开,这个博客有介绍:基类参数包的展开

具体代码如下:

template<typename...T>
class role :public T...{ //传入的模板参数作为基类
public:
	double m_attack;
	double m_defence;
	double m_life;

	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){};

};

这里实际上是一个多继承,在实例化模板的时候会先实例化出基类,然后最后再实例化出 r o l e role role类,因此这个 r o l e role role类会具有所有基类的属性。

那么如何定义一个玩家类或者 N P C NPC NPC类呢?答案呼之欲出:继承玩家属性类,继承 N P C NPC NPC属性类!
是的,具体代码如下:

我们使用 u s i n g using using来简化类型名,这样代码会更简洁一点。

using role_npc = role<npcattr>; //类似与多继承

void Test1() {

	role_npc mynpc;
	mynpc.m_attack = 15;
	mynpc.m_defence = 10;
	mynpc.m_life = 120;

	mynpc.m_sort = 1;
	mynpc.m_lang = "Are you OK ?";

}

//同样可以构造出这样的玩家类
using role_player = role<playerattr>;

void Test2() {
	role_player mynpc;
	mynpc.m_attack = 15;
	mynpc.m_defence = 10;
	mynpc.m_life = 120;

	mynpc.m_strength = 100;
	mynpc.m_agile = 130;
	mynpc.m_constitution = 150;
}

当然,由于是继承的可变基类参数包,我们可以定义更复杂的类,比如具有玩家和 N P C NPC NPC共同属性的类,如下:

using role_mixnpc = role<npcattr, playerattr>;

void Test3() {
	role_mixnpc mynpc;
	mynpc.m_attack = 15;
	mynpc.m_defence = 10;
	mynpc.m_life = 120;

	mynpc.m_sort = 1;
	mynpc.m_lang = "Are you OK ?";

	mynpc.m_strength = 100;
	mynpc.m_agile = 130;
	mynpc.m_constitution = 150;
}

这样看起来像是随意的排列组合,因此混入这个词看上去十分恰当。

最后,我们完善一下家族类。
由于 r o l e role role类是一个可变参模板,因此家族类也应该是一个可变参模板,如下:

//家族类
template<typename...T>
class family {
public:
	std::vector<role<T...>>m_member;

	//其他信息...
};

对于玩家家族和 N P C NPC NPC家族也同样是通过混入的方式来实现:

using family_npc = family<npcattr>; //npc家族

void Test4() {
	role_npc mynpc;
	mynpc.m_attack = 15;
	mynpc.m_defence = 10;
	mynpc.m_life = 120;

	mynpc.m_sort = 1;
	mynpc.m_lang = "Are you OK ?";

	family_npc myfamily;
	myfamily.m_member.push_back(mynpc);
}

//一样的,玩家家族
using family_player = family<playerattr>; //玩家家族

这样的写法大大简化了代码量,并且日后再增加新内容的时候可以直接通过已有内容进行排列即可,无需再构建新的类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值