对象与对象数组

对象与对象数组

实验介绍

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

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

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

对象数组

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

点类 - 示例代码 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.hqwc.cn/news/266340.html

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

相关文章

【JUC】二十四、线程局部变量ThreadLocal

文章目录 1、ThreadLocal2、常用方法3、案例4、线程池下必须remove掉线程的LocalThread值5、Thread、ThreadLocal、ThreadLocalMap6、Thread、ThreadLocal、ThreadLocalMap的关系 1、ThreadLocal ThreadLocal提供线程局部变量&#xff0c;这些变量与正常的变量不同&#xff0c;…

vm虚拟机固定IP

最近使用vm虚拟机 &#xff0c;可用了一段时间ip就自动变化&#xff0c;于是去网上看了不少教程&#xff0c;但很多都没用。 1.编辑配置 vim /etc/sysconfig/network-scripts/ifcfg-ens33 修改BOOTPROTO为static加入属性IPADDR,设置你想要设置的ip配置GATEWAY与DNS1 不配置GA…

Leetcode—2962.统计最大元素出现至少 K 次的子数组【中等】

2023每日刷题&#xff08;五十六&#xff09; Leetcode—2962.统计最大元素出现至少 K 次的子数组 滑动窗口算法思想 参考的灵神思路 实现代码 class Solution { public:long long countSubarrays(vector<int>& nums, int k) {int n nums.size();long long ans…

文心一言 VS 讯飞星火 VS chatgpt (154)-- 算法导论12.3 1题

一、用go语言&#xff0c;给出 TREE-INSERT 过程的一个递归版本。 文心一言&#xff0c;代码不能运行&#xff1a; 下面是用 Go 语言实现一个递归版本的 TREE-INSERT 过程的代码&#xff1a; package main import "fmt" type TreeNode struct { Val int Lef…

JUC并发编程03——LockSupport与线程中断

一.线程中断机制 假设从网络下载一个100M的文件&#xff0c;如果网速很慢&#xff0c;用户等得不耐烦&#xff0c;就可能在下载过程中点“取消”&#xff0c;这时&#xff0c;程序就需要中断下载线程的执行。 1.1如何停止中断运行中的线程&#xff1f; 通过一个volatile变量…

Android--Jetpack--Databinding源码解析

慢品人间烟火色&#xff0c;闲观万事岁月长 一&#xff0c;基本使用 关于databinding的基本使用请看之前的文章 Android--Jetpack--Databinding详解-CSDN博客 二&#xff0c;xml布局解析 分析源码呢&#xff0c;主要就是从两方面入手&#xff0c;一个是使用&#xff0c;一个…

D28|买卖股票的最佳时机+跳跃游戏

122.买卖股票的最佳时机 II 初始思路&#xff1a; 这道题解题的时候比较像在找规律&#xff0c;发现只要计算这个过程中的两数之差然后相加即可。 题解复盘&#xff1a; 可以更加清晰的发现如何从题意中获得贪心的思路。 如何贪心&#xff1a;局部最优&#xff1a;收集每天的…

mybatis多表映射-延迟加载,延迟加载的前提条件是:分步查询

1、建库建表 create database mybatis-example; use mybatis-example; create table t_book (bid varchar(20) primary key,bname varchar(20),stuid varchar(20) ); insert into t_book values(b001,Java,s001); insert into t_book values(b002,Python,s002); insert into …

2024美赛备战2--模型建立(*****必看****)

建模 美赛涉及的建模知识范围非常广且深&#xff0c;纵观美赛真题不难发现&#xff0c;很多的模型 都是读研或者读博的时候才会真正深入开始研究&#xff0c;因此&#xff0c;对于做建模的同学来说&#xff0c; 是无法在赛前吃透大量模型的。推荐本科生分两个步骤去有效准备比赛…

SpringBootWeb请求响应之前言及状态码的详细解析

SpringBootWeb请求响应 前言 在上一次的课程中&#xff0c;我们开发了springbootweb的入门程序。 基于SpringBoot的方式开发一个web应用&#xff0c;浏览器发起请求 /hello 后 &#xff0c;给浏览器返回字符串 “Hello World ~”。 其实呢&#xff0c;是我们在浏览器发起请求…

外包干了3个月,技术退步明显。。。

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

搜集怎么绘制三维曲线和曲面?

1、针对函数对象是单一变量、两个函数的情况。用plot3函数&#xff1b;&#xff08;三维曲线&#xff09; 看一下matlab官方的例子&#xff1a; t 0:pi/50:10*pi; st sin(t); ct cos(t); plot3(st,ct,t) 绘制出来的曲线&#xff1a; 几个比较关键的点&#xff1a; &…