Symbian编程总结-深入篇-瘦模板正解
2008-12-04 16:05
405 查看
本文章由杨芹勍原创,如需转载请注明出处。谢谢!
C++的模板对于那些可以被多种类型重用的代码是非常有用的,如:Symbian C++中的泛型集合RArray和RPointerArray就是使用的模板来实现的。但是,使用普通C++模板会带来代码尺寸大大增加的问题。本文将分为“C++模板基础”、“TBuf分析”、“瘦模板”三个部分,逐步深入讲解Symbian C++瘦模板的概念和使用方法。
而在Symbian SDK中,我们看到的最常见的形式是:
在上面的代码中,T通常成为“模板参数”。以上两种模板声明方式效果是一样的,template <class T>的声明形式会容易让人产生误解,在此不一定非要类类型才能作为模板参数,相比之下template <typename T>的形式更好一些。
以上代码的作用:如果TStudent的模板参数为TInt型,则在屏幕上打印“TInt”,否则打印“T”。
输出结果:
如果没有“在编译器确定数组大小”这个需求,我们可以很简单的把这个类设计好:
但是,以上代码中CStudent类的成员变量iValue指向的数组大小是在运行期才确定的,这显然不能满足我们的需求。
再看以下代码:
我们将模板参数类型替换成TInt,S就是非类型的类模板参数。从以上代码来分析,使用非类型的类模板参数的好处和限制:
TStudent类中的成员变量iValue数组的长度在声明stu变量(第18行)时就已经确定,在编译期可以由用户确定iValue数组的大小;
模板可以具有值模板参数,而不仅仅是类型模板参数;
对于非类型模板参数,不能使用浮点数、class类型的对象作为实参。如:不能使用template <float T>,或template <TDesC T>。
从以上声明我们可以看出以下几点:
从第一行可以看出,TBuf类模板只有一个模板参数,此参数的类型为“非类型的模板参数”;
第13行,__Align为一个宏,相关代码:
运算步骤:
sizeof(TUint) = 4;
sizeof(TUint16) = 2;
__Size = 2;
如果s = 10;
11 / 2 = 5, 5 * 2 = 10
__Align(s) = (10 + 2 - 1) / 2 * 2 = 10;
如果我们定义TBuf<10>,对象内部会转变为
因为存在第10行的运算符“=”号重载方式,所以以下代码能够正确执行:
而我们上面的TStudent类代码是不能够这样编写的。
在程序编译的时候,会生成类模板的两个副本:TStudent_TInt,TStudent_TUint,来区分不同类型的类模板参数调用。
所以,使用C++模板不会带来运行效率的降低,但是会带来编译后代码尺寸的增大!
如果一个类不大,这种尺寸的开销不足为惧。但是如果一个类很大且存在很多不同类型的模板参数调用,这个问题就大了(提醒一下:大家在做内存受限的系统开发,不是PC机)。
我们来看一下SDK中RArray类的相关声明:
我们从以上代码可以学习到:
RArray类的绝大多数函数都是从RArrayBase继承的,而RArrayBase不是类模板;
瘦模板的设计方法:
在通用基类RArrayBase中实现必要的逻辑代码,但是使用非类型安全的TAny*指针,因为为类型不安全的,所以都要放在protected块中;
在派生类中使用模板,已达到类型安全的目的;
虽然RArray经过编译后也会产生RArray_TInt、RArray_TUint等等这样的类,但由于其继承自RArrayBase,主要逻辑代码在RArrayBase中,所以代码大小的开销会很小,RArray_TInt、RArray_TUint在这里就相当于RArrayBase的不同的“壳”而已;
RArray私有继承自RArrayBase,这样,在外部调用者看来,RArray和RArrayBase就没有任何继承上“is a”的关系,如:以下代码是不可行的:
所以,我们在编写Symbian C++类模板时,要按照以上几点进行设计,以保证生成代码的最小化。
另:TBuf类的设计也属于瘦模板,主要逻辑代码都继承自TBufBase,而TBuf只保存模板参数S所指定的不同大小的缓冲区结构。
C++ Templates 中文版
Symbian OS C++ 高效编程
C++私有继承和保护继承
C++的模板对于那些可以被多种类型重用的代码是非常有用的,如:Symbian C++中的泛型集合RArray和RPointerArray就是使用的模板来实现的。但是,使用普通C++模板会带来代码尺寸大大增加的问题。本文将分为“C++模板基础”、“TBuf分析”、“瘦模板”三个部分,逐步深入讲解Symbian C++瘦模板的概念和使用方法。
一、C++模板基础
在这一部分中不会详细的介绍C++的模板,只会已不同的代码的形式介绍C++模板的几种不同的使用方法,在这里只会以类模板作为例子。如果大家想对C++模板进行更深一步的了解,请参阅《C++ Templates》一书。1、类模板的声明
template <typename T> class TStudent { ... };
而在Symbian SDK中,我们看到的最常见的形式是:
template <class T> class TStudent { ... };
在上面的代码中,T通常成为“模板参数”。以上两种模板声明方式效果是一样的,template <class T>的声明形式会容易让人产生误解,在此不一定非要类类型才能作为模板参数,相比之下template <typename T>的形式更好一些。
2、类模板的特化
类似于函数重载,类模板提供了对模板参数的重载,实现了对不同类型的参数的不同处理,这一个过程就叫做类模板的特化。通过以下代码可以简单的说明这一点:template <typename T> class TStudent { public: void DoPrint() { console->Write(_L("T")); } }; template <> class TStudent<TInt> { public: void DoPrint() { console->Write(_L("TInt")); } }; LOCAL_C void MainL() { TStudent<TUint> stu1; stu1.DoPrint(); console->Write(_L("\n")); TStudent<TInt> stu2; stu2.DoPrint(); }
以上代码的作用:如果TStudent的模板参数为TInt型,则在屏幕上打印“TInt”,否则打印“T”。
输出结果:
3、非类型的类模板参数
假如我们有这样一个需求:由一个模板类TStudent,内部有一个模板数组T iValue[],要求该数组大小有用户来定,且在编译器就已经确定,实现这个类。如果没有“在编译器确定数组大小”这个需求,我们可以很简单的把这个类设计好:
template <typename T> class CStudent { private: T* iValue; TInt iSize; public: CStudent(TInt aSize) : iValue(new T[aSize]), iSize(aSize) { } ~CStudent() { delete []iValue; iValue = NULL; } void DoPrint() { TInt size = sizeof(T) * iSize; TBuf<10> buf; buf.Num(size); console->Write(buf); } }; LOCAL_C void MainL() { CStudent<TInt>* stu = new CStudent<TInt>(10); stu->DoPrint(); delete stu; stu = NULL; }
但是,以上代码中CStudent类的成员变量iValue指向的数组大小是在运行期才确定的,这显然不能满足我们的需求。
再看以下代码:
template <typename T, TInt S> class TStudent { private: T iValue[S]; public: void DoPrint() { TInt size = sizeof(iValue); TBuf<10> buf; buf.Num(size); console->Write(buf); } }; LOCAL_C void MainL() { TStudent<TInt, 10> stu; stu.DoPrint(); }
我们将模板参数类型替换成TInt,S就是非类型的类模板参数。从以上代码来分析,使用非类型的类模板参数的好处和限制:
TStudent类中的成员变量iValue数组的长度在声明stu变量(第18行)时就已经确定,在编译期可以由用户确定iValue数组的大小;
模板可以具有值模板参数,而不仅仅是类型模板参数;
对于非类型模板参数,不能使用浮点数、class类型的对象作为实参。如:不能使用template <float T>,或template <TDesC T>。
二、TBuf分析
首先贴出SDK中TBuf的声明代码,为了篇幅整洁,省去了一些不相关的代码:template <TInt S> class TBuf : public TBufBase16 { public: inline TBuf(); inline explicit TBuf(TInt aLength); inline TBuf(const TText* aString); inline TBuf(const TDesC& aDes); inline TBuf<S>& operator=(const TText* aString); inline TBuf<S>& operator=(const TDesC& aDes); inline TBuf<S>& operator=(const TBuf<S>& aBuf); private: TText iBuf[__Align(S)]; };
从以上声明我们可以看出以下几点:
从第一行可以看出,TBuf类模板只有一个模板参数,此参数的类型为“非类型的模板参数”;
第13行,__Align为一个宏,相关代码:
#define __Align(s) ((((s)+__Size-1)/__Size)*__Size) #define __Size (sizeof(TUint)/sizeof(TUint16))
运算步骤:
sizeof(TUint) = 4;
sizeof(TUint16) = 2;
__Size = 2;
如果s = 10;
11 / 2 = 5, 5 * 2 = 10
__Align(s) = (10 + 2 - 1) / 2 * 2 = 10;
如果我们定义TBuf<10>,对象内部会转变为
TText iBuf[10];
因为存在第10行的运算符“=”号重载方式,所以以下代码能够正确执行:
TBuf<20> buf1; TBuf<10> buf2; buf1 = buf2;
而我们上面的TStudent类代码是不能够这样编写的。
三、瘦模板
1、C++模板弊端
C++编译器会在编译的时候将类模板代码“拆分”,生成针对于不同类型模板参数的拷贝,这些拷贝的区别只在于类型的不同。如下代码:template <typename T> class TStudent { ... };
TStudent<TInt> stu1;
TStudent<TUint> stu2;
在程序编译的时候,会生成类模板的两个副本:TStudent_TInt,TStudent_TUint,来区分不同类型的类模板参数调用。
所以,使用C++模板不会带来运行效率的降低,但是会带来编译后代码尺寸的增大!
如果一个类不大,这种尺寸的开销不足为惧。但是如果一个类很大且存在很多不同类型的模板参数调用,这个问题就大了(提醒一下:大家在做内存受限的系统开发,不是PC机)。
2、RArray和RPointerArray
RArray类和RPointerArray类实现了瘦模板机制,有关以上两个类的说明,请参看“集合与数组(1)- RArray和RPointerArray”这篇随笔。我们来看一下SDK中RArray类的相关声明:
template <class T> class RArray : private RArrayBase { ... inline const T& operator[](TInt anIndex) const { return *(const T*)At(anIndex); } inline T& operator[](TInt anIndex) { return *(T*)At(anIndex); } ... } class RArrayBase { protected: ... IMPORT_C TAny* At(TInt anIndex) const; ... }
我们从以上代码可以学习到:
RArray类的绝大多数函数都是从RArrayBase继承的,而RArrayBase不是类模板;
瘦模板的设计方法:
在通用基类RArrayBase中实现必要的逻辑代码,但是使用非类型安全的TAny*指针,因为为类型不安全的,所以都要放在protected块中;
在派生类中使用模板,已达到类型安全的目的;
虽然RArray经过编译后也会产生RArray_TInt、RArray_TUint等等这样的类,但由于其继承自RArrayBase,主要逻辑代码在RArrayBase中,所以代码大小的开销会很小,RArray_TInt、RArray_TUint在这里就相当于RArrayBase的不同的“壳”而已;
RArray私有继承自RArrayBase,这样,在外部调用者看来,RArray和RArrayBase就没有任何继承上“is a”的关系,如:以下代码是不可行的:
RArray<TInt> arr; RArrayBase arrBase = arr;
所以,我们在编写Symbian C++类模板时,要按照以上几点进行设计,以保证生成代码的最小化。
另:TBuf类的设计也属于瘦模板,主要逻辑代码都继承自TBufBase,而TBuf只保存模板参数S所指定的不同大小的缓冲区结构。
[b]四、参考文献[/b]
C++编程思想 第1卷 标准C++引导C++ Templates 中文版
Symbian OS C++ 高效编程
C++私有继承和保护继承
相关文章推荐
- Symbian编程总结-深入篇-RTTI的实现及原理说明
- Symbian编程总结-基础篇-活动对象正解(1)-理解活动对象
- Symbian编程总结-图形图像篇-打开非Bitmap类型的图像
- Symbian编程总结-基础篇-活动对象正解(1)-理解活动对象
- 【Java并发编程】之十七:深入Java内存模型—内存操作规则总结
- Symbian编程总结-网络与通信-套接字(1)-套接字体系结构与相关API
- Symbian编程总结-基础篇-动态缓冲区(1)-回顾HBufC
- 模板编程总结
- linux c 编程模板总结(一)
- Symbian http 编程总结
- ClearSilver模板编程总结
- Symbian编程总结-关键篇-活动对象正解(2)-使用活动对象
- Symbian编程总结-基础篇-活动对象正解(3)-活动对象的工作原理
- Symbian编程总结-基础篇-活动对象正解(2)-使用活动对象
- Symbian编程总结-基础篇-集合与缓冲区(1)-RArray和RPointerArray
- Symbian编程总结-文件、流与数据库-文件系统及相关API(1)
- Symbian编程总结-关键篇-活动对象正解(3)-活动对象的工作原理
- Symbian编程总结-图形图像篇-使用双缓存进行图形的绘制
- C# 模板编程相关学习总结
- 深入模板编程笔记三