对象与对象数组

对象与对象数组

实验介绍

本章节主要介绍对象数组和对象成员。在实际的开发中,对象数组和对象成员是经常使用的,所以首先需要学习对象数组与对象成员的各种使用方法。

提示:为了方便课程讲解,示例代码使用类内定义的方式实现,如果自己动手做实验的时候希望能够使用分文件类外定义的方式来编写代码。

知识点
  • 对象数组
    • 实例化对象数组
    • 堆上操作对象数组
  • 对象成员
    • 构造和析构顺序
    • 初始化对象成员

对象数组

假设定义了一个学生类,现在要实例化一个班的学生,如果逐个对学生进行实例化操作那肯定是非常麻烦的,这时使用对象数组就能很方便的完成编写。假设有一个点类,如果实例化一个矩形也可以使用对象数组的方式。

点类 - 示例代码 1

定义一个点类,在本小节以后的示例代码中都是用该类,在以下的示例代码中尽量使用之前学到过的知识点。

为了方便查看运行结果,分别在构造函数、拷贝构造函数和析构函数中打印函数的名称。

#include <iostream>
using namespace std;class Point
{
public:// 使用带参数默认构造函数,并使用初始化列表初始化 x,yPoint(double x = 0, double y = 0) : x(x), y(y) {//cout << "Point(double x = 0, double y = 0)" << endl;cout << "Point(double x = " << x << ", double y = " << y << ")" << endl;}// 拷贝构造函数Point(const Point & p) {//cout << "Point(const Point &p)" << endl;// 打印点的值cout << "Point(const Point &p:(" << p.x << ", " << p.y << ")" << endl;this->x = p.x;this->y = p.y;}// 析构函数,由于没有申请内存,析构函数中不需要做什么~Point() {//cout << "~Point()" << endl;cout << "~Point():(" << x << ", " << y << ")" << endl;}// x, y 绑定的成成员函数void setPoint(const Point &p) {this->x = p.x;this->y = p.y;}void setPoint(double x, double y) {this->x = x;this->y = y;}void setX(double x) { this->x = x; }void setY(double y) { this->y = y; }double getX() { return x; }double getY() { return y; }
private:double x;double y;
};

栈上实例化

示例代码 2

为了效果演示,示例代码将对象数组定义在一个函数中,可以在函数执行完之后调用对象数组的析构函数。

// 栈上实例化
void stackInstantiation()
{// 实例化对象数组Point point[3];// 对象数组操作cout << "p[0]: (" << point[0].getX() << ", " << point[0].getY() << ")" << endl;cout << "p[1]: (" << point[1].getX() << ", " << point[1].getY() << ")" << endl;cout << "p[2]: (" << point[2].getX() << ", " << point[2].getY() << ")" << endl;point[0].setPoint(3, 4);cout << "p[0]: (" << point[0].getX() << ", " << point[0].getY() << ")" << endl;
}int main()
{stackInstantiation();return 0;
}

运行结果:

栈上实例化对象数组小结:

  • 实例化对象数组时,每一个对象的构造函数都会被执行。
  • 系统自动销毁栈上对象数组,并且销毁对象数组时,每一个对象析构函数都会被执行。
  • 访问对象数组时使用 [ i ] 的方式访问相应位置的对象。
  • 建议将类数据成员都初始化,可以使用默认值初始化。
  • void setPoint(const Point &p); // 如果是自定义类作为参数时,建议使用引用的方式传入参数,如果该参数在函数中无需修改且没有输出,建议加上 const
示例代码 3
void stackInstantiation()
{Point point[3];Point *p = point;cout << "p: (" << p->getX() << ", " << p->getY() << ")" << endl;p++;cout << "p: (" << p->getX() << ", " << p->getY() << ")" << endl;p++;cout << "p: (" << p->getX() << ", " << p->getY() << ")" << endl;point[2].setPoint(3, 4);cout << "p: (" << p->getX() << ", " << p->getY() << ")" << endl;
}int main()
{stackInstantiation();return 0;
}

运行结果:试验中声明的是对象数组,但是数组其本身也是可以当做指针使用。

堆上实例化

在堆上操作对象数据会比在栈上操作对象数组复杂,但却比栈上操作更加的灵活,如果数据量比较大建议在堆上操作。

示例代码 4
int main()
{// 堆上实例化对象数组Point *point = new Point[3];cout << "p[0]: (" << point[0].getX() << ", " << point[0].getY() << ")" << endl;cout << "p[1]: (" << point[1].getX() << ", " << point[1].getY() << ")" << endl;cout << "p[2]: (" << point[2].getX() << ", " << point[2].getY() << ")" << endl;point[0].setPoint(3, 4);cout << "p[0]: (" << point[0].getX() << ", " << point[0].getY() << ")" << endl;// 释放内存delete [] point;point = nullptr;return 0;
}

运行结果:按照示例代码 3 中的访问方式与栈上访问方式是一样的,跟栈上访问的结果也是一样的。但是别急,后面还有堆上特有的操作。

示例代码 5

在堆上操作对象数据会比在栈上操作对象数组复杂,但却比栈上操作更加的灵活,如果数据量会比较大建议在堆上操作。

// 堆上实例化
int main()
{// 实例化对象Point *p = new Point[3];Point *point = p;cout << "point: (" << point->getX() << ", " << point->getY() << ")" << endl;p++;cout << "point: (" << point->getX() << ", " << point->getY() << ")" << endl;p++;cout << "point: (" << point->getX() << ", " << point->getY() << ")" << endl;point->setPoint(3, 4);cout << "point: (" << point->getX() << ", " << point->getY() << ")" << endl;cout << "p: (" << p->getX() << ", " << p->getY() << ")" << endl;// 释放内存delete [] point;point = nullptr;return 0;
}

运行结果:发现使用指针的方式一样可以访问对象数组,但是使用时也要注意几个问题。

  • 使用 -> 的方式来访问类成员函数,并且不需要使用下标。
  • Point *point = p; 可以发现我又重新声明一个指针,因为一个指针只能指向一个对象,通过指针 ++ 或者 -- 运算符的方式来访问对象数组中对象。

示例代码 6

强调堆上申请空间与释放空间的问题,请注意一下代码与之前的异同之处,在销毁对象数组时使用的是 delete point; 而在之前的示例代码中使用的是 delete [] point; 来销毁对象数组的。

// 堆上实例化
int main()
{// 实例化对象Point *point = new Point[3];cout << "point: (" << point->getX() << ", " << point->getY() << ")" << endl;point++;cout << "point: (" << point->getX() << ", " << point->getY() << ")" << endl;point++;cout << "point: (" << point->getX() << ", " << point->getY() << ")" << endl;point->setPoint(3, 4);cout << "point: (" << point->getX() << ", " << point->getY() << ")" << endl;// 指针使用完成后需要将指针指到起始地址point -= 2;// 释放内存delete point;point = nullptr;return 0;
}

运行结果:

  • linux 环境直接运行报错,但在 Windows 环境下可以正确运行,这就造成了内存泄漏。

对象成员

对象成员即对象中包含其他的对象。

这里示例代码将继续使用示例代码 1 中的点类。

示例代码 7

首先看一下当对象 A 有对象 B 时调用构造函数与析构函数的顺序。

class Line
{
public:Line(const Point & pA, const Point &pB) : pointA(pA), pointB(pB) {cout << "Line(const Point & pA, const Point &pB)" << endl;}Line(double aX, double aY, double bX, double bY) : pointA(aX, aY), pointB(bX, bY) {cout << "Line(double aX, double aY, double bX, double bY)" << endl;}~Line() {cout << "~Line()" << endl;}
private:Point pointA;Point pointB;
};int main()
{// 实例化Line *line = new Line(1, 2, 3, 5);// 释放内存delete line;line = nullptr;return 0;
}

运行结果:可以看到先调用 pointA 的构造函数,再调用 pointB 的构造函数,最后调用 Line 的构造函数;而析构函数时正好反过来的。这也是为什么当对象成员没有默认构造函数时必须要使用初始化列表的原因,因为对象成员先于对象初始化。

示例代码 8

如果将对象成员类型作为参数输入时看看其调用构造函数以及析构函数的顺序。

int main()
{// 实例化Line *line = new Line(Point(1, 2), Point(3, 5));// 释放内存delete line;line = nullptr;return 0;
}

运行结果:对象成员类型作为参数传入时,传入的参数时会临时创建两个对象,初始化完成后临时对象自动销毁。

示例代码 9
int main()
{Line *p = new Line(1, 2, 3, 4);cout << "sizeof (p) = " << sizeof (p) << endl;cout << "sizeof (Line) = " << sizeof (Line) << endl;delete p;p = nullptr;return 0;
}

运行结果:p 指针占 8 字节,Line 类中有两个 Point 类数据成员,Point 类有两个 double 类型数据成员,所以 Line 一共占 32 个字节。

实验总结
  • 使用对象数组时会调用每个对象的构造函数和析构函数。
  • newdeletenew []delete [] 一定要配套使用。
  • 不要越界,不管是栈还是堆,访问数组时都不要越界。
  • 对象数组指针变量本身就是一个指针。
  • 堆上实例化的数组,要注意指针使用方法。
  • 如果是做项目,要考虑使用在堆上实例化申请内存,栈空间比堆空间小很多。
  • 当对象 A 中有常量时必须使用初始化列表。
  • 当对象 A 有其他的对象 B 并且对象 B 没有默认构造参数时需要使用初始化列表。
  • 除了以上两种情况,可以不使用初始化列表,但是推荐使用初始化列表。
  • 对象数据成员和对象成员先于对象初始化。
  • 在实例化对象时需要清楚初始化数据成员的顺序。

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

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

相关文章

19 redis缓存数据同步问题

1、缓存穿透 指缓存和数据库中都没有的数据&#xff0c;而用户不断发起请求。由于缓存不命中&#xff0c;并且出于容错考虑&#xff0c;如果从存储层查不到数据则不写入缓存&#xff0c;这将导致这个不存在的数据每次请求都要到存储层去查询&#xff0c;缓存就没有意义了。 在…

掌控安全 -- header注入

http header注入 该注入是指利用后端验证客户端口信息&#xff08;比如常用的cookie验证&#xff09;或者通过http header中获取客户端的一些信息&#xff08;比如useragent用户代理等其他http header字段信息&#xff09;&#xff0c;因为这些信息是会重新返回拼接到后台中的&…

JAVA定时任务技术总结

在日常的项目开发中&#xff0c;多多少少都会涉及到一些定时任务的需求。例如每分钟扫描超时支付的订单&#xff0c;每小时清理一次数据库历史数据&#xff0c;每天统计前一天的数据并生成报表&#xff0c;定时去扫描某个表的异常信息&#xff08;最终一致性的方案也可能涉及&a…

31、卷积 - 参数 dilation 以及空洞卷积

在卷积算法中,还有一个不常见的参数叫做dilation(中文:膨胀)。 很多同学可能没听说过这个参数,下面看看这个参数有什么作用,用来控制什么的。 我们还是放这个经典的卷积运算图,图中是看不出 dilation 这个参数的存在的。 如果再换一张图呢,发现两图的区别了吗? 没错…

怎么去评估数据资产?一个典型的政务数据资产评估案例

据中国资产评估协会《数据资产评估指导意见》&#xff0c;数据资产评估主要是三个方法&#xff1a;市场法、成本法和收益法。之前小亿和大家分享了数据资产评估方法以及价值发挥的路径&#xff0c;今天结合一个案例来具体讲解一下怎么去评估数据资产。 这个案例是一个典型的一个…

SAM+使用SAM应用数据集完成分割

什么是SAM&#xff1f; SAM(Segment Anything Model&#xff09;是由 Meta 的研究人员团队创建和训练的深度学习模型。在 Segment everything 研究论文中&#xff0c;SAM 被称为“基础模型”。 基础模型是在大量数据上训练的机器学习模型&#xff08;通常通过自监督或半监督学习…

CV计算机视觉每日开源代码Paper with code速览-2023.12.6

点击计算机视觉&#xff0c;关注更多CV干货 论文已打包&#xff0c;点击进入—>下载界面 点击加入—>CV计算机视觉交流群 1.【基础网络架构&#xff1a;Transformer】Rejuvenating image-GPT as Strong Visual Representation Learners 论文地址&#xff1a;https://a…

从零开始搭建企业管理系统(六):RBAC 权限管理设计

RBAC 权限管理设计 前言权限分类功能权限设计什么是 RBACRBAC 组成RBAC 模型分类基本模型RBAC0角色分层模型RBAC1角色限制模型RBAC2统一模型RBAC3 RBAC0 权限设计用户管理角色管理权限管理关联表 总结 前言 作为一个后台管理系统&#xff0c;权限管理是一个绕不开的话题&#…

视频剪辑:视频创意制作,背景图片融合视频制作画中画效果

随着社交媒体的兴起&#xff0c;视频制作不再仅仅是专业人士的专利。每个人都可以通过一些技巧&#xff0c;创作出独特而富有吸引力的视频内容。视频剪辑是一种非常重要的技术&#xff0c;它能让视频从平淡无奇变为生动有趣。背景图片融合视频制作画中画效果&#xff0c;也能增…

springboot利用easyexcel在浏览器中下载excel

前言 项目中操作excel是一种很常用的功能&#xff0c;比如下载一份excel的报价单。这篇文章会介绍一款excel的处理工具以及导出遇到的三个常见异常(重要)。 之前遇到一个这样的需求&#xff1a;后台管理页面&#xff0c;点击下载按钮&#xff0c;下载一份excel格式的报价清单…

《人工智能导论》知识思维导图梳理【1~5章节】

文章目录 说明第一章 绪论人工只能概述 第二章 知识表示和知识图谱一阶谓词逻辑和知识表示法产生式表示和框架表示法 第三章 确定性推理方法推理的基本概念自然演绎推理归结演绎推理谓词公式化子句集鲁宾孙归结原理归结反演归结反演求解问题 第四章 不确定性推理方法似然推理可…

npm run build时提示vue/types/jsx.d.ts中的错误

解决方法一&#xff1a; 可能是因为vue版本过高引起的 我直接将package.json中vue以及vue-template-compiler的版本的前面^去掉&#xff0c;安装指定的版本 注意&#xff1a;vue和vue-template-compiler需要版本一致 参考链接&#xff1a;链接 解决方法二&#xff1a; 如果如…

QT使用SQLite 超详细(增删改查、包括对大量数据快速存储和更新)

QTSQLite 在QT中使用sqlite数据库&#xff0c;有多种使用方法&#xff0c;在这里我只提供几种简单&#xff0c;代码简短的方法&#xff0c;包括一些特殊字符处理。在这里也给大家说明一下&#xff0c;如果你每次要存储的数据量很大&#xff0c;建议使用事务&#xff08;代码中…

canvas 有趣的弹簧效果

先上效果 两个小球之间有一根弹簧&#xff0c;这里有一条线表示&#xff0c;其中左球固定&#xff0c;在点击开始后&#xff0c;右球开始做自由落体 思路 先做受力分析 经过受力分析可以发现&#xff0c;整个系统一共有三个力在起作用&#xff0c;我们分别把他们求出来并合成…

控制台打印如来佛图像

代码 System.out.println(" _ooOoo_ \n"" o8888888o \n"" 88 \".\" 88 …

python——第十七天

方法重写(overwrite) 、方法覆盖(override )&#xff1a;在继承的基础上&#xff0c;子类继承了父类的方法&#xff0c;如果不能满足自己使用&#xff0c;我们就可以重写或覆盖该方法 函数重载(overload)&#xff1a; 在强数据类型的编程语言中(如Java、C、C等等): 函数名称…

PDI/Kettle-9.4.0.0-343源码下载及编译

目录 &#x1f351;一、概要&#x1f34a;最新版本10.x&#xff08;2023-11-30&#xff09; &#x1f351;二、下载&#x1f351;三、编译&#x1f34a;3.1、导入开发工具&#x1f34a;3.2、开始编译&#x1f34a;3.3、编译报错&#x1f34a;3.4、报错原因&#xff1a;jdk版本低…

centos7安全防护_CPU占用率超过百分之300_centos7.4中毒CPU百分之百_清理毒源---Linux工作笔记068

执行top命令的时候看到有个进程: sshd占用cpu百分之300多...而且就算是kill -9 杀掉进程以后,进程又会自动启动 ll /proc/7298 我们执行这个命令,可以看到有个/var/tmp/sshd的文件 我们进入cd /var/tmp 然后我们执行 rm -rf sshd删除这个文件,然后我们再去top可以看到 cpu就…

【数仓理论】

一、数仓建模方法论 1.1 ER模型&#xff08;Entity Relationship、实体关系模型、范式模型&#xff09; ER模型是Bill Inmon提出的一种建模方法&#xff0c;实体关系模型将复杂的数据抽象为两个概念 ---- 实体和关系 该模型在范式理论上符合3NF&#xff0c;这种模型目的是减少…

测距传感器

测距传感器 电子元器件百科 文章目录 测距传感器前言一、测距传感器是什么二、测距传感器的类别三、测距传感器的应用实例四、测距传感器的作用原理总结前言 测距传感器广泛应用于自动化控制、机器人导航、无人驾驶、测量仪器等领域。不同类型的测距传感器具有不同的测距范围、…