[原创文章欢迎转载,但请保留作者信息]
Justin
于 2010-03-04
话说看书看到第七章,大师开始讲模板。
第
41
课首先要讲的是:面向对象
(OO)
编程强调的是具体的接口和运行时的多态;而模板编程恰恰相反,侧重于模糊的接口以及编译期的多态。
具体的接口
(explicit interface)
有着具体的类型
(
包括参数的,内部变量的,返回值的等等
)
,这些类型被大师称为接口的特征
(signature)
;而模糊的接口却没有这些限制,取而代之的是表达式
(expression)
。
运行时的多态由虚拟机制而生,对象的运行时类型决定了真正得到执行的函数
/
接口
/
方法是什么;这和编译期的多态有些类似,不过后者多态特性是在程序编译期间呈现的:不同的模板参数决定了不同的函数会被调用。
例子就不举了,如果不清楚什么是模板,那么最好
Google
一下,会有一堆的入门读物。(哦对了,不知道现在Google是否会需要翻墙……)
OK
,下一课吧。
42
课的中心思想是
typename
的两重意义。
下面的课前作业做了没?template <class T> class TClass1;
template <typename T> class TClass2;
TClass1
和
TClass2
有什么区别?
答案是没有区别。因为当定义模板参数时,用
class
和
typename
是没有差异的。
既然用了两个不同的名字,自然会有不一样的地方。
下面便是一处应该用
typename
而不能用
class
的情形:
由于模板的中接口的模糊性质,下面的函数模板便有了这样那样的问题:
见下面的程序片段:T::name
的本意可能是模板
T
所指代的类中的
name
类型,而
pName
就是指向这种类型对象的指针。
但是模板中的接口是模糊的,所以同样的模板会因为
T
的不同而产出不一样的实际代码。如果
T
::
name
不是一个类型,而是
T
的一个成员,然后如果人品真的很差,
pName
是一个全局变量,这一句就变成了将
T::name
和
pName
相乘!
(
看到这里不得不佩服大师举例子的能力,这样都想得到
……)template <typename T>
void TFunc()
{
T::name * pName; // this won't even compile
// and it should be
// typename T::name * pName;
//..
}
会发生这种惨案的原因是因为
C++
在这种情形下,如果没有显式声明,就默认
T::name
不是个类型。于是这个时候就需要
typename
来显式地告诉编译器这个其实是个类型而非其他东东。当然,在这里
class
是无法替代
typename
的作用的。
书中把上面的情况用语法来描述:
typename
必须放在前面修饰
“
嵌套从属类型名
”(nested dependent type name)
。
个人觉得太拗口难记,所以还是记着上面的例子好了。
不过这个规定也还是有个例外,那就是
typename
作用于
“
嵌套从属类型名
”
时不能用于
(1)
基类列表,以及
(2)
成员初始化列表中。如下所示template <typename T>
class TClass():public BClass<T>::NestedClass
// ----------------------> (1)no typename is needed, nor allowed
{
explicit TClass(int x) : BClass<T>::NestedClass(x)
// -------------------------> (2)no typename is needed, nor allowed
{
//..
}
//..
};
如果觉得这种用法会打很多字
(@#
¥
%)
,
可以用
typedef
来减少一点工作量,改写后的例子是这样的:template <typename T>
void TFunc()
{
typedef typename T::name * pTname;
//ok, now we can do this:
pTname pname1;
pTname pname2;
//..
}