今天读《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. Java并发编程:Lock(转)

    本文转自:http://www.cnblogs.com/dolphin0520/p/3923167.html Java并发编程:Lock 在上一篇文章中我们讲到了如何使用关键字synchronized ...

  2. Win32GUI代码示例

    // Win32UI.cpp : 定义应用程序的入口点. // #include "stdafx.h" #include "Win32UI.h" #includ ...

  3. [原创]CSS 去掉点li 的点 使得LI前面的点不在显示

    我对博客的认识是:记录问题,解决问题,分享知识.如果有轮子,我不需要造轮子. 1.问题解决方式: 设置属性:li {list-style-type:none;} 2.更多属性参数参考 list-sty ...

  4. 吉哥系列故事——完美队形I

    /*hud4512 dp[i]表示当前以下标i结束的最长公共上升子序列. 我们让第一个序列为原序列,第二个序列为原系列的反向. 则,也就是说,第二个序列的顺序为原序列的下标[n-1,0],设为j 当j ...

  5. SQL学习笔记之MySQL索引知识点

    0x00 概述 之前写过一篇Mysql B+树学习,简单的介绍了B+数以及MySql使用B+树的原因, 有了这些基础知识点,对MySql索引的类型以及索引使用的一些技巧,就比较容易理解了. 0x01 ...

  6. python 切换工作路径 为指定文件夹

    切换工作路径为了更好的在目录下面执行针对当前项目的命令 比如 git.svn .打包 等操作 关键命令: os.chdir() 切换到指定路径: # sys.path[0] 当前脚本路径 # 当前脚本 ...

  7. 索引原理-btree索引与hash索引的区别

    btree索引与hash索引的区别,之前不清楚,mark一下. Hash索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree索引需要从根节点到枝节点,最后才能访问到页节点这样多 ...

  8. 201453131《Java程序设计》实验三实验报告

    实验三 敏捷开发与XP实践 实验内容 •下载并学会使用git上传代码: •与同学结对,相互下载并更改对方代码,并上传: 实验步骤 下载并用git上传代码: •1.下载并安装好git,在cmd中输入gi ...

  9. Android执行shell命令 top ps

    Android执行shell命令 一.方法 /** * 执行一个shell命令,并返回字符串值 * * @param cmd * 命令名称&参数组成的数组(例如:{"/system/ ...

  10. 实现在vista和win7中使用管理员权限接收WM_DROPFILES(OnDropFiles())消息的方法(好像XP不支持这个函数)

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #pragma once   #ifndef WM_COPYGLOBALD ...