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

news/2025/1/2 0:22:08/

八十五、 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.ppmy.cn/news/1143723.html

相关文章

机器学习极值问题

给出二次函数 f ( x ) 1 2 x T P x q T x r f(x) \frac{1}{2}x^TPx q^Tx r f(x)21​xTPxqTxr的极小值点。(P是对称矩阵) 解&#xff1a; 对f(x)求导数&#xff1a; f ( x Δ x ) − f ( x ) 1 / 2 ( x Δ x ) T P ( x Δ x ) q T ( x Δ x ) r − 1 / 2 x T P x…

Transformer预测 | Pytorch实现基于mmTransformer多模态运动预测(堆叠Transformer)

文章目录 文章概述程序设计参考资料文章概述 Transformer预测 | Pytorch实现基于mmTransformer多模态运动预测(堆叠Transformer) 程序设计 Initialize virtual environment: conda create -n mmTrans python=3.7# -*- coding: utf-8 -*- import argparse import os

Qt/C++原创推流工具/支持多种流媒体服务/ZLMediaKit/srs/mediamtx等

一、前言 1.1 功能特点 支持各种本地视频文件和网络视频文件。支持各种网络视频流&#xff0c;网络摄像头&#xff0c;协议包括rtsp、rtmp、http。支持将本地摄像头设备推流&#xff0c;可指定分辨率和帧率等。支持将本地桌面推流&#xff0c;可指定屏幕区域和帧率等。自动启…

【基于STM32OpenCV的车载机器人的抓取控制软件设计】

这里写自定义目录标题 本科优秀毕业论文《基于STM32&OpenCV的车载机器人的抓取控制软件设计》摘要:Abstract:前 言1方案设计与论证2机器人硬件电路设计3机器人软件设计4系统主要功能测试5 结 论参考文献本科优秀毕业论文《基于STM32&OpenCV的车载机器人的抓取控制软件…

关于深拷贝和浅拷贝你需要了解的内容

深拷贝&#xff08;Deep Copy&#xff09;和浅拷贝&#xff08;Shallow Copy&#xff09;是在复制对象或数据结构时使用的两种不同的策略&#xff0c;它们的主要区别在于复制后新旧对象之间的关系以及对嵌套对象的处理方式。 浅拷贝&#xff1a; 浅拷贝创建一个新对象&#xff…

Python视频剪辑-Moviepy视频尺寸和颜色调整技巧

在视频编辑中,尺寸和颜色是两个不能忽视的重要因素。本文将从专业角度深入探讨如何通过MoviePy进行视频尺寸和颜色的调整,以及遮罩透明度的应用。 文章目录 视频尺寸变换函数裁剪视频指定区域裁剪视频像素为偶数视频增加边框缩小、放大视频视频颜色变换函数blackwhite 视频变…

提高工作效率!本地部署Stackedit Markdown编辑器,并实现远程访问

文章目录 1. docker部署Stackedit2. 本地访问3. Linux 安装cpolar4. 配置Stackedit公网访问地址5. 公网远程访问Stackedit6. 固定Stackedit公网地址 StackEdit是一个受欢迎的Markdown编辑器&#xff0c;在GitHub上拥有20.7k Star&#xff01;&#xff0c;它支持将Markdown笔记保…

Java @Override 注解

在代码中&#xff0c;你可能会看到大量的 Override 注解。 这个注解简单来说就是让编译器去读的&#xff0c;能够避免你在写代码的时候犯一些低级的拼写错误。 Java Override 注解用来指定方法重写&#xff08;Override&#xff09;&#xff0c;只能修饰方法并且只能用于方法…