1.在数组与多态混用的情况下,数组元素的访问会出现不可预期的结果(因为数组元素的访问会使用到下标运算)
将一个子类对象数组传递给一个父类对象数组声明的函数,编译器会允许这个行为,但是由于子类对象和父类对象的内存结构不同,会导致运行结果异常,因为在这种情况下,编译器仍然假设每一个元素的大小是父类对象元素的大小,但此时实际上每一个元素的大小是子类对象元素的大小
#include<bits/stdc++.h> using namespace std; class BST { public: char c; }; class balancedBST:public BST//公有继承 { public: int x; }; void printBSTArray(ostream& s,const BST array[],int numElements)//打印基类数组,注意数组的类型为基类! { for(int i=0;i<numElements;i++) s<<array[i].c; s<<endl; } int main() { BST array[3]; for(int i=0;i<3;i++) array[i].c='A'+i; printBSTArray(cout,array,3);//传递进入函数的是基类数组,打印正常 balancedBST barray[3]; for(int i=0;i<3;i++) { barray[i].x=i+1; barray[i].c='a'+i; } printBSTArray(cout,barray,3);//传递进入函数的是子类数组,打印异常! /* 异常的原因: 因为函数定义的时候该对象的数组定义的是为基类类型,所以编译器在后续的操作中, 无论传入函数的是基类对象数组还是子类对象数组,都将视为基类对象数组 而基类对象的内存结构和子类对象的内存结构是不一样的 这样,通过下标访问到的东西,其实不是你想访问的东西 array[i] *(array+i) array[0]和array[i] 之间相隔 i*sizeof(BST) 函数是按照i*sizeof(BST)算的,而如果你传入的是基类对象数组,也按照这样算的话,肯定是访问不到你想要的东西的 */ } /* 运行结果: ABC ai */
2.在多态与数组混用的情况下,数组元素的删除也存在问题,会使用到没有定义的操作(通过基类指针来删除子类对象数组的行为在C++中是未定义的!)
#include<bits/stdc++.h> using namespace std; class BST { public: char c; }; class balancedBST:public BST//公有继承 { public: int x; }; void deleteArray(ostream& logStream,BST array[]) { logStream<<"Deleting array at adress: "<<static_cast<void*>(array)<<endl; delete [] array;//该操作的底层其实使用了下标,如果传入的是子类对象数组,那么通过基类指针来删除子类对象数组的行为在C++中是未定义的! } int main() { BST *array =new BST[3]; balancedBST *barray =new balancedBST[3]; deleteArray(cout,array);//传入的是基类对象 deleteArray(cout,barray);//传入的是子类对象,存在问题,底层使用了未定义的操作 }
总结:那么如何避免上述两种问题呢?
第一个办法是避免采用多态方式来处理数组
第二个方法是避免让一共具体类继承自另外一个具体类