More Effective C++ 条款03:绝对不要以多态方式处理数组

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

现在考虑有个函数,用来打印BSTs数组中的每一个BSTs数组中的每一个BST的内容:

void printBSTArray(ostream& s, const BST array[], int numElements)
{
    for(int i = 0; i < numElements; ++i){
        s << array[i];   //假设BST objects有一个 operator << 可用
    }
}

将一个BST对象组成的数组传给此函数,没问题

BST BSTArray[10];
printBSTArray(cout, BSTArray, 10);  //运行良好

然而如果将一个BalancedBST对象所组成的数组交给printBSTArray函数,会发生什么什么事:

BalancedBST bBSTArray[10];
printBSTArray(cout, bBSTArray, 10); //运行结果不对

array[i]其实是一个“指针算数表达式”的缩写:它代表的其实是*(array + i)。我们知道array是个指针,指向数组起始处。array所指内存和array+i所指的内存两者相距多远?答案是 i*sizeof(数组中的对象),因为array[0]和array[i]之间有i个对象,为了让编译器所产生的代码能够正确走访整个数组,编译器必须有能力决定数组中的对象大小。很容易呀,参数array不是被声明为“类型为BST”的数组吗?所以数组中的每个元素必然都是BST对象,所以array和array+i之间的距离一定是i*sizeof(BST)。但如果你交给printBSTArray函数一个由BalancedBST对象组成的数组,你的编译器就会被误导。这种情况下,它仍然假设数组中每个元素的大小是BST的大小,但其实每一个元素的大小是BalancedBST的大小。由于derived classes通常比其base classes有更多的data members,所以子类对象都比基类对象大得多。因此,我们我们合理地预期一个BalancedBST对象比一个BST对象大,如果是这样,编译器为printBSTArray函数所产生的指针算数表达式,对于BalancedBST对象所组成的数组而言就是错误的。

如果尝试通过基类指针删除一个由子类对象组成的数组,那么同样有错误:

void deleteArray(ostream& logStream, BST array[])
{
    logStream << "Delete array at address " << static_cast(array) << endl;

    delete [] array;
}

BalancedBST *balTreeArray = new BalancedBST[50]; //产生一个BalancedBST数组
deleteArra(cout, balTreeArray);

delete []array;

必须产出类似这样的代码:

for(int i = the number of elements in the array - 1; i >= 0; --i)
{
    array[i].BST::~BST(); //调用array[i]的destructor
}

通过base class 删除一个由子类对象构成的数组,其结果未定义。

总结:多态和指针算数不能混用。

你可能感兴趣的:(More,Effective,C++,c++,蓝桥杯,开发语言)