今天,翻看了一下上大学时写的一些练习代码。看到了以下一段C代码:
float sum(float a[], unsigned length)
{
int i;
float result = 0.0f;
for(i = 0; i<=length-1; i++)
result += a[i];
return result;
}
这段c code虽说能编译通过,但是,在length接受为0时,发生存储越界,而不是期望中的0.0f。
归咎原因,可以说是“i<=length-1”上发生的类型转换造成的,当然,这是从语言的角度来看待这个问题的。
更为本质的问题是,没有对length作范围检查!所以,更应该说是programmer的素质问题。
好,从中可以知道:
1 c的内部类型转换是混乱的.也就是在对待unsigned length和int length上的问题.
2 依赖于c的类型转换是危险的.
3 对于int和unsigned这样类型的值,有时需要类型检查,而这一步没有语言的直接支持.
对于1和2,只有靠自身的修为;对待3,在C语言里最多可以写个简单的宏或inline函数,比如:
inline int rangeCheck(unsigned n, unsigned lower, unsigned upper)
{
if( nupper)
return 0;
else
return 1;
}
这样的代码当然有许多缺陷.
到了c++也存在着这样的问题,看下面的:
templateclass Stack{
public:
Stack(unsigned size);
// others
};
//...
Stack s1(9); // OK!
Stack s2(0); // ??
Stack s3(-4); // ??
于是可能会想把constractor改成"Stack(int size);".
这么做意味着Stack模板类的不变性的一部分由用户负责,于是在这个类的文档中会有一些特别说明.这样做不见的有多好.
开动脑筋之后,于是有:
templateinline void Assert(Expression assertion)
{
if(!assertion)
throw Except();
}
// 范围(low, high)
templateclass Range
{
struct Error{};
int val;
void Check(int i)
{
Assert(low <= i && i <= high);
}
public:
class Error{};
Range(int i)
{
Check(i);
val=i;
}
operator int () { return val; }
operator int () const { return val; }
};
然后,再写Stack的实现就成了:
#include
templateclass Stack{
public:
Stack(Range<0,numeric_limits::max() > size);
// others
};
// user code
Stack s1(9); // OK!
Stack s2(0); // occur exception
Stack s3(-4); // occur exception
这么做实在太好了,用户代码的结构清晰.Stan Lippman也有过一篇文章论述过以上问题.