今天读《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. servlet中通过getWriter()获取out返回给浏览器出现中文乱码问题

    感谢博主https://blog.csdn.net/louyongfeng3613/article/details/50160317 在Servlet编程中,经常需要通过response对象将一些信息 ...

  2. Eclipse配置多个jdk

    Eclipse配置多个jdk 步骤: 1,打开windows > Preferences: 2,点击“Add”,新增jdk,选择“Standard VM”: 3,下一步,选择对应版本的jer: ...

  3. excel表中同一列相同内容进行合并

    如下图所示一张表格,如果想要把表格中相同考号的信息合并来查看的话,首先我们先通过表格的排序功能,把相同的内容先排列在一起.在数据选项卡下找到排序点就可以.   第二步,选中整个数据区域,在数据选项卡下 ...

  4. spark SQL学习(spark连接 mysql)

    spark连接mysql(打jar包方式) package wujiadong_sparkSQL import java.util.Properties import org.apache.spark ...

  5. Sqoop-问题

    1.权限问题 // :: INFO mapreduce.Job: Job job_1555064328415_0328 failed with state FAILED due to: Job set ...

  6. codeforce AIM tech Round 4 div 2 B rectangles

    2017-08-25 15:32:14 writer:pprp 题目: B. Rectangles time limit per test 1 second memory limit per test ...

  7. 安装mysql数据库中的技巧、错误排查

    针对解压版本5.7.16(博主使用的这个版本.在某些低版本中部分命令失效) 一.安装.初始化data目录(解压版解压后没有data目录) 安装:配置path环境变量,然后管理员运行命令提示符cmd   ...

  8. 实现基本的Ajax和Json请求

    前面已经封装好了一个方法ajax(),通过这个方法可以实现Ajax请求,接下来就是给出 例程来测试这个方法和实现简单的功能.   视图的部分代码如下: <body> <div> ...

  9. 【Python】关于使用pycharm遇到只能使用unittest方式运行,无法直接选择Run

    相信大家可能都遇到过这个问题,使用pycharm直接运行脚本的时候,只能选择unittest的方式,能愁死个人

  10. 毕业设计总结(1)-canvas画图

    去年6月底完成的毕业设计,到现在也才开始给它做个总结,里面有很多可以学习和借鉴的东西. 我的毕业设计的题目是“一种路径规划算法的改进与设计”,具体的要求可参见下面的表格: 题目 一种路径规划算法的改进 ...