寒星轩

There are innumerable stars in the sky, the smallest is me!

用户操作
[即时聊天] [发私信] [加为好友]
李星ID:starlee
207030次访问,排名341好友62人,关注者106
欢迎大家访问我的Blog。
主要是C++,设计模式,面向对象设计方面的技术文章。
starlee的文章
原创 98 篇
翻译 0 篇
转载 45 篇
评论 331 篇
李星的公告
郑重声明

        本BLOG所发表的 原创文章,作者保留一切权利。必须经过作者本人同意后方可转载,并注名作者(StarLee)和出处(CSDN Blog)。
作者Email:
coolstarlee(at)sohu.com
最近评论
陈诚:好象不一样,我这个共两个类,实现类和接口类
深夜才走在路上:实际上使用CLR封送C++类让人很受伤,在mc中有很多C++的特性不能使用,甚至STL都不可用
hfg :错了错了,当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。

不管基类的析构函数是不是virtual ,派生类的析构函数还是会被调用的,区别只是在于基类的析构函数有没有被调用
Forrest Yu:Star Lee:

如果有两个以上的类需要包装,那又应该怎样做呢?
Forrest Yu:CLR 还是很强大的,
一些老的MFC项目可以先手动添加
#include <afx.h>,
其他的可能要加
#include <windows.h>,
然后再用这种方法.
文章分类
收藏
相册
友情链接
houdy的专栏
lijgame的专栏
lyrebing的专栏
禾青谷
存档
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

原创 让你编写的类也有类型信息收藏

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

    比如说有这样一个类的继承体系:类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在运行期对效率的影响。

 

2008年10月6日更新:
 
    国庆节前参加了公司组织的STL的高级培训,主讲人就是大名鼎鼎的侯捷。他是《STL源码分析》的作者,而这次主讲的内容也是对STL源码的分析。对我来说这也是第一次比较深入的学习STL的源码。而本文中所讲的其实就是STL里面的Traits。STL源码中到处都能看到Traits的身影,这也是实现编译期函数重载的有效方法。
    下面对文中的最后一段代码进行一下解释:
    1.在编译的时候T的类型已经确定;
    2.代码typename category_traits<T>::category()其实就是创建一个类型为T::category的临时变量;
    3.编译期根据上面的那个变量可以推导出这个变量的类型(lx_Base_tag,lx_DerivedA_tag,lx_DerivedB_tag);
    4.通过上面的类型就可以判断调用哪个函数。
    经过上面的几步,就实现了编译期的函数重载。
    另外,对上面2中代码里的typename关键字做一个说明:如果仅仅看category_traits<T>::category这样的代码的话,是分辨不出category是什么的(有可能是一个静态变量),加上typename关键字就是明确的告诉编译器这是一个类型。

发表于 @ 2007年06月27日 08:50:00|评论(loading...)|收藏

新一篇: 用欧几里得算法求最大公约数 | 旧一篇: 引用计数(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();
每个类实现这个方法,返回自己的类名即可!
#erac 发表于2008-02-24 08:15:09  IP: 125.69.28.*
个人比较倾向于使用楼上的用法。
发表评论  


登录
Csdn Blog version 3.1a
Copyright © 李星