今天读《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构成的数组,其结果是未定义的。所以,多态和指针算术不能混用,数组对象几乎总会涉及指针的算术运算,因而数组和多态不要混用。
本文来自:http://www.cnblogs.com/mengwang024/p/4548821.html
|