3.禁止对数组使用多态

前言

 
多态的本质在于:通过操作指向bc对象的指针或引用来操作dc对象,这样子引用或指针看起来就如同有多种类型。数组也可以使用多态,但其结果几乎不可能与你的预期结果一致。


问题实例

 
假设有一个类BST(比如是搜索树对象)和继承自BST类的派生类BalancedBST:

1
2
class BST { ... };
class BalancedBST: public BST { ... };

现有一个遍历函数:
1
2
3
4
5
void printBSTArray(ostream& s,const BST array[],int numElements){
for (int i = 0; i < numElements;++i) {
s << array[i];
}
}

当我们将含有BalancedBST对象的数组变量传递给遍历函数时,后果如何?
1
2
BalancedBST bBSTArray[10];
printBSTArray(cout, bBSTArray, 10);

答案是编译通过,但无法正常运行。


问题剖析

 
原因很简单:

1
2
3
for (int i = 0; i < numElements;++i) {
s << array[i];
}

array[i]等价于*(array+i),而指针的加法又等价于地址+=i*sizeof(element),一般我们认为sizeof(dc)>=sizeof(bc),所以上述代码会产生不可预期的后果。


删除数组

 
如果我们试图通过一个bc指针去删除dc数组,那么结果也会崩溃,有实例如下:

1
2
3
4
5
6
void deleteArray(BST array[]){
delete [] array;
}
BalancedBST *balTreeArray = new BalancedBST[50];
...
deleteArray(balTreeArray);

当数组被删除时,每一个元素的destructor都会被调用,所以当执行delete[] array时,实际上执行了如下代码:
1
2
3
while(...)
array[i].BST::~BST();
}

C++明确规定,由bc的引用或者指针删除dc,其结果未定义。


总结

 
简单的说,多态和指针算术不能混用.又由于数组总是会牵扯一些指针算术,所以多态和数组也不能混用。
一般而言,如果你尽量“不用一个具体类继承自另一个具体类”,那你应该会很少犯这种错误。