让你编写的类也有类型信息
2007-06-27 08:50
169 查看
比如说有这样一个类的继承体系:类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关键字就是明确的告诉编译器这是一个类型。
首先,根据类的继承关系创建类型信息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关键字就是明确的告诉编译器这是一个类型。
相关文章推荐
- 让你编写的类也有类型信息
- 让你编写的类也有类型信息
- Java类型信息与用反射机制编写通用的Excel导入导出
- 蒋承尧编写的查看表空间中各页的类型和信息的脚本
- 1.编写手机类型;2.输出张三李四的姓名...;3.小组内各成员信息显示
- 通过封装编写Book类。要求:类具有私有属性书名title、页数pageNum、类型type(默认为计算机类),并为这三个属性分别编写set和get方法。其中,页数不能少于200页,否则输出错误信息,
- Java类型信息与用反射机制编写通用的Excel导入导出
- 获取系统磁盘类型信息以及判断是否是U盘
- 编程思想-类型信息
- 大数据学习笔记:编写脚本分发配置,数据分布,以及使用代码打印调试信息
- Java编程思想(十五) —— 类型信息之反射
- cocos2d-x获取屏幕位置信息代码 分类: cocos2d代码编写 2015-07-28 21:32 7人阅读 评论(0) 收藏
- sql中如何查询表的信息,字段名,长度,类型等等?
- [常见问题解答-ASP.NET]分析器错误信息: 未能加载类型“Application1.XXX”
- java的运行时类型信息
- 编写程序读入一组string类型的数据,并将它们存储在vector中,再将vector对象复制给一个字符指针数组。
- 编写苹果游戏中心应用程序(翻译 1.8 获取本地玩家的好友信息)
- 14.9 接口与类型信息
- 编写苹果游戏中心应用程序(翻译 1.15 编程获取成就信息)
- 编写spring配置文件时,不能出现帮助信息