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

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

基类和派生类是这样的:

class BST                            /*base class*/
{
public:
BST() : x1() {}
virtual ~BST()
{
cout << "Good Bye BST." << endl;
}
int x1;
}; class BalancedBST : public BST /*derived class*/
{
public:
BalancedBST() : BST(), x2() {}
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 = ; i < n; ++i)
{
os << arr[i];
}
}

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

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

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

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

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


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

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

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

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

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

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

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

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

C++以多态方式处理数组可能会遇到的问题的更多相关文章

  1. 【M3】绝对不要以多态方式处理数组

    1.考虑下面的情况,有个方法,如下: void Print(ostream& s, const Base array[], int size) { for(int i=0; i< siz ...

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

    1.在数组与多态混用的情况下,数组元素的访问会出现不可预期的结果(因为数组元素的访问会使用到下标运算) 将一个子类对象数组传递给一个父类对象数组声明的函数,编译器会允许这个行为,但是由于子类对象和父类 ...

  3. 在ASP.NET MVC中以post方式传递数组参数的示例

    最近在工作中用到了在ASP.NET MVC中以post方式传递数组参数的情况,记录下来,以供参考. 一.准备参数对象 在本例中,我会传递两个数组参数:一个字符串数组,一个自定义对象数组.这个自定义对象 ...

  4. URL 通过Get方式传递数组参数

    URL 通过Get方式传递数组参数 方法1: ?id=1&id=2&id=3 后台获取时,只需要reqeust.getParameterValues("id") 获 ...

  5. 出于性能考虑,C语言自动地以传地址的方式将数组传递给被调函数 const 编译错误 最小权限原则

    #include <stdio.h> int main(void) { char array[5]; printf("array=%p,&array[0]=%p,& ...

  6. 在ASP.NET MVC中以post方式传递数组参数的示例【转】

    最近在工作中用到了在ASP.NET MVC中以post方式传递数组参数的情况,记录下来,以供参考. 一.准备参数对象 在本例中,我会传递两个数组参数:一个字符串数组,一个自定义对象数组.这个自定义对象 ...

  7. 用最复杂的方式学会数组(Python实现动态数组)

    Python序列类型 在本博客中,我们将学习探讨Python的各种"序列"类,内置的三大常用数据结构--列表类(list).元组类(tuple)和字符串类(str). 不知道你发现 ...

  8. 用递归的方式处理数组 && 把递归方法方法定义到数组的原型上 (这是一次脑洞大开的神奇尝试)

    在 javascript 里,如果我们想用一个函数处理数组 (Array) 中的每个元素,那我们有很多种选择,最简单的当然就是用自带的 forEach 函数(低版本也可以使用 lodash 中的 fo ...

  9. 以forin的方式遍历数组时进行删除操作的注意点

    今天在修改某项需求的时候,需要在遍历的时候将匹配项移除掉,采用的时forin的方式遍历,然后运行的时候却crash掉了 for (NSString*str in self.btnArray) { if ...

随机推荐

  1. 20145329 《Java程序设计》实验二总结

    实验指导教师:娄嘉鹏老师 实验日期:2016.4.12 实验时间:15:30~17:30 实验序号:实验二 实验名称:Java面向对象程序设计 实验目的与要求: 1.初步掌握单元测试和TDD 2.理解 ...

  2. 20135320赵瀚青LINUX第五周学习笔记

    赵瀚青原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 概述 按照刘老师的周从三个角 ...

  3. localAddress

    $(function(){ <% out.println("/** ip:"+request.getLocalAddr()+"("+request.get ...

  4. JAVA基础补漏--Collections工具类排序

    Collections在对自定义对象进行排序时,自定义类需要对compareTo()函数进行重写. public class Student implements Comparable<Stud ...

  5. String中的equals方法解析 jdk1.7

    注  此篇为jdk1.7中的源码解析 equals()方法中的判断分一下步骤 1先判断内存地址是否相同  如果内存地址相同 那么字符串就是相同的 返回true 2 判断当前字符串和参数字是否属于同一类 ...

  6. 随机数的生成 - rand(), srand()

    2017-08-20  17:43:29 writer:pprp 我们采用随机数可以对我们的算法进行大数据检验 /* name : 简单的随机数生成算法 writer : pprp declare : ...

  7. 使用maven命令进行打包,部署项目到远程仓库

    如果要部署项目到远程仓库, 方法一:配置pom.xml: <distributionManagement> <repository> <id>releases< ...

  8. POJ 1144 Network(无向图的割顶和桥模板题)

    http://poj.org/problem?id=1144 题意: 给出图,求割点数. 思路: 关于无向图的割顶和桥,这篇博客写的挺不错,有不懂的可以去看一下http://blog.csdn.net ...

  9. NPM Scripts 2 -- rimraf copyfiles imagemin usemin htmlmin uglifyjs

    NPM Scripts Part 2 Objectives and Outcomes In this exercise you will learn to build a distribution f ...

  10. Mysql数据库实用语句集

    mysql实用语句 (1).从第0行开始取,取3行(一般用于分页)select * from student limit 0,3 (2).查看当前数据库编码 show variables like ' ...