C++中函数变量布局小结
把布局作为一种信仰(Layout as Religion)。
--Steve McConnell(《代码大全》一书作者)
在 C 语言的早期版本中,由于规定变量的赋值必须在所有变量的声明之前,因此经常能看到如下形式的代码:
void func1() {
int a1, a2, a3;
double b1, b2, b3;
double* c1;
.....
b1 = b2 = b3 =0.0;
......
c1 = NULL;
.....
a1 = 3+a2;
.....
}
阅读上述代码的一个麻烦之处在于,要记得所有变量第一次赋值的地方,比如上述代码中后面涉及到的 a1 的赋值就需要去前面找到它第一次出现的地方,然后依次查看其赋值信息。在《代码大全》中有个形象的比喻,早期的 C 函数变量的声明和使用风格就像在第一集电视剧中将所有的演员表都列出来,等第二集以后就不再提供演员表,如果想要找到演员的信息,只好到第一集中去找。显然,这对于观众来说相当不方便。对应到程序里,读代码的人读到这段代码也会很头疼。
回到比方那里,一个合适的方案是,每集电视剧中出现的演员只会在本集的演员表中,这样就不用每次去第一集中去找演员的信息了。同样地,C 的后续版本以及 C++ 就采取了这种方式,它放弃了C 中原有的变量的赋值必须在所有变量的声明之后,而是可以直到使用到该变量的时候再去声明,在 C++ 中,代码的布局格式为:
void func1() {
double b1 = 0.0, b2 =0.0, b3 =0.0;
......
double* c1 = NULL;
......
int a1 = 0, a2 =0, a3 = 0;
a1 = 3+a2;
.....
}
阅读修改之后的代码, 它的层次分明了很多,总结起来就是变量初始化原则:在靠近第一次使用变量的位置声明和使用该变量[1]。
如果考虑到调试的因素,变量初始化原则有时候也要发生些变化。如果调试过程中观察中间的运行结果,假设用 showTempResult()函数来表达,为了省去频繁的注释和取消注释代码操作,不妨使用一个变量bool isShowRes来控制这个函数的调用,按照前面提到的规则,代码的布局格式为:
void func1() {
double b1 = 0.0, b2 =0.0, b3 =0.0;
......
double* c1 = NULL;
......
bool isShowRes = FALSE;
if (isShowRes == TRUE) {
showTempResult();
}
int a1 = 0, a2 =0, a3 = 0;
a1 = 3+a2;
.....
}
当代码量比较少时,比如小于一个屏幕的显示(一般地,约50-150行,IBM 曾经把子程序的长度限制在50行以内 [1]),这个时候主要将程序从上往下找就可以了。但是,当该函数的代码量比较大,调试的时候就显得有些麻烦了。比如我最近用到的那个函数就有几百行,而不巧的是,要进行显示的地方位于子函数的后半段,只好每次记住显示函数的名字,进入搜索,查找到调用该函数的位置,对控制该函数调用的变量 isShowRes 的值进行修改。一个合适的方法是将该调试相关的变量提到最前面,必要的话加上相应的注释信息,每次要调用显示函数的时候直接在函数的前面进行修改,代码的布局格式为:
void func1() {
bool isShowRes = FALSE; //用于控制是否显示中间结果
double b1 = 0.0, b2 =0.0, b3 =0.0;
......
double* c1 = NULL;
......
if (isShowRes == TRUE) {
showTempResult();
}
int a1 = 0, a2 =0, a3 = 0;
a1 = 3+a2;
.....
}
细心的童鞋可能会问,为什么不把函数的代码行限制在一个屏幕内呢,这样就不用违背变量初始化原则了?其实,不是不想,而是不能。原因是,如果在遗留代码上进行调试,而遗留代码的该函数的代码本身就很长,将这个很长的代码行修改为较短的代码行谈何容易?即便不去考虑具体的实现细节,光想想函数的参数列表的个数就相当恐怖,当然这在理论上可以通过结构体(truct) 或者C++ 的类来克服,不过,代码的修改量仍然是巨大的。当修改的时间比代码的质量优先级更高的时候,只好牺牲代码质量来换取代码的健壮性和正确性。因此,如果是初次 Coding 的话,请尽可能遵循使用 C++ 中的类来减少函数的参数列表以及子函数的行数尽量短的原则,它会为以后的调试带来巨大的方便。
参考资料:
[1] 《代码大全(第2版)》, Steve McConnell著,金戈等译, 电子工业出版社,2006年3月:10.3 变量初始化原则 7.4 子程序可以写多长