一个二叉搜索树的例子:
class BST { ... };
class BalancedBST: public BST{...};
有这样一个函数,他能打印BST的对象:
void printBSTArray(ostream& s, const BST array[], int numElements)
{
for (int i = 0; i < numElements; ) {
s << array[i];
}//假设重载了<<运算符
}
这样的程序,传入一个BST对象,当然没问题,但是当地我们传入一个BST的子类的时候,就将会产生未定义的行为。
因为,对于数组的内存地址的计算方式是这样处理的。 array[i]只是一个指针算法的缩写:它所代表的是*(array)。我们知道 array是一个指向数组起始地址的指针,但是 array 中各元素内存地址与数组的起始地址的间隔究竟有多大呢?它们的间隔是 isizeof(一个在数组里的对象),因为在 array 数组[0]到[I]间有 I 个对象。编译器为了建立正确遍历数组的执行代码,它必须能够确定数组中对象的大小,这对编译器来说是很容易做到的。参数 array 被声明为 BST 类型,所以array 数组中每一个元素都是 BST 类型,因此每个元素与数组起始地址的间隔是 isizeof(BST)。但是当我们传入的是一个BST的派生类的时候,这个派生类的对象的大小往往都是比基类更大的,因此,这时,我们的指针数组的计算方式是按照传入的参数(基类类型)来计算下一个地址,这样的计算方式显然是不对的。
当用多态的方式进行删除对象时,也会同样的产生这样的未定义行为,对于delete [ ] 这样的运算符,编译器将会把它解析为这样的方式。
for ( int i = 数组元素的个数 1; i >= 0;--i)
{
array[i].BST::~BST(); // 调用 array[i]的
}
于是便会产生用基类对象的析构函数去析构派生类的对象这样的结果,这样便会导致未定义行为。
对于多态使用数组的情况,建议用vector代替。