您的位置:首页 > 其它

让你编写的类也有类型信息

2008-08-27 12:41 162 查看
比如说有这样一个类的继承体系:类CDerivedA和CDerivedB都继承自类CBase。如果要写一个函数传入上面类中的一个对象的引用,在函数里面我们要根据对象的类型来进行相应的处理,也就是说对不同类型的对象进行不同的处理。在这种情况下我们就需要类的对象中保存有类型信息。下面就用一个简单的例子来说明怎么让自己编写的类有类型信息。
首先,根据类的继承关系创建类型信息tag:

struct lx_Base_tag {};
struct lx_DerivedA_tag : public lx_Base_tag {};
struct lx_DerivedB_tag : public lx_Base_tag {};
然后,创建一个类型特征结构:

template<typename T>
struct category_traits
{
typedef typename T::category category;
};
最后,在每个类里面加上类型信息:

// 为了简单起见,这里的类没有实际内容,只保存类型信息

class CBase
{
public:
typedef lx_Base_tag category;
};

class CDerivedA : public CBase
{
public:
typedef lx_DerivedA_tag category;
};

class CDerivedB : public CBase
{
public:
typedef lx_DerivedB_tag category;
};
这样,我们就可以在函数中根据对象类型的不同进行不同的处理:

// 为了简单起见,这里不同的处理仅仅为输出不同的字符串

template<typename T>
void Test(T &t)
{
if (typeid(typename category_traits<T>::category) == typeid(lx_DerivedA_tag))
cout << "class DerivedA" << endl;

if (typeid(typename category_traits<T>::category) == typeid(lx_DerivedB_tag))
cout << "class DerivedB" << endl;
}
上面的函数虽然实现了想要的功能,但是却不是最优方案。
根据模板的特性,在上面的例子中类型T在编译期就已经确定。但是,if语句的判断却是在运行期进行判断的。如果出现下面的代码,那么Test就会生成两个副本。

CDerivedA a;
TestA(a);

CDerivedA b;
Test(b);
这样就会造成编译出的可执行文件膨胀,而且typeid在运行期对效率也有一定的的影响。
那么有没有什么方法使得在编译期就能根据对象类型的不同而将不同的处理方法编译进可执行文件呢?当然有,那就是函数重载。函数重载的作用就是根据参数类型的不同而调用不同的函数。所以,可以引入一个重载的帮助函数:

// 为了简单起见,这里不同的处理仅仅为输出不同的字符串

template<typename T>
void DoTest(T &t, lx_DerivedA_tag)
{
cout << "class DerivedA" << endl;
}

template<typename T>
void DoTest(T &t, lx_DerivedB_tag)
{
cout << "class DerivedB" << endl;
}
而真正的函数只要调用这个帮助函数就行了:

template<typename T>
void Test(T &t)
{
DoTest(t, typename category_traits<T>::category());
}
这样,在编译期,所有的类型都确定了,而且根据类型的不同编译器会选择不同的函数编译进可执行文件中。
通过这种方法,既不会造成编译出的可执行文件膨胀,也避免了typeid在运行期对效率的影响。

发表于 @ 2007年06月27日 08:50:00|评论(8
AddFeedbackCountStack("1668126")
)
|编辑

新一篇: 用欧几里得算法求最大公约数 | 旧一篇: 引用计数(Reference Counting)和代理(Proxy)的应用

评论

#匿名 发表于2007-07-05 17:24:38 IP: 221.207.246.*
在基类中定义虚拟函数,在派生类重载实现,不就结了吗!

#emmett 发表于2007-08-07 08:59:18 IP: 219.142.178.*
这种方法很多地方都讲到过。目前也没碰到过非用这种方法才能解决的问题。
如楼上所说大部分用虚函数就成了。

#starlee 发表于2007-08-07 09:30:49 IP: 218.83.157.*
上面的两位说的是有道理。
可是,用虚函数实现的话,那是执行期多态;而我这里的方法,可以说是编译期多态。
而且,用虚函数并不能实现我文章中的要求,请看文章最开头的几句话:
“比如说有这样一个类的继承体系:类CDerivedA和CDerivedB都继承自类CBase。如果要写一个函数传入上面类中的一个对象的引用,在函数里面我们要根据对象的类型来进行相应的处理,也就是说对不同类型的对象进行不同的处理。”
也就是说需要对类型进行判断,才能决定进行什么操作,而不是调用虚函数那么简单。
用我文章中的方法除了可以实现上面的要求外,最大的好处就是在编译期就可以选择需要的代码编译进可执行文件。我在文章最后也说明了这一点:
“通过这种方法,既不会造成编译出的可执行文件膨胀,也避免了typeid在运行期对效率的影响。”

#emmett 发表于2007-08-09 16:51:09 IP: 219.142.178.*
利用模板进行编译器的类型计算,Boost库里充满了这种代码。
《Modern C++ Design》更是将这种用法发挥到了“令人发指”的地步.
利用模板进行编译期计算不是标准C++提倡的做法。绝大多数程序员只要知道这种用法就可以了。不必刻意去追求。实际上它带给我们的那一点点好处是用程序的可读性、代码的可维护性换取的。而且就目前来讲并不是所有编译器都支持这种用法,代码的可移植性又降低了。 想想实在是不值啊!

#wdx04 发表于2007-08-10 18:32:51 IP: 218.94.62.*
Boost作者中有很多能够影响C++标准发展的成员,从某种意义上说,Boost就是进化中的C++标准。C++标准的制定者会“不提倡”他们自己惯用的编程手法?编译器也不是问题,现在每个主流平台上都有高度支持C++标准的编译器,包括免费的,想要就可以得到,不用担心你的程序因为用了高级C++语法而不能移植到其它平台上。至于对那些9年后还抱着VC6编译器爱不释手的人,反正你跟他讲什么C++标准也是对牛弹琴。

#June_wu 发表于2007-12-05 12:46:37 IP: 210.13.106.*
关键要弄懂其中技术和方法

#shendl 发表于2007-12-11 11:31:44 IP: 211.144.96.*
应该用虚函数,virtual string* getClassName();
每个类实现这个方法,返回自己的类名即可!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐