条款3:绝对不要以多态(polymorphically)方式处理数组

继承(inheritance)的最重要性质之一就是:你可以通过“指向 base class objects"的pointers 或references,来操作derived class objects。

如此的pointers和references,我们说其行为是多态的(polymorphically)——犹如它们有多重类型似的。

C++也允许你通过 base class的pointers和references 来操作“derived class objects 所形成的数组”。但这一点也不值得沾沾自喜,因为它几乎绝不会如你所预期般地运作。

举个例子,假设你有一个class BST(意思是binary search tree)及一个继承自BST的 class
在一个真正具规模的程序中,这样的classes 可能会被设计为templates,不过这不是此处重点;如果加上template各种语法,反而使程序更难阅读。针对目前的讨论,我假设BST 和 BalancedBST 都只内含 ints。

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

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

void printBSTArray(ostream& s, const BST arrayl], 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);
//可以正常运行吗?

你的编译器会毫无怨言地接受它,但是看看这个循环(就是稍早出现的那一个)。

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

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,所以derived class objects 通常都比其 base class objects 来得大。

因此,我们可以合理地预期一个 BalanceaBsT objoct 比一个 BST object 大。

如果是这样,编译器为PrintBSTArray 函数所产生的指针算术表达式,对于BalancedBST objects所组成的数组而言就是错误的。至于会发生什么结果,不可预期。无论如何,结果不会人愉快。

如果你尝试通过一个base class指针,删除一个由 derived class objects组成的
数组,那么上述问题还会以另一种不同面貌出现。下面是你可能做出的错误尝试:

//删除一个数组,但是首先记录一个有关此删除动作的消息。
void deleteArray(ostream& logStream, BST arraytj)
dogstream < "Deleting array at address"
ce static_cast<void*>(array)<< '\n';
delete [] array;
BalancedBST *balTreeArray
//产生一个 BalancedBST 数组。
new BalancedBST[50];
deleteArray(cout, balTreeArray);//记录此删除动作。


虽然你没有看到,但其中一样有“指针算术表达式”的存在。是的,当数组被删除,数组中每一个元素的destructor 都必须被调用(见条款8),所以当编译器看到这样的句子;

delete [] array;


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

//将*array中的对象以其构造顺序的相反顺序加以析构。
for (int i = the number of elements in the array - 1; i >= 0; --i)
{array[i].BST::~BST();//调用array[i]的 destructor。}


如果你这么写,便是一个行为错误的循环。编译器如果产生类似代码,当然同样是个行为错误的循环。C++语言规范中说,通过base class指针删除一个由derived classes objects 构成的数组,其结果未定义。我们知道所谓“未定义”的意思就是:执行之后会产生苦恼。简单地说,多态(polymorphism)和指针算术不能混用。

数组对象几乎总是会涉及指针的算术运算,所以数组和多态不要混用。
 

  • 17
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值