分享

与LSGO一起学“14 数组(14.26 堆中对象数组)”

 老马的程序人生 2020-08-17

14.26 堆中对象数组

上一节的程序用10000个指针来记录10000个对象的地址,解决了对象丢失的问题,但是把10000个指针存放在栈中,也是一种资源浪费,栈的空间很小,容不得这样的浪费。那么如何解决这个问题呢?

我们可以利用对象数组来解决这个问题,声明一个由10000个对象组成的数组,然后用一个指针来指向该数组。这样用一个指针就可以操作10000个对象了。

我们直接将一个由10000个对象组成的数组放到堆里,用一个指针来指向数组,那么当程序结束的时候,只需要删除这个数组就可以了。

程序演示:

#include<iostream>

#include"Area.h" 

using namespace std;

int main()

{

    const int n = 10000;

    Area* const pone = new Area[n];

    for (int i = 0;i < n;i++)

    {

       pone[i].set(i, i * 2);

       cout <<"Area["<<i <<"]:";

       cout <<pone[i].getArea() <<endl;

    }

    delete[] pone;

    return 0;

}

Area* const pone = new Area[100000];

该语句声明了一个指针pone,同时初始化了指针pone的值,让它指向由10000个Area对象组成的数组。这个数组保存在堆中。

由于new Area[100000]返回的是数组的地址,也就是pone[0]的地址。因此指针pone保存的就是pone[0]的地址,也就是该数组第一个对象的地址。

我们知道一个对象在堆中所占用的内存是根据它所拥有的变量来定义的,假如一个对象拥有4个占4字节的整型变量,那么该对象则占用16个字节的内存空间,同理,假如一个数组中拥有20个这样的对象,那么该对象数组则占用20乘以16等于320个字节的内存空间。

指针在内存中的访问也是根据这个原理,pone保存的是数组中第一个对象的地址,每个Area对象拥有2个int成员,每个int成员占4个字节,这样一个对象就占用8个字节。

pone+1后指向第二个对象,因此第二个对象的地址为数组首地址开始的第8个字节,而第三个对象的地址为数组首地址开始的第16个字节,其他以此类推。

当我们要访问数组元素的时候,可以:

   Area* const pone = new Area[n];

   *pone = Area(10, 10);

   *(pone + 1) = Area(10, 20);

   *(pone + 2) = Area(10, 30);

这样给数组第一个元素赋值为Area(10, 10),第二个元素赋值为Area(10, 20),第三个元素赋值为Area(10, 30)。但是通常我们都不这样用,而是用数组的下标[]去访问数组元素。这样:

   Area* const pone = new Area[n];

   pone[0] = Area(10, 10);

   pone[1] = Area(10, 20);

   pone[2] = Area(10, 30);

这样看起来很直观很方便,读代码时我们一眼就可以看出这是一个数组。

注意,万万不可这样:

   pone++;

   *pone = Area(10, 10);

这样看去上,pone指针自加后pone指针指向了数组的第二个元素,然后对第二个元素赋值为Area(10, 10),编译没有错,也能成功赋值。但要注意!我们在堆中创建的东西用完后需要用delete来释放,对于堆中数组,这样来释放:

delete[] pone;

由于刚才我们对pone进行了自加,这里用delete释放的时候就会出错,因为pone自加后就没有指向数组第一个元素的地址了,系统无法正确的释放我们在堆中创建的数组所占的内存空间。就会出现程序崩溃。所以千万不要改变pone指针。

delete[] pone;

[]表示我们删除的是一个数组,如果忘记输入[],那么你删除的只是pone[0]。该错误带来的后果就是内存泄漏,因为其余对象你都没有机会删除了。

为了防止对pone指针的误操作,最好的办法是将它声明为常量指针,我们在一开始堆中创建数组的时候就这样:

   Area* const pone = new Area[n];

将pone声明为常量指针后,我们就无法改变pone指针了,比如无法pone++,但是我们仍然可以正常的访问和修改pone指向的数组元素的值,pone[0]= Area(10, 10);

本节的程序形象地说明了对象数组的作用,与上节的程序相比,本节的程序对资源的浪费更少,因为只是在栈中保存了一个指针,而不是10000个。

【注意】

Area one[10000];

Area *one[10000];

Area *one = new Area[10000];

第1种方式是声明了一个由10000个Area对象组成的数组,数组名叫one。

第2种方式是声明了一个由10000个指向Area的指针组成的数组,数组名为one。

第3种方式是声明了一个指针one,该指针指向由10000个Area对象组成的一个数组。

在这里要注意的是,第3种方式的one是一个指向数组的指针,也就是说one保存的是数组第1个元素的地址。


与LSGO一起学系列图文之 C++

第1章 初识C++

   1.1 C++简介

   1.2 C++的发展过程

   1.3 C++与C有什么不同

   1.4 学习C++之前需要先学C吗?

   1.5 C++与其他语言的区别

   1.6 Visual Studio 2010编译器

第2章 做一个简短的C++程序

   2.1 简单的屏幕输出小程序

   2.2 输出语句的使用

   2.3 std::介绍

   2.4 iostream与iostream.h的区别

   2.5 重名问题

   2.6 注释

   与LSGO一起学“C++上机小练习01”

第3章 初步了解函数

   3.1 一个简单的函数

   3.2 函数的参数

   3.3 函数的返回值、参数与变量

   3.4 函数的声明与定义

   3.5 局部变量

   3.6 全局变量

   与LSGO一起学“C++上机小练习02”

第4章 C++数据类型

   4.1 C++数据类型引入

   4.2 什么是变量

   4.3 变量及数据如何存储在内存上

   4.4 布尔型

   4.5 字符型

   4.6 双字节型

   4.7 整型概述

   4.8 为什么使用补码

   4.9 整型变量的定义

   4.10 浮点型变量

   4.11 常量

   4.12 枚举型常量

   与LSGO一起学“C++上机小练习03”

第5章 if语句与逻辑运算符

   5.1 什么是语句

   5.2 什么是块

   5.3 什么是表达式

   5.4 什么是运算符

   5.5 赋值运算符与数学运算符的联合

   5.6 什么是自加与自减

   5.7 表达式的优先级

   5.8 关系运算符

   5.9 if语句

   5.10 else语句

   5.11 else if语句

   5.12 if语句的嵌套

   5.13 带括号的嵌套语句

   5.14 逻辑“与”运算符

   5.15 逻辑“或”运算符

   5.16 逻辑“非”运算符

   5.17 逻辑运算符的优先级

   5.18 运算式的真假关系

   5.19 三目运算符

   5.20 三目运算符的优先问题

   5.21 三目运算符的使用问题

   5.22 三目运算符的型别问题

   5.23 三目运算符在字符型变量中的使用

   5.24 复杂的嵌套if语句

   5.25 逗号运算符

   与LSGO一起学“C++上机小练习04”

   与LSGO一起学“C++上机小练习05”

第6章 面向对象

   6.1 什么是面向对象程序语言

   6.2 面向对象程序语言的主要特征

   6.3 什么是类,对象和成员

   6.4 声明一个类

   6.5 命名习惯

   6.6 定义一个对象

   6.7 定义类或对象容易犯的错误

   6.8 共有与私有

   6.9 成员函数的声明和定义

   6.10 内联函数

   6.11 头文件与源文件

   6.12 const成员函数

   6.13 构造函数

   6.14 默认构造函数

   6.15 析构函数

   6.16 析构对象数组

   与LSGO一起学“C++上机小练习06”

   与LSGO一起学“C++上机小练习07”

   与LSGO一起学“C++上机小练习08”

第7章 循环语句

   7.1 循环语句的老祖宗——goto语句

   7.2 while语句

   7.3 while语句的其他用法

   7.4 continue和break语句

   7.5 永不休止的while循环

   7.6 do……while循环

   7.7 for循环

   7.8 灵活的for循环

   7.9 条件为空的for循环

   7.10 嵌套的for循环

   7.11 switch语句

   7.12 switch语句常见错误

   7.13 switch的菜单功能

   与LSGO一起学“C++上机小练习09”

   与LSGO一起学“C++上机小练习10”

第8章 指针

   8.1 什么是地址

   8.2 用指针来保存地址

   8.3 空指针

   8.4 指针与变量类型

   8.5 用指针来访问值

   8.6 容易混淆的概念

   8.7 指针对数值的操作

   8.8 更换指针保存的地址

   8.9 为什么使用指针

   8.10 内存泄漏

   8.11 在堆中创建对象

   8.12 在堆中删除对象

   8.13 访问堆中的数据成员

   8.14 在构造函数中开辟内存空间

   8.15 对象在栈和堆中的不同

   8.16 this指针

   8.17 指针的常见错误

   8.18 指针的运算

   8.19 常量指针

   8.20 对指针的认知

   与LSGO一起学“C++上机小练习11”

第9章 引用

   9.1 什么是引用

   9.2 引用的地址

   9.3 引用就是别名常量

   9.4 引用对象

   9.5 空引用

   9.6 按值传递

   9.7 按指针(地址)传递

   9.8 按别名传递

   9.9 让函数返回多个值

   9.10 用值来传递对象

   9.11 用指针来传递对象

   9.12 用const指针来传递对象

   9.13 用引用来传递对象

   9.14 到底是使用引用还是使用指针

   9.15 引用和指针可以一块用

   9.16 引用容易犯的错误

   9.17 引用一个按值返回的堆对象

   9.18 引用一个按别名返回的堆对象

   9.19 在哪里创建就在哪里释放

第10章 深入函数

第11章 运算符重载

第12章 继承

第13章 虚函数

第14章 数组

   14.1 什么是数组

   14.2 数组元素

   14.3 数组下标越界

   14.4 倒序输出

   14.5 将数组的下标定义为常量以便于修改

   14.6 手动操作数组元素

   14.7 数组的初始化

   14.8 求平均考试成绩

   14.9 兔子繁殖问题

   14.10 数字排序问题

   14.11 数组在内存中的分布

   14.12 数组名

   14.13 数组名与函数

   14.14 传递与接收

   14.15 将数组传递给函数

   14.16 求数组所有元素的和

   14.17 用递增法查找数据

   14.18 用二分法查找数据

   14.19 判断数组是否按照顺序排列

   14.20 判断数组排列方式然后执行不同的函数

   14.21 数组在对象中的传参

   14.22 对象数组

   14.23 在对象数组中初始化成员变量

   14.24 复习:堆对象

   14.25 指针数组

   14.26 堆中对象数组

   14.27 枚举常量与数组

   14.28 多维数组

   14.29 多维数组初始化

   14.30 定义字符数组

   14.31 二维数组输出图形

   14.32 字符串数组

   14.33 字符串的输入问题

   14.34 strcat函数

   14.35 strcpy函数

   14.36 strcmp函数

   14.37 strupr函数

   14.38 strlwr函数

   14.39 strlen函数

   14.40 打印杨辉三角形

第15章 链表

第16章 多态

第17章 类的特殊成员

第18章 字符串

第19章 代码重用

第20章 友元类与嵌套类

第21章 流

第22章 命名空间

第23章 模板

第24章 异常和错误处理

第25章 补充内容

   25.6 位运算

C++学习补充资料

   C++中inline函数

   C++中类的inline成员函数

   C++中混合运算的类型转换

   C++上机小练习01参考代码

   C++上机小练习02参考代码

   C++上机小练习03参考代码

   C++上机小练习04参考代码

   C++上机小练习05参考代码

   C++上机小练习06参考代码

   C++上机小练习07参考代码

   C++上机小练习08参考代码

   C++上机小练习09参考代码

   C++上机小练习10参考代码

   C++上机小练习11参考代码


华北电力大学LSGO软件技术团队成立于2010年09月25日,团队主要以机器学习地理信息系统为主要研究方向,成立几年来为学校培养了大量优秀人才,他们或者就职于IBM、阿里巴巴、网易游戏、百度等IT企业,或者就读于中科院信安所、中科院计算所、中科院自动化所、中国科技大学、北京理工大学、武汉大学、华南理工大学、哈尔滨工业大学、华北电力大学等著名高校。

今年(2016年07月)毕业的李文乔同学保送到北京理工大学,安晟同学继续在华北电力大学读研究生,期间华硕公司,小米公司也希望团队推荐学生就业,综上,来LSGO软件技术团队学习可作为驻保高校学生,打发课余时间的一个不错选择。

如果对我们感兴趣,可以与我联系,通过考核后即可成为我们的一员。

请阅读以下代码(C#),得到联系方式:

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多