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

news2025/1/18 8:58:49

目录

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.coloradmin.cn/o/1293840.html

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

相关文章

二分查找|差分数组|LeetCode2251:花期内花的数目

作者推荐 贪心算法LeetCode2071:你可以安排的最多任务数目 本文涉及的基础知识点 二分查找算法合集 题目 给你一个下标从 0 开始的二维整数数组 flowers &#xff0c;其中 flowers[i] [starti, endi] 表示第 i 朵花的 花期 从 starti 到 endi &#xff08;都 包含&#x…

低代码与MES:智能制造的新篇章

一、引言 随着工业4.0和智能制造的兴起&#xff0c;企业对于生产过程的数字化、智能化需求日益迫切。制造执行系统&#xff08;MES&#xff09;作为连接计划层与控制层的关键信息系统&#xff0c;在提升生产效率、优化资源配置、保障产品质量等方面发挥着重要作用。然而&#…

Linux Docker 安装Nginx

1.21、查看可用的Nginx版本 访问Nginx镜像库地址&#xff1a;https://hub.docker.com/_/nginx 2、拉取指定版本的Nginx镜像 docker pull nginx:latest #安装最新版 docker pull nginx:1.25.3 #安装指定版本的Nginx 3、查看本地镜像 docker images 4、根据镜像创建并运行…

创投课程研报专题课 | 如何写出高质量研报

协会邀请了来自GPTDAO的分析师——Will作为VC创投课程研报专题课的嘉宾&#xff0c;将于北京时间12月2日(周六)晚上21:00 PM-22:00 PM&#xff0c;与所有对Web3投资、创业心怀热忱的朋友一同探讨《如何写出高质量的研报》这个激动人心的话题。 浙江大学学生区块链协会&#xff…

连号区间数

/* 两重循环,如果是连号区间,那么区间长度必然 最大值 - 最小值 第二层循环的时候可以更新每个区间的最大值,最小值。中间的元素不需要考虑 *//* 暴力做法:在第二层循环时,每次多创建一个数组存放i~j之间的数,对其排序Arrays.sort(data,i,j1) 然后再多一层循环判断d[k] 1 d[k…

集合的几个遍历方法

1. 集合的遍历 1.0 创建集合代码 List<String> strList new ArrayList<>(); strList.add("huawei"); strList.add("xiaomi"); strList.add("tencent"); strList.add("google"); strList.add("baidu");1.1 fo…

css 十字分割线(含四等分布局)

核心技术 伪类选择器含义li:nth-child(2)第2个 lili:nth-child(n)所有的lili:nth-child(2n)所有的第偶数个 lili:nth-child(2n1)所有的第奇数个 lili:nth-child(-n5)前5个 lili:nth-last-child(-n5)最后5个 lili:nth-child(7n)选中7的倍数 border-right: 3px solid white;borde…

概率测度理论方法(第 1 部分)

一、说明 概率的应用范围广泛到经济学、量子力学、生物学甚至政治学&#xff0c;可以说是数学最重要的分支之一。然而&#xff0c;普遍教授和广泛接受的概率版本错过了一些令人难以置信的令人满意的直觉。在本文中&#xff0c;我们将利用这种直觉。为了做到这一点&#xff0c;我…

windows MYSQL解决中文乱码问题

1.首先确保你已经把mysql配置了环境变量 2.打开window终端 3.输入mysql -u root -p 4.输入密码&#xff0c;就是安装的时候设置的root超级管理员权限密码 5.输入&#xff1a; SHOW VARIABLES LIKE ‘character%’; 出现上图&#xff0c;说明就会出现中文乱码问题。 6.该怎么办…

Holynix

信息收集阶段 存活主机探测&#xff1a;arp-scan -l 当然了&#xff0c;正常来说我们不应该使用arp进行探测&#xff0c;arp探测的是arp的缓存表&#xff0c;我们应该利用nmap进行探测&#xff01; nmap -sT --min-rate 10000 192.168.182.0/24 端口探测 nmap -sT --min-rat…

智能优化算法应用:基于天鹰算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于天鹰算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于天鹰算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.天鹰算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

【web安全】文件包含漏洞详细整理

前言 菜某的笔记总结&#xff0c;如有错误请指正。 本文用的是PHP语言作为案例 文件包含漏洞的概念 开发者使用include&#xff08;&#xff09;等函数&#xff0c;可以把别的文件中的代码引入当前文件中执行&#xff0c;而又没有对用户输入的内容进行充分的过滤&#xff0…

视频封面提取:精准截图,如何从指定时长中提取某一帧图片

在视频制作和分享过程中&#xff0c;一个有吸引力的封面或截图往往能吸引更多的观众点击观看。有时候要在特定的时间段内从视频中提取一帧作为封面或截图。如果每个视频都手动提取的话就会耗费很长时间&#xff0c;那么如何智化能批量提取呢&#xff1f;现在一起来看下云炫AI智…

AI人工智能和大模型的总结概述之一

GPT引领了AIGC时代的到来&#xff0c;即AI生成内容&#xff08;文本、图片、音频、视频&#xff09; GPT&#xff0c;GPT是一种生成式的、预训练的大模型&#xff0c;属于深度学习&#xff1a; G&#xff1a;Generative 生成式 GPT能够通过深度学习算法对已有数据库进行学习&…

工作中常用的RabbitMQ实践

目录 1.前置 2.导入依赖 3.生产者 4.消费者 5.验证 验证Direct 验证Fanout 验证Topic 1.前置 安装了rabbitmq&#xff0c;并成功启动 2.导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-…

Vulnerability: File Upload(low)--MYSQL注入

选择难度&#xff1a; 1.打开DVWA&#xff0c;并登录账户 2.选择模式&#xff0c;这里我们选择 文件上载的最低级模式&#xff08;low&#xff09; 在vsc里面写个一句话木马 这里我们注意&#xff0c;因为这个是木马很容易被查杀&#xff0c;从而无法使用&#xff0c;所以我们…

Docker安装postgres最新版

1. postgres数据库 PostgreSQL是一种开源的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它是一种高度可扩展的、可靠的、功能丰富的数据库系统。以下是关于PostgreSQL的一些介绍&#xff1a; 开源性&#xff1a;PostgreSQL是一个开源项目&#xff0c;可以…

这个sql有点东西,记录一下

我有一个需求&#xff1a;在订单表里面查询指定时间的订单数据&#xff0c;如果要是没有订单的话&#xff0c;需要展示当天日期和数据&#xff0c;数据为0 先看一下效果&#xff1a; 话不多说&#xff0c;直接上SQL SELECTdate_range.date AS 日期,COUNT( oco.id ) AS 总订单…

返回列表中满足指定条件的连续元素:只返回第一个不符合条件元素之前的各元素itertools.takewhile()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 返回列表中满足指定条件的连续元素&#xff1a; 只返回第一个不符合条件元素之前的各元素 itertools.takewhile() [太阳]选择题 请问以下代码输出的结果是&#xff1f; import itertools a …

spark sql基于RBO的优化

前言 这里只对RBO优化进行简单的讲解。讲解RBO之前必须对spark sql的执行计划做一个简单的介绍。 这个里讲解的不是很清楚&#xff0c;需要结合具体的执行计划来进行查看 1、执行计划 在spark sql的执行计划中&#xff0c;执行计划分为两大类&#xff0c;即逻辑执行计划、物…