混入(Mixins)这个概念,是一种编程手法,用于描述类与类之间的一种关系,这种关系比较类似于多重继承,看起来像颠倒的继承(基类继承自派生类)。混入的实现手法是把传入的模板参数当作改模板的父类。
示例代码:
namespace _nmsp3
{
//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; //血量(生命值)
// //......其他信息
//};
struct npcattr
{
int m_sort; //npc种类:0代表无实际功能装饰游戏场景用的NPC,1代表卖服装的,2代表把游戏任务派送给玩家
std::string m_lang; //记录自言自语的一句话的内容
};
//class role_npc : public role
//{
//public:
// //构造函数
// role_npc() : role(), m_strucattr{ 0,"" } {}
// role_npc(double att, double def, double life, int extraa, int sort, std::string lang) : role(att, def, life), m_strucattr{ sort,lang } {}
//public:
// npcattr m_strucattr;
//};
struct playerattr
{
int m_strength; //力量
int m_agile; //敏捷
int m_constitution; //体质
};
/*class role_player :public role
{
public:
role_player() :role(), m_strucattr{ 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) {}
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>
template<typename... T>
class family
{
public:
//std::vector<role> m_members;
//std::vector<T> m_members;
std::vector<role<T...>> m_members;
//......其他信息,比如家族创建日期等
};
using role_npc = role<npcattr>;
using role_mixnpc = role<npcattr, playerattr>;
using family_npc = family<npcattr>;
}
_nmsp3::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?";
_nmsp3::family_npc myfamily;
myfamily.m_members.push_back(mynpc);
用参数化的方式表达成员函数的虚拟性
难道成员函数的虚拟性也能用参数化的方式表达吗?答案是肯定的。
首先看下一个用混入技术实现的简单范例
namespace _nmsp4
{
template<typename... T>
class Base : public T...
{
public:
void myfunc()
{
cout << "Base::myfunc()执行了!" << endl;
}
};
}
上面的代码非常简单,其中值得注意的是成员函数myfunc(),这个成员函数看起来是一个普通成员函数,但是这里面问题是Base是一个可变参类模板,带着模板参数T,模板参数T代表"一包类型",并且,由于使用了混入技术,所以T代表的一包类型都会作为Base的父类,这意味着,一旦这一包类型中的某个类型包含一个名字叫做myfunc()的虚函数,那么它的子类(Base)中的myfunc()就变成了虚函数,即使Base中的myfunc()不用virtual修饰。
接着,创建Derived类模板做为Base类模板的子类模板,代码如下:
namespace _nmsp4
{
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() {}
};
}
现在,父类和子类都有myfunc()函数,根据C++语言中的虚函数特点,父类指针指向子类对象时,有几点说明。
(1)如果在Base中的myfunc()是一个虚函数,使用父类指针调用myfunc()函数必然执行的是子类中的myfunc()函数。
(2)如果在Base中的myfunc()是一个普通函数,使用父类指针调用myfunc()函数执行的就是父类Base中的myfunc()函数。
(3)如果一个函数在父类中是虚函数,则子类中即便不用virtual修饰,该函数依然是虚函数。
在mian函数添加下面代码
_nmsp4::Base<_nmsp4::A>* pb1 = new _nmsp4::Derived<_nmsp4::A>; //父类指针指向子类对象
pb1->myfunc();
输出:Base::myfunc()执行了。从结果可以看到,虽然父类指针pb1指向的是子类对象,但是因为myfunc()是非虚函数,所以调用的还是父类Base中的myfunc函数。
再看下面代码:
_nmsp4::Base<_nmsp4::AVir>* pb2 = new _nmsp4::Derived<_nmsp4::AVir>;
pb2->myfunc()
在main函数输出:
Derivd::myfunc执行了,因为子类myfunc用virtual修饰了。
这里还有一个技巧,为防止父类指针用new运算符创建一个子类的对象,可以把父类的析构函数(正常而非虚析构用protected修饰,一旦这样修饰,就会发现虽然用父类指针可以创建一个子类对象,却无通过父类指针删除改子类对象,因为析构对象时要调用父类的析构函数,而父类的析构函数是protected类型的,无法调用。
class A
{
protected:
~A() {}
};
class B :public A {};
A* pa = new _nmsp5::B();
//delete pa; //编译报错