【More Effective C++ 条款3】最好不要以多态方式处理数组

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
ai
*/

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);//传入的是子类对象,存在问题,底层使用了未定义的操作
}


总结:那么如何避免上述两种问题呢?

第一个办法是避免采用多态方式来处理数组

第二个方法是避免让一共具体类继承自另外一个具体类



上一篇:LaTeX 中的参考文献(.bib) BibTex 用法


下一篇:【LeetCode】230. Kth Smallest Element in a BST