【C++】内存管理与模板

 

目录

一、内存管理

1.new与delete基本用法

(1) 内置类型

(2) 自定义类型

2.new, delete与malloc, free对比

(1) 内置类型

(2) 自定义类型

(3)综合特点

 3.new与delete的底层实现

 4. 定位new表达式

二、模板

1.引入机制

2. 基本使用

(1) 函数模板

①概念:

②格式:

③原理

 ④模板实例化

1)隐式实例化

2)显式实例化

 ⑤模板参数的匹配原则

(2) 类模板

①格式:

②模板实例化

③类模板的声明和定义分离


一、内存管理

C语言中对内存管理主要借助的是malloc,calloc,realloc,free这几个库函数

而C++中进行内存管理借助的是new 与 delete这两个库函数

new是用来动态申请内存空间的,相当于malloc或者calloc

delete是用来手动释放动态申请的内存空间的,相当于free

1.new与delete基本用法

(1) 内置类型

① 申请与释放动态申请单个元素的空间

#include<iostream>
int main()
{//只是开空间int* p1 = new int; //申请一个int大小的空间,返回该空间的起始地址char* p2 = new char;  //申请一个char大小的空间,返回该空间的起始地址//开空间+初始化int* p3 = new int(1); //申请一个int大小的空间,并初始化这块空间为1char* p4 = new char('w'); //申请一个char大小的空间,并初始化这块空间为'w'//销毁动态申请空间delete p1;delete p2;delete p3;delete p4;
}

② 申请与释放连续的空间

#include<iostream>
int main()
{//只是开空间int* p1 = new int[5]; //申请5个int大小的空间,返回该空间的起始地址char* p2 = new char[5];  //申请5个char大小的空间,返回该空间的起始地址//开空间+初始化int* p3 = new int[10]{1, 2, 3, 4, 5}; //申请5个int大小的空间,并初始化为1, 2, 3, 4, 5char* p4 = new char[5]{'a','b','c','d','e'}; //申请5个char大小的空间,并初始化这块空间为'a','b','c','d','e'//销毁动态申请空间delete[] p1;delete[] p2;delete[] p3;delete[] p4;
}

ps: 初始化时, [ ]里面写的是个数, ()里面写的是初始化内容

(2) 自定义类型

① 申请与释放动态申请单个元素的空间

#include<iostream>
class A
{
public:A(int a = 0):_a(a){}
private:int _a;
};
int main()
{//只是开空间A* p1 = new A; //创建1个A类型大小的空间//开空间+初始化A aa1; //A类型创建出了aa1对象A* p2 = new A(aa1); //用aa1对象初始化申请的一个A类型大小的空间A* p3 = new A(A()); //匿名对象初始化申请的一个A类型大小的空间(用缺省值)A* p3 = new A(A(1)); //匿名对象初始化申请的一个A类型大小的空间(传实参)A* p4 = new A(1); //1隐式类型转换成A类型的数据去初始化一个A类型大小空间//销毁动态内存空间delete p1;delete p2;delete p3;delete p4;
}

② 申请与释放连续的空间

#include<iostream>
class A
{
public:A(int a = 0):_a(a){}
private:int _a;
};
int main()
{//只是开空间A* p1 = new A[2]; //创建2个A类型大小的空间//开空间+初始化A aa1; A aa2;A* p2 = new A[2]{ aa1,aa2 }; //用aa1,aa2对象初始化申请的2个A类型大小的空间A* p3 = new A[2]{A(1),A(2)}; //两个匿名对象初始化申请的2个A类型大小的空间A* p4 = new A[2]{1, 2}; //1, 2隐式类型转换成两个A类型的数据去初始化2个A类型大小空间//销毁动态内存空间delete p1;delete p2;delete p3;delete p4;
}

2.new, delete与malloc, free对比

(1) 内置类型

#include<iostream>
int main()
{//开辟5个int大小的空间int* p1 = (int*)malloc(sizeof(int) * 5);int* p2 =  new int[5];free(p1);delete p2;
}

①new与malloc,delete与free 功能上没有实质差异

②new比malloc更简洁: 

1). malloc需要用计算单个数据类型大小, 写进表达式,new不需要

2). malloc需要对返回值做强制类型转换,new不需要

③new在开空间的时候可以手动初始化,malloc不可以,即使是calloc也只是自动初始化成0

(2) 自定义类型

①new在申请空间时会调用构造函数,malloc不会,因此new可以在申请空间同时完成初始化, malloc无法初始化

②delete在释放空间时会调用析构函数,free不会,因此delet可以在释放空间同时可以完成对对象中资源的清理, 而free无法完成

#include<iostream>
using namespace std;
class A
{
public:A(int a = 0):_a(a){cout << "A(int a = 0)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
int main()
{A* p1 = (A*)malloc(sizeof(A));free(p1);cout << "-----------" << endl;A* p2 = new A;delete p2;
}

ps: 对于动态申请的连续空间,new和delete会多次调用构造函数和析构函数

(3)综合特点

①malloc和free是函数,new和delete是操作符

②malloc开辟空间失败,会返回空指针,因此我们在使用malloc时需要判断返回值是否为空,而new不需要做检查,new开辟失败的话会直接抛异常

 

 3.new与delete的底层实现

new = 开辟空间 + 调用构造函数

delete = 调用析构函数 + 释放空间

而库中开辟空间和释放空间的实现是借助两个全局函数operator new 与 operator delete 完成的

而operator new本质是对 malloc的封装,不过是增加了一些机制,使得开辟失败能报异常

而operator delete本质是对 free 的直接封装

因此,operator new 和operaotor delete 使用起来和 malloc 与 free 是完全一样的

int main()
{int* p1 = (int*)operator new(sizeof(int) * 10);operator delete(p1);
}

 4. 定位new表达式

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象

格式: new (place_address) type(initializer-list), place_address必须是一个指针, initializer-list是参数列表,  如果构造函数需要传参,则必须传参

#include<iostream>
using namespace std;
class A
{
public:A(int a = 0):_a(a){cout << "A(int a = 0)" << endl;}~A(){cout << "~A" << endl;}
private:int _a;
};
int main()
{A* p1 = (A*)operator new(sizeof(A) * 10);//显式调用构造函数new(p1)A(10);//显式调用析构函数p1->~A();operator delete(p1);
}

有些场景下会出现内存空间分配了,但是没有初始化的场景(内存池),这时就需要用定位new表达式对内存进行初始化

二、模板

1.引入机制

两数交换,是我们经常碰到的一个需求,因此我们经常会把两数交换逻辑封装成一个函数

void Swap(int& left, int& right)
{int tmp = left;left = right;right = tmp;
}

但是我要交换的数据不是整形呢?是其他类型呢?再写一份太麻烦了,那我们typedef一下~

typedef int DataType;
void Swap(DataType& left, DataType& right)
{DataType tmp = left;left = right;right = tmp;
}

这时我们只需要把int改成其他类型就行了,但是我如果想同时交换整形数据和其他类型数据呢?就算typedef也只能调用函数去交换固定类型的数据呀,于是还是得有多份逻辑相同的交换函数~

void Swap(int& left, int& right)
{int tmp = left;left = right;right = tmp;
}
void Swap(double& left, double& right)
{double tmp = left;left = right;right = tmp;
}
void Swap(char& left, char& right)
{char tmp = left;left = right;right = tmp;
}

还是太麻烦了,因此C++引入了模板,模板如同现实中的模板, 比如说数学书把数学公式给你了,要根据数学公式去计算具体的题目,你只需要把公式中的符号替换成具体数字就行了~

C++中的模板就是给了编译器一个模子,让编译器根据不同的类型利用该模子生成代码

2. 基本使用

模板分为函数模板与类模板

(1) 函数模板

①概念:

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参化,根据实参类型产生特定的类型模板

②格式:

方式1) 关键字template <class T1,class T2,  class T3 ··· >

方式2) 关键字template <typename T1,  typename T2, typename T3···>

其中T1, T2, T3都是模板参数

template<class T>
void Swap(T& left, T& right)
{T tmp = left;left = right;right = tmp;
}

③原理

模板本身并不是函数,只是一个模子,具体使用时编译器会根据实参类型将模板推演成函数,去之执行相关代码,因此之前需要我们做的事情交给编译器去完成了

 ④模板实例化

用不同类型参数使用模板称为模板的实例化(对比类的实例化---通过类创建出具体对象)

1)隐式实例化

编译器自动根据实参推演模板参数的类型

template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
int main()
{//以下代码均为隐式实例化Add(1, 2); Add(1.1, 2.2);Add(1.1, 2);//(×) 传递参数不一致时,会报错,因为T也不知道该实例化成哪种类型//强制类型转化同一类型Add((int)1.1, 2);Add(1.1, (double)2);
}
2)显式实例化

在函数名后的<>中手动指定模板参数的实际类型

template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
int main()
{//显式实例化Add<int>(1, 2.2);Add<double>(1, 2.2);
}

 ⑤模板参数的匹配原则

1) 一个非模板函数可以和同名函数模板同时存在,且函数模板还可以被实例化成这个非模板函数

#include<iostream>
using namespace std;
//非模板函数
void Swap(int& left, int& right)
{int tmp = left;left = right;right = tmp;
}
//函数模板
template<class T>
void Swap(T& left, T& right)
{T tmp = left;left = right;right = tmp;
}
int main()
{int a = 1, b = 2;Swap<int>(a, b); //函数模板会实例化成上面的非模板函数
}

2)对于非模板函数和同名函数模板,如果其他条件相同,在调用函数时会优先调用非模板函数;如果模板可以产生一个更好匹配的函数,那么选择模板

#include<iostream>
using namespace std;
//函数模板
template <class T1, class T2>
T1 Add(T1 left, T2 right)
{cout << "函数模板" << endl;return left + right;
}
//非模板函数
int Add(int left, int right)
{cout << "非模板函数" << endl;return left + right;
}
int main()
{Add(1, 2); //调用非模板函数cout << "------------" << endl;Add(1.1, 2); //函数模板实例化
}

3)模板函数不允许自动类型转化,但普通函数可以进行自动类型转换

void func(int a) 
{cout << a;
}
int main()
{func(1.1);//自动进行类型转换
}

template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
int main()
{Add(1.1, 2);//(×) 1.1不会自动转换成整形, 2也不会自动转换成浮点型
}

(2) 类模板

①格式:

与类函数是一样的,template <class T1, class T2, class T3···>

②模板实例化

类模板实例化需要在类模板名后面加上<>,将实例化类型写在<>即可

类模板名字不是真正的类,而实例化的结果才是真正的类

template<class T>
class Vector
{
public:Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}size_t Size() { return _size; }T& operator[](size_t pos){assert(pos < _size);return _pData[pos];}
private:T* _pData;size_t _size;size_t _capacity;
};
int main()
{//Vector是类名, Vector<int>才是类型Vector<int> v1;Vector<double> v2;
}

③类模板的声明和定义分离

template<class T>
class Vector
{
public:Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}// 使用析构函数演示:在类中声明,在类外定义。~Vector();void PushBack(const T& data);void PopBack();// ...size_t Size() { return _size; }T& operator[](size_t pos){//assert(pos < _size);return _pData[pos];}private:T* _pData;size_t _size;size_t _capacity;
};
//析构函数的定义
template<class T>
Vector<T>::~Vector()
{delete[] _pData;_pData = nullptr;
}
int main()
{Vector<int> v;v.PushBack(1);v.PushBack(1);v.PushBack(1);v.PushBack(1);return 0;
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

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

相关文章

Hadoop+Python+Django+Mysql热门旅游景点数据分析系统的设计与实现(包含设计报告)

系统阐述的是使用热门旅游景点数据分析系统的设计与实现&#xff0c;对于Python、B/S结构、MySql进行了较为深入的学习与应用。主要针对系统的设计&#xff0c;描述&#xff0c;实现和分析与测试方面来表明开发的过程。开发中使用了 django框架和MySql数据库技术搭建系统的整体…

06-1_Qt 5.9 C++开发指南_对话框与多窗体设计_标准对话框

在一个完整的应用程序设计中&#xff0c;不可避免地会涉及多个窗体、对话框的设计和调用&#xff0c;如何设计和调用这些对话框和窗体是搞清楚一个庞大的应用程序设计的基础。本章将介绍对话框和多窗体设计、调用方式、数据传递等问题&#xff0c;主要包括以下几点。 Qt 提供的…

OffSec Labs Proving grounds Play——FunboxEasyEnum

文章目录 端口扫描目录扫描文件上传漏洞利用查看用户爆破密码sudo提权flag位置FunboxEasyEnum writeup walkthrough Funbox: EasyEnum ~ VulnHub Enumeration Brute-force the web server’s files and directories. Be sure to check for common file extensions. Remote…

Hadoop理论及实践-HDFS四大组件关系(参考Hadoop官网)

NameNode&#xff08;名称节点&#xff0c;Master主节点&#xff09; NameNode主要功能 1、NameNode负责管理HDFS文件系统的元数据&#xff0c;包括文件&#xff0c;目录&#xff0c;块信息等。它将元数据Fsimage与Edit_log持久化到硬盘上。一个是Fsimage(镜像文件&#xff09…

android,Compose,消息列表和动画(点击item的时候,就会删除)

Compose,消息列表和动画&#xff08;点击item的时候&#xff0c;就会删除&#xff09; package com.example.mycompose08import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundat…

PoseiSwap 开启“Poseidon”池,治理体系或将全面开启

PoseiSwap 曾在前不久分别以 IDO、IEO 的方式推出了 POSE 通证&#xff0c;但 PoseiSwap DEX 中并未向除 Zepoch 节点外的角色开放 POSE 资产的交易。而在前不久&#xff0c;PoseiSwap 推出了全新的“Poseidon”池&#xff0c;该池将向所有用户开放&#xff0c;并允许用户自由的…

Git:在本地电脑上如何使用git?

git 版本&#xff1a; 2.40.1.windows.1 文章目录 一. 使用git之前你必须要理解的几个概念1.1 理解工作区、版本库、暂存区的概念1.2 提交Git版本库的步骤【分两步执行】 二. Git本地库实战2.1 初始化版本库2.2 新建 & 提交 & 状态2.3 查看日志2.4 回退 & 穿梭 &am…

Codeforces Round 892 (Div. 2) C. Another Permutation Problem 纯数学方法 思维题

Codeforces Round 892 (Div. 2) C. Another Permutation Problem 源码&#xff1a; #include <iostream> #include <algorithm> #include <set> #include <map> #include <queue> #include <vector> #include <stack> #include &l…

面试热题(螺旋矩阵)

给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素 一看到这个大家有没有想到 就是一个螺旋形状&#xff0c;那这道题我们应该怎么解决&#xff1f; 我们先来仔细的看&#xff0c;它这种螺旋形状的遍历是先【右-下-左-上】…

aardio 调用 python pickle load 数据

aardio 调用 python pickle load 词典数据&#xff1b; pip install readmdict dump_pickle.py import os import sys import time import pickle from readmdict import MDX, MDDos.chdir("/mdict")mdxfile "your.mdx" if not os.path.exists(mdxfil…

Kotlin和Java互操作时的可空性

注&#xff1a;文中demo的kt版本是1.7.10 一、kotlin语言中的可空性设计 在Java语言中的NPE&#xff08;NullPointerException&#xff09;可以说非常常见&#xff0c;而且诟病已久。 kotlin做为后起之秀&#xff0c;在空指针的问题上进行了升级&#xff0c;即&#xff1…

数据结构-带头双向循环链表的实现

前言 带头双向循环链表是一种重要的数据结构&#xff0c;它的结构是很完美的&#xff0c;它弥补了单链表的许多不足&#xff0c;让我们一起来了解一下它是如何实现的吧&#xff01; 1.节点的结构 它的节点中存储着数据和两个指针&#xff0c;一个指针_prev用来记录前一个节点…

微服务监控技术skywalking的部署与使用(亲测无坑)

微服务监控技术skywalking的部署与使用 1. 前期准备2. skywalking安装部署2.1 Java Agent2.2 apache/skywalking-oap-server2.3 apache/skywalking-ui 3. 项目启动4.效果展示 1. 前期准备 注&#xff1a;本篇文章采用docker部署&#xff0c;采用8.2.0版本&#xff0c;版本一定…

【Nginx】Nginx负载均衡

负载均衡&#xff1a;通过反向代理来实现 Nginx的七层代理和四层代理&#xff1a; 七层是最常用的反向代理方式&#xff0c;只能配置在nginx配置文件的http模块当中 &#xff1b;配置的方法名称为&#xff1a;upstream模块&#xff0c;不能写在server中也不能写在location中&a…

FPGA实践 ——Verilog基本实验步骤演示

0x00 回顾&#xff1a;AND/OR/NOT 逻辑的特性 AND&#xff1a;与门可以具有两个或更多的输入&#xff0c;并返回一个输出。当所有输入值都为 1 时&#xff0c;输出值为 1。如果输入值中有任何一个为 0&#xff0c;则输出值为 0。 OR&#xff1a;或门可以具有两个或更多的输入…

湘大 XTU OJ 1290 Alice and Bob 题解(非常详细):字符串 分类讨论 简单模拟

一、链接 1290 Alice and Bob 二、题目 题目描述 Alice和Bob玩剪刀-石头-布的游戏&#xff0c;请你写个程序判断一下比赛的结果。 输入 第一行是一个整数K&#xff0c;表示样例的个数。 以后每行两个单词&#xff0c;rock表示石头&#xff0c;paper表示布&#xff0c;scis…

山东布谷科技直播程序源码使用Redis进行服务器横向扩展

当今&#xff0c;直播程序源码平台作为新媒体时代主流&#xff0c;受到了世界各地人民的喜爱&#xff0c;这也使得直播程序源码平台用户数量的庞大&#xff0c;也难免会出现大量用户同时访问服务器&#xff0c;使服务器过载的情况&#xff0c;当服务器承受不住的时候&#xff0…

Win11中使用pip或者Cython报错 —— error: Microsoft Visual C++ 14.0 is required.

第一步&#xff1a;下载Visual Studio 2019 下载地址&#xff1a; https://learn.microsoft.com/zh-cn/visualstudio/releases/2019/release-notes 第二步&#xff1a;安装组件 选择单个组件&#xff0c;勾选以下两个组件 其他错误&#xff1a; 无法打开文件“python37.li…

VMware虚拟机NAT模式Ubuntu无法上网解决方案

发现只要NAT模式&#xff0c;ping地址时就报网络不可达&#xff0c;且右上方网络图标消失&#xff0c;但是外部USB网络设备又只能在NAT模式下使用。。。 博主的解决方案如下&#xff1a; 按WinR键入services.msc&#xff0c; 找到VMware DHCP Service、VMware NAT Service和V…

Unity数字可视化学校_昼夜(三)

1、删除不需要的 UI using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;public class EnvControl : MonoBehaviour {//UIprivate Button btnTime;private Text txtTime; //材质public List<Material> matListnew Li…