MEC | 条款3 绝对不要以多态(polymorphically)方式处理数组

条款3 绝对不要以多态(polymorphically)方式处理数组

文章目录

  • 条款3 绝对不要以多态(polymorphically)方式处理数组
    • 继承
      • Example 打印基类-派生类数组
        • 传入BalencedBST 数组到函数
      • 删除基类-派生类数组
      • >>>>> 欢迎关注公众号【三戒纪元】 <<<<<

继承

继承(inheritance)最重要的性质之一就是可以通过“指向 base class objects”的 pointers 或 references,来操作 derived class objects。这就是多态(polymorphically)行为。

Example 打印基类-派生类数组

假设有个 基类 class BST(binary search tree) 以及一个继承自BST的 class BalancedBST:

class BST { ... };
class BalancedBST: public BST { ... };

在正式的代码中,BST及其基类可能会被设置为模板类(templates)。

考虑有个函数可以打印 BSTs 数组中的每一个 BST 内容:

void PrintBSTArray(ostream& s, const BST array[], int numElements) {for (int i = 0; i < numElements; ++i) {s << array[i]; // 假设 BST objects 有1个 operator<< 可用}
}

当你将1个由BST 对象组成的数组传递给此函数,是OK的

BST BSTArray[10];
...
PrintBSTArray(cout, BSTArray, 10); // OK

array[i]是1个“指针算术表达式”的简写,代表的是 *(array + i)

array 是个指针,指向数组起始处。

Qarray 所指内存和 array + i 所指内存两者相距多远?

Ai * sizeof(数组中对象)。因为 array[0] array[i] 之间有i个对象。

传入BalencedBST 数组到函数

加入传递到PrintBSTArray函数 的是1个由 BalancedBST 对象组成的数组,编译器会被误导

编译器假设数组中每个元素的大小是 BST 的大小,但其实每个元素大小是 BalancedBST 的大小。

由于 derived classes 通常比 base classes 有更多的 data members,所以 derived classes objects 通常都比 base classes objects 来的大。

因此,编译器为PrintBSTArray 函数所产生的指针算术表达式,对于 BalancedBST objects 所组成的数组而言就是错误的。结果不可预期。

测试:

class BST {public:int a{0};
};class BalancedBST : public BST {public:int b{1};int c{2};
};int main() {BST *bst = new BST[22];std::cout << "print BST array:" << std::endl;PrintBSTArray(bst, 5);BalancedBST* balTreeArray = new BalancedBST[33];std::cout << "print balTreeArray array:" << std::endl;PrintBSTArray(balTreeArray, 5);
}

结果:

print BST array
0
0
0
0
0
print balTreeArray array
0
1
2
0
1

删除基类-派生类数组

同理,如果尝试通过一个 base class 指针,删除1个由 derived class objects 组成的数组,同样存在上述问题:

// 删除1个数组,首先记录关于删除动作的消息
void DeleteArray(ostream& logStream, BST array[]) {logStream << "Deleting array at address "<< static_cast<void*>(array) << '\n';delete [] array;
}// 产生1个 BalancedBST 数组
BalancedBST *balTreeArray = new BalancedBST[33];
... // work
DeleteArray(cout, balTreeArray);

上述代码,仍然存在1个“指针算术表达式”。

当数组被删除,数组中每1个元素的 destructor 都必须被调用,编译器其实看到的这样的句子delete[] array;,相当于看到:

// 将 *array 中的对象以其构造顺序的相反顺序加以析构
for (int i =  the number of elements in the array - 1; i >= 0; --i) {array[i].BST::~BST();
}

C++ 语言规范说,通过 base class 指针删除1个由 derived class objects 构成的数组,其结果未定义。

简单的说,多态(polymorphism) 和指针数组不能混用。

测试:

 int main() {BST* bst = new BST[2];std::cout << "print BST array:" << std::endl;PrintBSTArray(bst, 5);DeleteArray(bst);BalancedBST* balTreeArray = new BalancedBST[3];std::cout << "print balTreeArray array:" << std::endl;PrintBSTArray(balTreeArray, 5);DeleteArray(balTreeArray);std::cout << "Finish!" << std::endl;return 0;}

结果:

print BST array:
0
0
0
0
1041
BST destructor.
BST destructor.
print balTreeArray array:
0
1
2
0
1
BST destructor.
BST destructor.
BST destructor.
Finish!

所以删除 derived class 的时候,会调用基类的析构函数,解决这种办法需要将基类的析构函数设为虚函数。

详见《Effective C++》的《条款7 为多态基类声明virtual析构函数》


>>>>> 欢迎关注公众号【三戒纪元】 <<<<<

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/65341.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

06 为什么需要多线程;多线程的优缺点;程序 进程 线程之间的关系;进程和线程之间的区别

为什么需要多线程 CPU、内存、IO之间的性能差异巨大多核心CPU的发展线程的本质是增加一个可以执行代码工人 多线程的优点 多个执行流&#xff0c;并行执行。&#xff08;多个工人&#xff0c;干不一样的活&#xff09; 多线程的缺点 上下文切换慢&#xff0c;切换上下文典型值…

MySQL入门学习教程(三)

上一章给大家说的是数据库的视图&#xff0c;存储过程等等操作&#xff0c;这章主要讲索引&#xff0c;以及索引注意事项&#xff0c;如果想看前面的文章&#xff0c;url如下&#xff1a; MYSQL入门全套(第一部)MYSQL入门全套(第二部) 索引简介 索引是对数据库表中一个或多个…

Java面试——一分钟搞懂限流算法

为什么限流 运营网站&#xff0c;经常会遇到各种挑战&#xff1a;某黑客发起DoS攻击、网络爬虫网页抓取、商品秒杀活动、双十一与618等场景&#xff0c;会使流量突然激增&#xff0c;如果不限制流量的访问就会使系统宕机。 常见的限流算法 1.漏桶算法&#xff08; LEAKY BUC…

Qt扫盲-Qt Model/View 理论总结 [下篇]

Qt Model/View 理论总结 [下篇] 一、处理Item view 中的选择1. 概念1. 当前 item 和已选 item 2. 使用选择 model1. 选择 item2. 读取选区状态3. 更新选区4. 选择 model 中的所有项 二、创建新 model1. 设计一个 model2. 只读示例 model1. model 的尺寸2. model 头和数据 3. 可…

【0.2】lubancat鲁班猫4远程ubuntu22.04.2 无需任何安装

环境 lubancat4鲁班猫4 (4G0)不带emmc 系统镜像ubuntu-22.04.2-desktop-arm64-lubancat-4.img 网络环境:有线网络与本win10电脑同意环境 操作步骤ubuntu正常开机登陆用户&#xff0c;连接好网络进入设置>网络查看设备当前局域网IP 如192.168.199.159进入设置>共享>远程…

【golang】怎样判断一个变量的类型?

怎样判断一个变量的类型&#xff1f; package mainimport "fmt"var container []string{"zero", "one", "two"} func main() {container : map[int]string{0: "zero", 1: "one", 2: "two"}fmt.Printf…

深入了解 Vue 3 组件间通信机制

什么是组件&#xff1f; 在 Vue3 中&#xff0c;组件是构建应用界面的核心概念之一。组件可以看作是可复用、自包含和可组合的代码块&#xff0c;用于封装 UI 元素和相应的行为逻辑。 通俗来说就是&#xff0c;组件&#xff08;Component&#xff09;是一种对数据和方法的简单…

【Vue3】自动引入插件-`unplugin-auto-import`

Vue3自动引入插件-unplugin-auto-import&#xff0c;不必再手动 import 。 自动导入 api 按需为 Vite, Webpack, Rspack, Rollup 和 esbuild 。支持TypeScript。由unplugin驱动。 插件安装&#xff1a;unplugin-auto-import 配置vite.config.ts&#xff08;配置完后需要重启…

阿里云Windows服务器怎么安装多个网站?

本文阿里云百科介绍如何在Windows Server 2012 R2 64位系统的ECS实例上使用IIS服务器搭建多个Web站点。本教程适用于熟悉Windows操作系统&#xff0c;希望合理利用资源、统一管理站点以提高运维效率的用户。比如&#xff0c;您可以在一台云服务器上配置多个不同分类的博客平台或…

【MySQL--->数据库操作】

文章目录 [TOC](文章目录) 一、操作语句1.增2.删3.改4.查5.备份 二、字符集与校验规则 一、操作语句 1.增 语句格式:create database [if no exists]数据库名[create_specification [,create_specification] …]; 中括号内是可选项,if no exists是指如果数据库不存在就创建,存…

Https、CA证书、数字签名

Https Http协议 Http协议是目前应用比较多应用层协议&#xff0c;浏览器对于Http协议已经实现。Http协议基本的构成部分有 请求行 &#xff1a; 请求报文的第一行请求头 &#xff1a; 从第二行开始为请求头内容的开始部分。每一个请求头都是由K-V键值对组成。请求体&#xf…

混合云环境实现K8S可观测的6大策略

2023年&#xff0c;原生云应用及平台发展迅猛。大量企业都在努力发挥其应用程序的最大潜力&#xff0c;以确保极致的用户体验并推动业务增长。 混合云环境的兴起和容器化技术(如Kubernetes)的采用彻底改变了现代应用程序的开发、部署和扩展方式。 在这个数字舞台上&#xff0c;…