C++以多态方式处理数组可能会遇到的问题

今天读《More Effective C++》时遇到一个条款:绝对不要以多态方式处理数组。以前自己也没有注意过,觉得有必要记录下来。

 

C++是允许通过base class的指针或引用来操作derived class所形成的数组的。但发生的事情可能会令你感到意外。下面举例说明:

基类和派生类是这样的:

class BST                            /*base class*/
{
public:
    BST() : x1(1) {}
    virtual ~BST()
    {
        cout << "Good Bye BST." << endl;
    }
    int x1;
};

class BalancedBST : public BST        /*derived class*/
{
public:
    BalancedBST() : BST(), x2(2) {} 
    virtual ~BalancedBST()
    {
        cout << "Good Bye BalancedBST." << endl;
    }
    int x2;
};

下面我重载了两个输出操作符:

/*输出base class*/
ostream& operator<<(ostream& os, const BST& obj)        
{
    os << "class BST: " << obj.x1 << endl;
    return os;
}

/*输出derived class*/
ostream& operator<<(ostream& os, const BalancedBST& obj)    
{
    os << "Class BalancedBST: " << obj.x1 << ' ' << obj.x2 << endl;
    return os;
}

下面这个函数用于输出base class和derived class的数组。

/*输出base class和derived class数组*/
void Print(ostream& os, const BST arr[], int n)
{
    for (int i = 0; i < n; ++i)
    {
        os << arr[i];
    }
}

当以如下方式测试时,没有问题。

BST baseArr[10];
Print(cout, baseArr, 10);    //好的,没问题,正常

当以如下方式测试时,就会出现问题。

BalancedBST deriveArr[10];
Print(cout, deriveArr, 10);    //出错啦

编译器要想遍历数组中每一个元素,它必须知道每一个元素的大小。很明显,当print参数为BalancedBST数组时,编译器静态的将其数组大小当作BST的大小处理,以*(i+arr)的方式前进,结果是未知的。


还有一种情况,就是通过一个base class指针,删除一个由derived class组成的数组。

当以如下方式测试时,没有问题。

BST *base = new BST[10];
delete [] base;                    //好的,没有问题

BalancedBST *derived = new BalancedBST[10];
delete [] derived;                //好的,没有问题

当我以如下方式测试时,就会有问题。

当数组被删除时,数组中每个元素的destructor会被调用,调用的顺序与构造顺序相反。也就是说执行delete [] base语句时,会产生类似下面的代码。

for (int i = 9; i >= 0; --i)    //编译器产生类似的代码,但是是错误的。
{
    base[i].BST::~BST();
}

根本原因还是编译器把derived class数组成员的大小当作base class来计算数组元素的位置。

 

C++规定,通过base class指针删除一个由derived class objects构成的数组,其结果是未定义的。所以,多态和指针算术不能混用,数组对象几乎总会涉及指针的算术运算,因而数组和多态不要混用。

posted @ 2015-06-03 14:21  QingLiXueShi  阅读(1478)  评论(0编辑  收藏  举报