嵌入式养成计划-37----C++内存动态分配与回收--C++类中特殊的成员函数

news2024/11/15 0:32:33

八十五、 C++内存动态分配与回收

  • C语言中的动态内存分配和回收是用malloc、free来完成的
  • C++中也可以用上述两个函数来完成。
  • C++中也为用户提供了两个关键字来进行动态内存分配和回收 new 、delete

85.1 分配

  • 单个内存分配
    格式: 数据类型 *指针名 = new 数据类型
    • 例如: int *p1 = new int;
      //此时表示在堆区中申请一个整型大小的空间,把地址赋给p
  • 连续内存分配
    格式:数据类型 * 指针名 = new 数据类型[个数]
    • 例如: int *p2 = new int[5];
      //在堆区中连续申请5个整型大小的空间

85.2 回收

  • 单个内存回收
    格式: delete 指针名
    • 例如: delete p1;
  • 连续内存回收
    格式:delete [ ]指针名
    • 例如 : delete [ ]p2;

85.3 new、delete 和malloc 、free之间的区别 (笔试面试题)

  • new delete 是关键字,malloc free是函数
  • new申请空间是以数据类型为单位,malloc申请空间是字节单位
  • new可以申请空间是初始化,malloc申请空间不会
  • new申请空间是什么类型指针就是什么类型,malloc返回值是void*,需要强制转换才能使用。
  • new 申请对象空间时,会自动调用构造函数,而malloc不会
  • delete释放对象空间时,会自动调用析构函数,而free不会

85.4 示例:

#include <iostream>
using namespace std;
int main()
{
    //在堆区空间中申请int大小空间
    int *p1 = new int;
    cout << *p1 << endl;

    *p1 = 1314;
    cout << *p1 << endl;

    //在堆区空间中申请int大小空间并初始化
    int *p2 = new int(10);
    cout << *p2 << endl;

    //在堆区空间中申请char大小空间并初始化
    char *p3 = new char('H');
    cout << *p3 << endl;
    
    //释放(回收)内存空间
    delete p1;
    delete p2;
    delete p3;
    cout << "----------------------" << endl;
    //在堆区空间中连续申请5个int大小空间
    int *p4 = new int[5];
    for(int i=0; i<5; i++)
    {
        cout << p4[i] << endl;
    }
    cout << "----------------------" << endl;
    //在堆区空间中连续申请5个int大小空间并初始化
    int *p5 = new int[5]{100,200,300,400,500};
    for(int i=0; i<5; i++)
    {
        cout << p5[i] << endl;
    }
    //释放(回收)连续内存
    delete []p4;
    delete []p5;

    //给指针来个指向 避免野指针
    p1 = nullptr;
    p2 = nullptr;
    p3 = nullptr;
    p4 = nullptr;
    p5 = nullptr;
    return 0;
}

八十六、 C++类中特殊的成员函数

  • 特殊函数种类: 构造函数析构函数拷贝构造函数拷贝赋值函数、移动赋值、移动拷贝、取地址运算符、常取地址运算符
  • 特殊原因:
    1. 这些函数无需程序员手动定义,系统会默认提供,如果程序员手动定义,那么系统就取消默认提供。
    2. 这些函数无需程序员手动调用,在特定的条件下,系统自动调用,即使是程序员手动定义的函数。

86.1 构造函数

86.1.1 功能

  • 在类实例化对象时,会自动调用构造函数来给类对象申请空间以及初始化

86.1.2 格式

  • 函数名:与类同名
  • 返回值:无 也无void
  • 参数:可以有参数,可以无参数
  • 权限:一般为public
类名(形参列表)
{
    函数体内容;
}

86.1.3 调用时机

  • 类实例化对象时,会自动调用构造函数。
  • 栈区
    • 何时实例化对象,何时调用构造函数
      形式 :类名 对象(实参);
  • 堆区
    • 何时使用new ,何时调用构造函数

示例:

#include <iostream>
using namespace std;
class Stu
{
private:
    string name;
    int id;
public:
    Stu()  //无参构造函数 由系统默认提供
    {
        cout << "Stu::无参构造函数" << endl;
    }
    Stu(string name, int id)
    {
        this->name = name;
        Stu::id = id;
        cout << "Stu::有参构造函数" << endl;
    }
    void show()
    {
        cout << "姓名:" << name << "   学号:" << id << endl;
    }
};
int main()
{
    Stu s1; //自动调用无参构造函数
    Stu s2("张三", 1001); //自动调用有参构造函数
    s2.show();
    return 0;
}

86.1.4 注意

  • 系统会提供一个默认无参构造函数。
    如果自己写了有参构造函数,会把系统提供的默认无参构造函数给屏蔽掉。
    如果想使用无参构造函数,则需要显性定义出无参构造函数,否则报错。
  • 构造函数中可以给定默认参数值。

86.1.5 初始化列表

  • 构造函数的本身功能是完成给对象申请空间的,初始化工作是由初始化列表完成。
  • 初始化列表格式:
    由构造函数形参列表后的小括号后面由冒号引出
    类名 (形参1,形参2,形参n):成员变量1(形参1),成员变量2(形参2),······,成员变量n(形参n)
    
  • 必须使用初始化列表的情况:
    1. 当类中有常成员变量时,对该变量的初始化必须使用初始化列表来完成。
    2. 当类中有引用变量时,对该变量的初始化必须使用初始化列表来完成。
    3. 当类中有其他类的子对象时,对该子对象的初始化必须使用初始化列表来完成。
  • 注意:只有构造函数才有初始化列表,其他普通函数没有初始化列表。

示例:

#include <iostream>
using namespace std;
class Bir
{
private:
    int year;
    int month;
    int day;
public:
    Bir(int y,int m, int d):year(y),month(m),day(d)
    {
        cout << "Bir::有参构造函数" << endl;
    }
};

class Stu
{
private:
    string name;
    int id;
    Bir bir;
public:
    //无参构造函数
	//Stu() {}

    //有参构造函数
    Stu(string name, int id, int y,int m, int d):name(name),id(id),bir(y,m,d)
    {
        cout << "Stu::有参构造函数" << endl;
    }
    void show(){cout << name;}
};
int main()
{
    Stu s1("zhangsan", 1001, 2002, 9, 9); //先调用子对象的构造函数,再调用自己的构造函数
    return 0;
}

86.2 析构函数

86.2.1 功能

  • 当类对象生命周期结束后,自动调用析构函数,来对类对象回收资源(释放空间)。

86.2.2 格式

  • 函数名:~类名
  • 返回值:无 无void
  • 参数:无参数
  • 权限:一般是public
~类名()
{
    函数体内容;
}

86.2.3 调用时机

  • 类对象生命周期结束后,会自动调用析构函数。
  • 栈区
    • 当类对象所在的函数结束时,自动调用析构函数
  • 堆区
    • 何时使用delete ,何时调用析构函数

示例 :

#include <iostream>
using namespace std;
class Stu
{
private:
    string name;
    int id;
public:
    //无参构造函数
    Stu() {cout << "Stu::无参构造函数" << endl;}
    //有参构造函数
    Stu(string name, int id):name(name),id(id)
    {
        cout << "Stu::有参构造函数" << endl;
    }
    //析构函数
    ~Stu()
    {
        cout << "Stu::析构函数" << endl;
        cout << this << endl;
    }
    void show()
    {
        cout << name << endl;
    }
};
int main()
{
    Stu s1; //自动调用无参构造
    Stu s2("zhangsan", 1001); //自动调用有参构造函数
    cout << "&s1 = " << &s1 << "     &s2 = " << &s2 << endl;
    //先构造的 后析构   后构造的 先析构
    return 0;
}

在这里插入图片描述
在这里插入图片描述

小结:

  1. 类中都系统默认提供析构函数,如果显性定义了析构函数,则系统取消默认提供。
  2. 当类中的成员变量有指针并且这个指针成员变量在堆区申请了空间,则此时需要显性定义析构函数,并且在析构函数中手动将指针成员所申请的空间释放,避免内存泄漏。
  3. 每个类中只有一个析构函数,原因:析构函数没有参数,所以不能重载。
#include <iostream>
using namespace std;
class Stu
{
private:
    string name;
    int id;
    double *score; //有指针成员变量时,并申请了堆区空间
public:
    //无参构造函数
    Stu() {cout << "Stu::无参构造函数" << endl;}
    //有参构造函数
    Stu(string name, int id, double b):name(name),id(id),score(new double(b))
    {
        //score = new double(b);
        cout << "Stu::有参构造函数" << endl;
    }
    //析构函数
    ~Stu()
    {
        cout << "Stu::析构函数" << endl;
        cout << this << endl;
        delete score;  //需要把析构函数显性定义出来,在其中把指针成员释放掉
    }
    void show(){cout << name << endl;}
};
int main()
{
    Stu s1; //自动调用无参构造
    Stu s2("zhangsan", 1001, 89); //自动调用有参构造函数
    cout << "&s1 = " << &s1 << "     &s2 = " << &s2 << endl;
    
    Stu *p = new Stu; //自动调用无参构造函数
    delete p; //自动调用析构函数
    return 0;
}

86.3 拷贝构造函数

86.3.1 功能

  • 拷贝构造函数其实就是一种特殊的构造函数,用一个类对象给另一个类对象初始化的。

86.3.2 格式

  • 函数名:与类同名
  • 返回值:无 无void
  • 参数: 同类的其他类对象
  • 权限:一般为public
类名(const 类名 &other)
{
    函数体内容
}

86.3.3 调用时机

  1. 用一个类对象给另一个类对象初始化时,自动调用拷贝构造函数
    eg:
    Stu s(s1);
    Stu s = s1;
  2. 当类对象作为函数的形参时, 实参传递给形参时,自动调用拷贝构造函数
  3. 当函数返回一个类对象时,自动调用拷贝构造函数

86.4 浅拷贝和深拷贝(笔试面试)

  1. 系统会提供一个默认的拷贝构造函数,如果自己显性定义出了拷贝构造函数,则系统会取消默认提供函数。
  2. 系统提供的拷贝构造函数,是把一个对象的所有数据成员初始化另一个对象的所有数据成员,叫浅拷贝
    在这里插入图片描述
    在这里插入图片描述

小作业:

设计一个Per类,
类中包含私有成员:
	姓名、年龄、指针成员身高、体重,

再设计一个Stu类,
类中包含私有成员:
	成绩、Per类对象p1,

设计这两个类的  构造函数、 析构函数  和  拷贝构造函数。

我写的:

class.h

#ifndef __CLASS_H__
#define __CLASS_H__

#include <iostream>
using namespace std;

class Pre
{
private:
    string name;
    int age;
    int *high;
    int *weight;

public:
    //构造函数
    Pre();
    Pre(string name, int age, int high, int weight);
    //析构函数
    ~Pre();
    //拷贝构造函数
    Pre(const Pre &other);
    //获取姓名
    string get_name();
};

class Stu
{
private:
    //构造函数
    int score;
    Pre p;
public:
    Stu();
    Stu(int score, Pre p);
    //析构函数
    ~Stu();
    //拷贝构造函数
    Stu(const Stu &other);
};

#endif // __CLASS_H__

pre.cpp

#include "class.h"

//构造函数
Pre::Pre(){
    cout << this->name << " 调用了无参构造函数" << endl;
}
Pre::Pre(string name, int age, int high, int weight){
    this->name = name;
    this->age = age;
    this->high = new int(high);
    this->weight = new int(weight);
    cout << this->name << " 调用了有参构造函数" << endl;
}
//析构函数
Pre::~Pre(){
    delete this->high;
    delete this->weight;
    cout << this->name << " 调用了析构函数" << endl;
}
//拷贝构造函数
Pre::Pre(const Pre &other){
    this->name = other.name;
    this->age = other.age;
    this->high = new int(*(other.high));
    this->weight = new int(*(other.weight));
    cout << this->name << " 调用了拷贝构造函数" << endl;
}
string Pre:: get_name(){
    return this->name;
}

stu.cpp

#include "class.h"

Stu::Stu(){
    cout << this->p.get_name() << " 调用了无参构造函数" << endl;
}
Stu::Stu(int score, Pre p){
    this->score = score;
    this->p = p;
    cout << this->p.get_name() << " 调用了有参构造函数" << endl;
}
//析构函数
Stu::~Stu(){
    cout << this->p.get_name() << " 调用了析构函数" << endl;
}
//拷贝构造函数
Stu::Stu(const Stu &other){
    this->score = other.score;
    this->p = other.p;
    cout << this->p.get_name() << " 调用了拷贝构造函数" << endl;
}

在这里插入图片描述

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

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

相关文章

uml图是什么?uml图中的各类箭头代表的是什么含义

一 UML图是什么&#xff1f; UML&#xff08;Unified Modeling Language&#xff09;是一种标准化的图形化建模语言&#xff0c;用于描述、分析、设计和表示软件系统的结构、行为、关系和交互。它提供了一组符号和规则&#xff0c;可以用于创建各种类型的图表&#xff0c;从…

大模型推理框架概述

从 ChatGPT 面世以来&#xff0c;引领了大模型时代的变革&#xff0c;除了大模型遍地开花以外&#xff0c;承载大模型进行推理的框架也是层出不穷&#xff0c;大有百家争鸣的态势。本文主要针对业界知名度较高的一些大模型推理框架进行相应的概述。 简介 vLLM是一个开源的大模…

序列化反射filter添加Neo-reGeorg内网代理

前言&#xff1a; 当被攻击服务器网络比较苛刻&#xff0c;可以选择通过filter添加Neo-reGeorg进行内网代理&#xff0c;这样做的好处首先通过反序列化漏洞&#xff0c;添加的filter在内存中&#xff0c;无文件落地可以防止杀软查杀。 基础&#xff1a; 首先我们先要了解如何…

Android 属性动画ValueAnimator整理

Android 属性动画有 ObjectAnimator 、AnimatorSet 、ValueAnimator 等。 ObjectAnimator 、AnimatorSet 参考前篇 Andorid 属性动画ObjectAnimation整理_南国樗里疾的博客-CSDN博客 本篇介绍 ValueAnimator &#xff0c;它本身不作用于任何对象&#xff0c;它是对值进行动画…

如何在小程序中设置导航栏文字颜色和背景颜色

不同商家有不同的颜色风格&#xff0c;例如有些做设计的公司&#xff0c;主要是黑色风格&#xff1b;有些卖珠宝的商家&#xff0c;主要是金色风格&#xff1b;他们的小程序&#xff0c;也需要进行同样的风格设定。下面具体介绍怎么在小程序中进行整个风格设定。 1. 在小程序管…

C# .net创建一个MVC框架工程

二、C# .net创建一个MVC框架工程 1.步骤 首先打开VS &#xff0c;然后点击创建新项目 在三个选项框中输入我们需要的项目条件 最后一步创建完毕 创建会在资源解决方案生成如图&#xff1a;

Spark 9:Spark 新特性

Spark 3.0 新特性 Adaptive Query Execution 自适应查询(SparkSQL) 由于缺乏或者不准确的数据统计信息(元数据)和对成本的错误估算(执行计划调度)导致生成的初始执行计划不理想&#xff0c;在Spark3.x版本提供Adaptive Query Execution自适应查询技术&#xff0c;通过在”运行…

小说推文和短剧推广的优势和申请授权方式

小说推文和短剧推广都可以通过”巨量推文“申请授权 先说说优势&#xff0c;短视频赛道有很多&#xff0c;普遍的门槛过高&#xff0c;目前小说推文和短剧推广只要你有短视频账号就可以推广 相对来说门槛更低&#xff0c;可以”白嫖“流量来进行变现

Spring核心源码-如何解决循环依赖

假设有两个类A和B B是A的成员变量&#xff0c;A也是B的成员变量。 假设类A的bean为a&#xff0c;类B的bean为b。且IOC容器先处理A。 熟悉Spring容器初始化的同学&#xff0c;应该都知道&#xff0c;容器初始化的过程中&#xff0c;bean的创建是如下触发的&#xff1a; getBean…

Unity可视化Shader工具ASE介绍——4、ASE的自定义模板使用

大家好&#xff0c;我是阿赵。   继续介绍Unity可视化Shader编辑工具ASE。之前的文章介绍了在ASE里面可以选择不同的Shader类型。这一篇来继续探讨一下&#xff0c;这些Shader类型究竟是什么。 一、所谓的Shader类型是什么 选择不同的Shader类型&#xff0c;会出现不同的选项…

Unity 之 EditorGUILayout.BeginHorizontal/EndHorizontal异常报错问题

报错内容&#xff1a; 缘由&#xff1a;由于在EditorGUILayout.EndHorizontal()之前执行了类似打开窗口的逻辑 解决办法&#xff1a; 在EditorGUILayout.EndHorizontal()之前执行GUIUtility.ExitGUI();

vulnhub_driftingblues7靶机渗透测试

Driftingblues7靶机 文章目录 Driftingblues7靶机信息收集web渗透获取权限另外思路靶机总结 信息收集 使用nmap扫描得到靶机ip为192.168.78.174&#xff0c;开放发端口有很多&#xff0c;而且开放了443端口&#xff0c;所以访问网站是需要https协议的 再对该网站进行目录扫描&…

10-09 周一 图解机器学习之深度学习感知机学习

10-09 周一 图解机器学习之深度学习感知机学习 时间版本修改人描述2023年10月9日14:13:20V0.1宋全恒新建文档 简介 感知机是神经网络中的概念&#xff0c;1958年被Frank Rosenblatt第一次引入。感知机作为一种基本的神经网络模型&#xff0c;它模拟了人脑神经元的工作原理。感…

解决Feign的自定义解码器在接口返回值为void时不执行的问题

项目的接口有一个全局的响应包装器&#xff0c;将接口的所有返回&#xff0c;包括各种类型如List、Entity&#xff0c;或者void&#xff0c;以及抛出的异常&#xff0c;封装成统一的结构给到前端&#xff0c;所以在使用Feign发起远程调用的时候&#xff0c;需要一个自定义的解码…

详解 ElasticSearch 基础教程

&#x1f339; 分享 ElasticSearch 基础&#xff0c;请指教。&#x1f339;&#x1f339; 如你对技术也感兴趣&#xff0c;欢迎交流。&#x1f339;&#x1f339;&#x1f339; 如有对阁下帮助&#xff0c;请&#x1f44d;点赞&#x1f496;收藏&#x1f431;‍&#x1f3cd;分…

Python 自动化Web测试

限于作者水平有限&#xff0c;以下内容可能是管窥之见&#xff0c;希望大家高抬贵手&#xff0c;且让我斗胆抛砖引玉。 公司产品迪备主要是通过网页操作来进行数据库的备份与恢复&#xff0c;监控与管理&#xff0c;因此在测试的过程中&#xff0c;可以用python测试脚本来模拟…

运维知识点汇总

一.公共基础 linux常用目录 链接一 链接二 linux系统启动 链接一 链接二 LVM 链接一 磁盘挂载 链接一 文件权限 链接一 二.VLAN详解 链接 三.中间件 单体部署&#xff1a; 优点&#xff1a; &#xff08;1&#xff09;小团队成型即可完成开发-测试-上线&am…

3D机器视觉:解锁未来的立体视野

原创 | 文 BFT机器人 机器视觉领域一直在不断演进&#xff0c;从最初的二维图像处理&#xff0c;逐渐扩展到了更复杂的三维领域&#xff0c;形成了3D机器视觉。3D机器视觉技术的涌现为计算机系统带来了全新的感知和理解能力&#xff0c;这一领域的发展正日益受到广泛关注。本文…

Android系统为什么采用Binder作为IPC机制

Android系统提供了多种进程间通信&#xff08;IPC&#xff09;的机制&#xff0c;用于不同进程之间的数据交换和通信。以下是Android系统中常用的几种IPC机制&#xff1a; Intent&#xff1a;Intent是Android系统中常用的一种进程间通信方式。通过发送Intent&#xff0c;可以在…

链表去重Java

去除掉链表中重复的元素,两种方法: static class ListNode{private int val;private ListNode next;public ListNode(int val, ListNode next) {this.val val;this.next next;}Overridepublic String toString() {return "ListNode{" "val" val ",…