【带头学C++】----- 九、类和对象 ---- 9.8 动态对象创建

目录

9.8 动态对象创建

9.8.1 动态创建对象基础概念

9.8.2 C语言创建动态对象的

9.8.3 new创建动态对象

9.8.4 delete释放动态对象

9.8.5 动态对象数组


9.8 动态对象创建

9.8.1 动态创建对象基础概念

       在创建数组时,我们通常需要预先指定数组的长度,然后编译器会为数组分配相应大小的空间。然而,在使用数组时会出现一些问题。一方面,可能会造成空间的浪费,因为数组的空间可能会过大。另一方面,可能会出现空间不足的情况。因此,对于数组来说,

     如果能够根据需要动态地分配空间大小就更好了......思考ing

        动态的意思表示了空间分配的不确定性。为了解决这个普遍的编程问题,最基本的要求之一就是可以在运行时创建和销毁对象。虽然 C++ 提供了动态内存分配的函数(如 malloc 和 free),可以在运行时从堆中分配存储单元,但这些函数在 C++ 中并不能很好地完成对象的初始化工作。

//从堆区栈区可以自己申请到空间int n = 101;int *p = new int;

对象可以这样吗?

接着往下看......

9.8.2 C语言创建动态对象的

首先不管是C语言还是C++,他们都是为了对象存储而申请内存空间的。但是方法不一样

1、为对象分配内存,申请空间。
2、调用构造函数来初始化那块内存,第一步我们能保证实现,需要我们确保第二步一定能发生。C++强迫我们这么做是因为使用末初始化的对象是程序出错的一个重要原因。C语言中动态分配内存方法为了在运行时动态分配内存,C在他的标准库中提供了一些函数,malloc以及calloc和realloc,释放内存的free,这些函数是有效的、但是原始的,需要程序员理解和小心使用。为了使用c的动态内存分配函数在堆上创建一个类的实例,我们需要按照下面这样做:

出现的问题:

1) 程序员必须确定对象的长度。
2) malloc返回一个void指针,c++不允许将void赋值给其他任何指针,必须强转。
3) malloc可能申请内存失败,所以必须判断返回值是否为null来确保内存分配成功。
4)用户在使用对象之前必须记住对他初始化,构造函数不能显示调用初始化(构造函数是由编译器调用),用户有可能忘记调用初始化函数。

C的动态内存分配函数太复杂,容易令人混淆,是不可接受的,c++中我们推荐使用运算符new 和delete。

9.8.3 new创建动态对象

        C++中解决动态内存分配的方案是把创建一个对象所需要的操作都结合在一个称为new的运算符里。可以使用C++中的new运算符来创建动态对象。new运算符会自动为对象分配内存并调用对象的构造函数进行初始化

使用new创建动态对象的语法如下:

Class* objectPtr = new Class(arguments);

其中,Class是要创建的对象的类名,arguments是传递给构造函数的参数。

例如,创建一个Person类的动态对象可以这样写:

Person* personPtr = new Person("John", 25);

        这将在堆上动态分配内存,创建一个Person对象,并调用Person类的构造函数进行初始化。返回的personPtr是一个指向动态对象的指针。
        New操作符能确定在调用构造函数初始化之前内存分配是成功的,所有不用显式确定调用是否成功。现在我们发现在堆里创建对象的讨程变得简单了,只需要一个简单的表达式,它带有内置的长度计算、类型转换和安全检查。这样在堆创建一个对象和在栈里创建对象一样简单。

9.8.4 delete释放动态对象

        delete是new关键字对应的释放内存的关键字,new和delete一般成对出现,new申请的堆区内存,需要手动释放,只需要程序员使用delete 加上需要释放的对象的名称就可以实现手动回收内存。使用delete以后,会调用构造函数对应的析构函数,然后进行内存空间的释放。

s        记得在不需要使用动态对象时,使用delete来释放动态分配的内存,以防止内存泄漏。

delete personPtr;

        以上是使用newdelete手动进行内存管理的传统方法。然而,现代C++提供了更安全和方便的智能指针(如std::shared_ptrstd::unique_ptr)来管理动态对象的生命周期,推荐使用它们来避免手动释放内存的繁琐和潜在错误。

代码:

class Person2{
public:Person2(){cout << "无参构造函数!"<<endl;pName = new char[strlen("undefined")+1];strcpy_s(pName,strlen("undefined")+1,"undefined");mAge = 0;}Person2(const char* name,int age){cout << "有参构造函数!"<<endl;pName = new char[strlen(name)+1];strcpy_s(pName,strlen(name)+1,name);mAge = age;}void showPerson(){cout <<"Name:"<< pName << " Age:" << mAge << endl;}~Person2(){cout <<"析构函数!"<<endl;if (pName != nullptr){delete [] pName;  //值得注意一个地方,释放const char *类型,需要带[]pName = nullptr;}}
public:int mAge;char *pName;
};
void test03(){Person2* person1 = new Person2;Person2* person2 = new Person2("ohn",33);person1->showPerson();person2->showPerson();delete person1;delete person2;
}

9.8.5 动态对象数组

         在创建一个对象数组时,需要为数组中的每个对象调用构造函数进行初始化。这是因为对象数组需要为每个元素分配内存,并执行构造函数来初始化对象的状态。

        在栈上创建对象数组时,你可以使用聚合初始化来初始化数组元素的值。这样做不会调用默认构造函数,而是直接为数组元素设置指定的初始值。

        对于堆上创建的对象数组,如果对象没有提供默认构造函数,那么你必须提供一个可以将对象初始化的构造函数。否则,无法正确地初始化数组元素。

        需要注意的是,如果对象数组中的对象是具有非平凡析构函数的类类型对象,那么你需要手动释放内存,以避免内存泄漏。

拓展:无需记忆,了解一下

       聚合初始化是一种在创建对象时,使用大括号 {} 或等号 = 进行初始化的方式。它可用于初始化聚合类型的成员变量或数组元素。聚合类型包括数组、结构体和类(满足特定条件的类)。

#include <iostream>struct Point {int x;int y;
};int main() {// 聚合初始化结构体对象Point p1 = {2, 3};std::cout << "p1.x = " << p1.x << ", p1.y = " << p1.y << std::endl;// 聚合初始化数组元素Point points[] = {{1, 2}, {3, 4}, {5, 6}};std::cout << "points[0].x = " << points[0].x << ", points[0].y = " << points[0].y << std::endl;std::cout << "points[1].x = " << points[1].x << ", points[1].y = " << points[1].y << std::endl;std::cout << "points[2].x = " << points[2].x << ", points[2].y = " << points[2].y << std::endl;return 0;
}

        在上述示例中,我们定义了一个结构体 Point,具有两个整数成员变量 x 和 y。我们使用聚合初始化方式,分别在 p1 和 points 中初始化了结构体对象和数组元素。在输出中可以看到这些对象被正确地初始化了。

        通过使用聚合初始化,我们可以在创建对象时直接为其成员变量指定一个初始值,个数和顺序需要与对象的定义一致。这种初始化方式非常方便,尤其是在初始化较大的数组时可以提高代码的可读性和简洁性。

        当一个类的析构函数不是使用默认实现时,我们称其为非平凡析构函数。一个非平凡析构函数可能会执行一些特殊的清理操作,如释放资源、关闭文件或释放动态分配的内存。

示例:

#include <iostream>class Resource {
public:Resource() {std::cout << "Resource acquired." << std::endl;}~Resource() {// 假设这里有一个复杂的清理逻辑std::cout << "Resource released." << std::endl;}
};int main() {Resource* resources = new Resource[5];delete[] resources;return 0;
}

        在上述示例中,我们定义了一个名为 Resource 的类,它具有一个非平凡的析构函数。在 main 函数中,我们使用 new 操作符在堆上动态分配了一个包含 5 个 Resource 对象的数组。当我们使用 delete[] 删除数组时,会自动调用每个 Resource 对象的析构函数进行清理操作。

        通过运行这段代码,你将看到每个 Resource 对象在释放时输出 "Resource released." 的消息。这证明了非平凡析构函数在析构对象时执行了一些特殊的操作。

接着回归正题,除了在栈上可以聚合初始化,必须提供一个默认的构造函数;

 代码:

class Person3{
public:Person3(){cout << "无参构造函数!"<<endl;pName = nullptr;mAge = 0;}Person3(const char* name,int age){cout << "有参构造函数!"<<endl;pName = new char[strlen(name)+1];strcpy_s(pName,strlen(name)+1,name);mAge = age;}~Person3(){cout <<"析构函数!"<<endl;if (pName != nullptr){delete [] pName;pName = nullptr;}}
public:int mAge;char *pName;
};
void test04(){//栈聚合初始化,实际就是批量好几个对象的初始化Person3 person[] = {Person3("Jery",18),Person3("Tom",19)};cout << person[1].pName<<endl;//创建堆上对象数组必须提供构造函数Person3* worker = new Person3[10];
//此时会触发10个构造,对象数组。数组每个元素为Person3类型的对象delete [] worker;
//注意:new和delete成对出现,一个申请初始化构造,一个析构释放空间
}

注:代码的注释部分内容,也需要仔细阅读,不可大意。看到这里,点个赞赞吧,收藏、关注看下一章。谢谢各位大佬支持,加油!!!!!

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

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

相关文章

Unity中的ShaderToy

文章目录 前言一、ShaderToy网站二、ShaderToy基本框架1、我们可以在ShaderToy网站中&#xff0c;这样看用到的GLSL文档2、void mainImage 是我们的程序入口&#xff0c;类似于片断着色器3、fragColor作为输出变量&#xff0c;为屏幕每一像素的颜色&#xff0c;alpha一般赋值为…

最简单的pixel刷机和安装面具、lsposed

一 下载手机对应的系统 1&#xff0c;手机usb连接然后重启进入Fastboot模式&#xff1a;adb reboot bootloader2&#xff0c;找到你下载的系统&#xff0c;Windows 系统 直接运行 flash-all.bat上图 &#xff1a;左边就是安卓11和12的系统&#xff0c;右边是对应的手机型号 下…

Java Web开发中的Servlet

目录 1. Servlet 的作用和优势 2. Servlet 的工作原理 3. Servlet 的生命周期 4. Servlet 在现代 Web 开发中的重要性 结论 当谈及 Java Web 开发&#xff0c;Servlets 是不可或缺的组件之一。Servlet 是在服务器端处理 HTTP 请求和响应的 Java 类。它们允许开发人员编写动…

牛客网(二叉树)

https://www.nowcoder.com/practice/4b91205483694f449f94c179883c1fef?tpId60&&tqId29483&rp1&ru/activity/oj&qru/ta/tsing-kaoyan/question-ranking 这个题目和leetcode比起来就是有一些不一样&#xff0c;需要我们自己来写接口函数&#xff0c;所以…

Nacos配置管理-微服务配置拉取

yaml已配置内容 目录 一、配置获取步骤 二、统一配置管理步骤 三、Nacos管理配置的步骤总结 一、配置获取步骤 二、统一配置管理步骤 1、引入Nacos的配置管理客户端依赖: <!--nacos配置管理依赖--> <dependency> <groupId>com.alibaba.cloud&l…

python+gdal地理坐标转投影坐标

1 前言 地理坐标系&#xff0c;是使用三维球面来定义地球表面位置&#xff0c;以实现通过经纬度对地球表面点位引用的坐标系。 地理坐标系经过地图投影操作后就变成了投影坐标系。而地图投影是按照一定的数学法则将地球椭球面上点的经维度坐标转换到平面上的直角坐标。 2 流程…

springboot+ssm+java植物养护花卉花圃管理系统

花圃管理系统&#xff0c;主要的模块包括查看个人中心、游客管理、员工管理、植物种类管理、植物信息管理、植物绿化管理、花圃园区管理、商品服务管理、系统管理等功能。系统中管理员主要是为了安全有效地存储和管理各类信息&#xff0c;还可以对系统进行管理与更新维护等操作…

【JavaWeb笔记】单选框,结合Servlet

各个部分的作用 jsp部分 form action"..."&#xff1a;表单标签&#xff0c;供用户提交数据。内部的submit点击之后相当于是点action的URL input type"radio"&#xff1a;输入类型为单选框。把name设置为一样的&#xff0c;这样效果上就是单选&#xff…

巧用ChatGPT高效搞定Excel数据分析【文末送书-04】

文章目录 一.巧用ChatGPT高效搞定Excel数据分析1. ChatGPT简介2. 安装所需工具2.1 Python2.2 OpenAI GPT库 3. 与ChatGPT交互进行数据分析4. 利用ChatGPT进行筛选和排序5. ChatGPT的局限性和注意事项6. ChatGPT与数据可视化7. ChatGPT与进阶数据分析任务 二. 结论&文末福利…

米软单病种质量管理智能上报系统之基础资源管理

米软单病种质量管理智能上报系统 面市以来&#xff0c;便在以下各方各面获得一致好评&#xff0c;包括&#xff1a;病例匹配抓取、数据处理效率、填报耗时、用户体验、客户评价等。 这些亮眼的成果&#xff0c;源于米软人持续不懈地对基础数据进行了严谨、细致、反复验证的处理…

047:vue加载循环倒计时 示例

第047个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

格雷希尔V系列自封阀公母头配合快速接头完成流水线式测试的使用方法

在工业生产线上&#xff0c;有些产品在进行气密性测试时需要快速密封连接器跟随着流水线一起移动&#xff0c;此时连接器上的气管就成了一个问题&#xff0c;由于气管是固定在测试设备上的&#xff0c;不能随着产品线的流动而移动&#xff0c;因此将会随着产品的移动而受到干扰…