清早,风从窗外踏进,穿堂而过。我还在回味刚刚的梦境,和梦境中呈现的纯真的友谊所带来的泪水。
“怎么,电脑找到了吗?”我起身跑到了隔壁,看到一伙人坐在那高谈阔论,一洗昨日的颓废。
“差不多了,昨晚警察审讯了一夜,他交代了。”
“他是谁?”
“就是4室的那个,高高的,黑黑的。”
“他?难以置信……我曾屡屡和他亲切地交谈,他『看上去』那么……!”
不过这让我想起了C++。
来看一个例子(取自Scott Meyers 的EC条款13),目的是为解决C/C++程序员不可自定义数组下界的“痛苦”(也许是痛苦,也许是福音),不过这不是今天的重点。
template<class T>
class Array {
public:
Array(int lowBound, int highBound);
...
private:
vector<T> data; // 数组元素安放于vector<T>容器
size_t size; // 数组大小
int lBound, hBound; // 下界,上界
};
template<class T>
Array<T>::Array(int lowBound, int highBound)
: size(highBound - lowBound + 1),
lBound(lowBound), hBound(highBound),
data(size) {}
这个程序唯一的好处也许是:你的编译器或者老板(如果是老板,你最好别跟他谈提薪的事!),决不会因之而给你宽恕或怜悯,乃至任何有关成功的妄想(人们总是很容易地就相互同情,所以有时候冷漠的拒绝完全堪称一项“好处”)。
来看看它干了什么。它小心翼翼地先初始化size,然后再用size去初始化data。不过,这只是『看上去』罢了。编译、组建都没有问题,可是运行时,灾难来了。
原因是data在初始化时,size却还未定义(undefined)。这是不能容忍的。C++没有Java那么“博爱”(有人称之为“溺爱”),在Java的世界里,对象在使用之前必须要被初始化才行,编译器会强制你这么做(这可能就是“溺爱”的原由)。C++就没有这么周到的家境了,如前面的size,在你显式地(explicit)初始化它之前,鬼知道它会是什么(也许是0,也许不是)!
先知Scott Meyers告诉我们,class members以它们于class中声明的次序被初始化,而不是以它们出现于初始化列表中的次序被初始化。所以,最好让你的初始化列表的成员次序与它们声明的次序相吻合。(具体原因请翻阅Meyers的书)。
C++不会错,错的只会是你,是你不懂标准就乱来。其实问题解决起来还算简单,只要按照下面这样改了就行——
……
private:
size_t size;
int lBound, hBound;
vector<T> data;
……
template<class T>
Array<T>::Array(int lowBound, int highBound)
: size(highBound - lowBound + 1),
lBound(lowBound), hBound(highBound),
data(size) {}
现在世界重新恢复秩序,人们(程序员们)从此安居乐业……
且慢,假若再这样地改一下呢——
template<class T>
class Array {
public:
Array(int lowBound, int highBound);
...
// 将members安放于两个access section
private:
size_t size;
int lBound, hBound;
private:
vector<T> data;
};
初始化列表照写如上,编译,运行,没有问题。
若这样改呢——?
// 交换两个access section的顺序
private:
vector<T> data;
private:
size_t size;
int lBound, hBound;
初始化列表照写如上,编译,运行,呜——!有问题!
来看看先知Stanley Lippman是怎么说的:C++中凡处于同一个access section的数据,必定保证以其声明次序出现在内存布局当中。然而被放置在多个access sections中的各笔数据,排列次序就不一定了。
我有一些疑惑:Meyers 说“class members以它们于class中声明的次序被初始化”,“members于class中声明的次序” 难道是指它们于内存中布局的次序?这样想好象可以理解,然而如上面的两个access section,data在上size在下时,不是说内存布局不一定吗,那么为什么在data 所处的section在下时可以,在上时就不可以呢?
结论只能是:members于class中声明的次序,不等于它们于内存中的布局(除非它们位于同一access section),而初始化是按声明次序,没必要与内存布局也扯上瓜葛(否则,倘若涉及到派生时,base class与derived class各自成员的内存布局也是不确定的,那岂不更加麻烦?)。
/// :~