目录

前言

1.为什么C++要引入new/delete?

2.operator new与operator delete函数

它们的实际作用

Placement New(定位new表达式)

总结



前言

在写上一篇博客“vector的模拟实现”时,我一直很好奇vector的private成员为什么要用三个封装后的迭代器指针,来替代原本数据结构顺序表中的成员变量,因为原本的数据结构成员是完全满足需求的,即:

(从数据结构的本质来讲,vector完全可以被理解为 C++ 版的、功能更强大的、高度自动化的顺序表。)

在学习了实际库中的vector代码后,我发现了未见过的东西:"::operator new与::operator delete"。在知道了它们的作用与使用场所后,或许我找到了用迭代器指针替代原本数据成员的原因。


1.为什么C++要引入new/delete?

C语言内存管理方式在C++中是可以继续使用,比如malloc、realloc等函数。但有些地方这些传统的空间申请函数就显得有些无能为力,而且使用起来比较麻烦。

是的,在C++的自定类型数据中,常需要在定义时顺便调用构造函数初始化。而原C语言的空间申请函数是没有这种功能,需要额外操作,于是C++提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

简而言之,什么C++要引入new/delete的原因在于:

new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间时,还会调用构造函数和析构函数。


2.operator new与operator delete函数

首先介绍它们是什么:

它们是C++中的内存分配原语函数它们只负责分配和释放原始内存,不涉及对象的构造和析构。

它们的用法:

#include <new> // 包含 operator new 和 operator delete 的声明// 分配内存
void* memory = ::operator new(size_t bytes);// 释放内存
::operator delete(void* ptr);

示例

#include <iostream>
#include <new>int main() 
{// 分配10个int大小的内存void* int_memory = ::operator new(10 * sizeof(int));std::cout << "内存分配成功,地址: " << int_memory << std::endl;// 使用内存...// 释放内存::operator delete(int_memory );std::cout << "内存已释放" << std::endl;return 0;
}

它们与new/delete的关系:

new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过 operator delete全局函数来释放空间。

操作做的事情
int* p = new int(42);1. 调用 operator new(sizeof(int)) 分配内存
2. 在内存上调用 int 的构造函数 构造对象(设为42)
delete p;1. 调用 p 的析构函数 析构对象
2. 调用 operator delete(p) 释放内存
void* mem = ::operator new(sizeof(int));只分配内存,不调用构造函数
::operator delete(mem);只释放内存,不调用析构函数

简单说:new/delete =  (operator new/operator delete + 构造函数/析构函数调用)

注意:和new与delete,new[ ]与delete[ ]相同,operator new只能与operator delete匹配。

它们的实际作用

回到前言部分,为什么vector的private成员为什么要用三个封装后的迭代器指针,来替代原本数据结构顺序表中的成员变量呢?

在查看vecotr的实际实现代码中,我发现 vector类中涉及空间增删的函数,实际上都是调用的reserve函数,而在reserve函数中则使用了operator new/delete。

我们来看看reserve函数的模拟实现:

		void reserve(size_t n){size_t oldSize = size(), oldCapa = capacity();if (n > oldCapa){size_t newcapa = oldCapa == 0 ? 16 : oldCapa * 2;while (newcapa < n)newcapa *= 2;iterator newVec =(iterator)::operator new(newcapa * sizeof(T));if (_start){for (int i = 0; i < oldSize; ++i)new(newVec + i)T(_start[i]);for (int i = 0; i < oldSize; ++i)_start[i].~T();}::operator delete(_start);_start = newVec;_finish = _start + oldSize;_end = _start + newcapa;}else return;}

讨论:在普通的reserve函数中,我们通常用new来申请空间,如下所示:

T* newcapa=new T[n];

可这带来一个问题:new在申请空间的同时会调用构造函数,可是这些空间我们真的需要全部初始化吗,换句话说这些空间我们真能全部用完吗?显然大部分场景是用不完的,因为vector的空间一般呈*2倍速度增长。那么这些不用的空间通过调用构造函数初始化,这不仅造成了一定的性能浪费,还让后续无法自定义使用这片内存,造成内存资源浪费。

于是,通过用operator new/delete替换之前的new/delete,既解决了性能损失,又满足了C++内存分配与对象构造分离的目的。

或许有读者注意到上述代码中的如下这段代码,这段代码实则揭开了为什么vector要使用三个指针作为成员变量的原因:

for (int i = 0; i < oldSize; ++i)new(newVec + i)T(_start[i]);

已知C++程序的一个核心设计思想:将内存分配(Allocation)和对象构造(Construction)分离。通过使用operator new/delete确实做到了只分配空间,不调用构造函数的目的。那么什么时候调用构造函数呢?

——在reserve函数中,通过提前记录的oldSize精确控制调用构造函数的次数。而实际调用构造函数是通过Placement New(定位new表达式)实现的。

Placement New(定位new表达式)

它是什么?

Placement new 是一种特殊的 new 表达式,它在已分配的内存上构造对象。它不分配内存,只调用构造函数。

语法

new (address) Type(constructor_arguments);

address:一般传入指针;

Type:某数据类型,可以是内置类型,也可以是自定义类型;constructor_arguments,该类型的构造函数。

使用示例

#include <iostream>
#include <new>class MyClass {
public:int value;MyClass(int v) : value(v) {std::cout << "构造函数被调用,value = " << value << std::endl;}~MyClass() {std::cout << "析构函数被调用,value = " << value << std::endl;}
};int main() {// 1. 只分配内存,不构造对象void* memory = ::operator new(sizeof(MyClass));// 2. 在已分配的内存上构造对象MyClass* obj = new (memory) MyClass(42);std::cout << "对象值: " << obj->value << std::endl;// 3. 显式调用析构函数obj->~MyClass();// 4. 释放内存::operator delete(memory);return 0;
}

operator new 设计时就考虑了与 placement new 的配合使用。这种组合提供了对对象构造和内存分配的完全控制。

现在回到上述有关reserve函数的讨论。

现在已知vector中有关的空间操作,全是由reserve函数完成的,其中reserve函数通过使用operator new/delete、定位new表达式完成了“内存分配和对象构造的分离”。

结合定位new表达式的使用语法,有关“vector的private成员为什么要用三个封装后的迭代器指针,来替代原本数据结构顺序表中的成员变量”的答案也就呼之欲出了,或许原因之一就是为了满足位new表达式的使用语法从而达到“内存分配和对象构造的分离”的目的。

同样是申请空间,为什么不使用原C语言malloc等函数,而设计出operator new函数呢?

可能的原因或许有很多,但作者认为或许与它们在面对异常时的反应不同:

operator new 的异常行为

  • 当 operator new 无法分配内存时,它会抛出 std::bad_alloc 异常,提醒程序员。

  • 这与 C++ 的异常处理机制完美集成。

malloc 的错误处理

  • 当 malloc 无法分配内存时,它返回 NULL(或 C++11 中的 nullptr),需要程序员自己检查。

  • 这要求你检查返回值,使用 C 风格的错误处理。


总结

本文从对“vector的private成员为什么要用三个封装后的迭代器指针,来替代原本数据结构顺序表中的成员变量”疑问中,引出operator new/delete的介绍,以及之后定位表达式new的使用语法。

本文或许对vector为什么要用三个指针,替换原本的使用一个指针加两个size_t(data, size, capacity)的回答不尽完美,甚至漏洞百出,但好在因此学到了新东西。

感谢你的阅读。

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

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

相关文章

Java中Integer转String

在 Java 中&#xff0c;将 Integer 转换为 String 有多种方法&#xff0c;以下是常见的几种方式&#xff1a;1. 使用 Integer.toString() 方法javaInteger num 123; String str Integer.toString(num); // 直接调用 Integer 的静态方法2. 使用 String.valueOf()javaInteger n…

智能装备如何与软件结合?

一、什么是智能装备&#xff1f; 智能装备是具备“感知-决策-执行-自适应”闭环能力的智能化系统&#xff0c;本质是“传统物理装备”与“数字智能”的深度融合。它不仅能完成预设动作&#xff08;如传统机械臂焊接&#xff09;&#xff0c;还能通过传感器“观察”环境、用算法…

react性能优化有哪些

React 性能优化的手段比较多&#xff0c;既有代码层面的&#xff0c;也有构建层面的&#xff0c;还涉及到运行时调优。我帮你系统性梳理一份&#xff1a;&#x1f539; 一、渲染性能优化1. 减少不必要的渲染React.memo&#xff1a;对函数组件做浅比较&#xff0c;避免相同 prop…

腾讯云OpenCloudOS 9系统部署OpenTenBase数据库详细教程

OpenTenBase简介OpenTenBase是一个关系型数据库集群平台&#xff0c;提供写入可靠性和多节点数据同步功能。可以在一台或多台主机上配置OpenTenBase&#xff0c;并将数据存储在多个物理主机上。OpenTenBase架构组件&#xff1a;Coordinator Node (CN)&#xff1a;应用程序访问入…

【计算机视觉】Pixel逐像素分类Mask掩码分类理解摘要

目标检测和实例分割是计算机视觉的基本任务。目标检测的传统方法中通常利用边界框技术进行对象定位&#xff0c;然后利用逐像素分类为这些本地化实例分配类。但是当处理同一类的重叠对象时&#xff0c;或者在每个图像的对象数量不同的情况下&#xff0c;这些方法通常会出现问题…

C++之stack类的代码及其逻辑详解

1. stack介绍及使用方法stack是一种后进先出的数据结构&#xff0c;所以在C的STL库中也同样遵循了这一点&#xff0c;我们在使用的时候不支持随机访问或迭代器遍历。注意事项调用 top() 或 pop() 前需确保栈非空&#xff0c;否则可能引发未定义行为。stack 没有 clear() 函数&a…

Spring Cache实现简化缓存功能开发

一. 介绍Spring Cache 是 Spring 框架提供的缓存抽象层&#xff0c;它简化了在应用中添加缓存功能的开发工作。通过 Spring Cache&#xff0c;开发者无需关注具体缓存实现的细节&#xff0c;只需通过注解就能快速实现方法级别的缓存管理。核心特点1. 与具体缓存实现解耦&#x…

Lombok(简化Java当中的开发)

Lombok概述 以前的Java项目中,充斥着太多不友好的代码:POJO的getter/setter/toString/构造方法;打印日志;I/O流的关闭操作等等,这些代码既没有技术含量,又影响着代码的美观,Lombok应运而生。 LomBok可以通过注解,帮助开发人员消除JAVA中尤其是POJO类中的冗长代码。 使…

【DeepSeek】公司内网部署离线deepseek+docker+ragflow本地模型实战

企业内部可能有些数据比较敏感&#xff0c;不能连接互联网。本次实验操作是将deepseek完全离线后迁移至内网使用&#xff0c;实验基于Windows server 2022 datacenter系统安装deepseek、docker、ragflow。 目录使用VMware新建WIN2022虚拟机一、安装DeepSeek模型二.安装Docker使…

【软考架构】面向服务的体系结构(SOA)深度解析

面向服务的体系结构&#xff08;SOA&#xff09;深度解析 面向服务的体系结构&#xff08;Service-Oriented Architecture, SOA&#xff09;是一种以服务为核心的软件架构范式&#xff0c;通过标准化接口实现异构系统间的高效集成与协作。以下从概念定义、发展脉络、技术演进、…

centos7中MySQL 5.7.32 到 5.7.44 升级指南:基于官方二进制包的原地替换式升级

目录前言1. 升级概述1.1 升级背景1.2 升级目的1.3 升级方法概述1.4 升级策略与注意事项2. 升级准备2.1 备份工作2.2 下载目标版本2.3 停止 MySQL 服务3. 替换二进制文件3.1 解压官方二进制包3.2 替换核心二进制文件3.3 更新共享库4. 执行升级并验证4.1 启动 MySQL 服务4.2 监控…

数学七夕花礼(MATLAB版)

前言参考的视频在抖音&#xff0c;电脑版的抖音一直登录不了&#xff0c;用手机分享的链接如下所示。4.35 Iv.FH yTl:/ 04/04 复制打开抖音&#x1f440;数学送的七夕花礼&#xff0c;记得查收噢.# 七夕花礼请查收 ... https://v.douyin.com/H-YpOJCyQyg/rho4sin(8theta)公式&a…

LeetCode - 21. 合并两个有序链表

题目 21. 合并两个有序链表 思路 我会采用双指针的方法&#xff0c;同时遍历两个链表&#xff0c;比较当前节点的值&#xff0c;将较小的节点添加到结果链表中。 具体思路是这样的&#xff1a; 首先创建一个哑节点(dummy node)作为合并后链表的头部&#xff0c;这样可以简…

ES01-环境安装

ES01-环境安装 文章目录ES01-环境安装1-参考网址2-知识总结1-参考网址 elasticsearch官网地址&#xff1a;https://www.elastic.co/安装elasticsearch9.0.0参考&#xff1a;https://zhuanlan.zhihu.com/p/1920780524991017021安装elasticsearch9.0.0参考&#xff1a;http://ww…

UI前端大数据可视化实战策略:如何设计符合用户认知的数据可视化界面?

hello宝子们...我们是艾斯视觉擅长ui设计、前端开发、数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩!UI前端大数据可视化实战策略&#xff1a;如何设计符合用户认知的数据可视化界面&#xff1f;数…

学习python第15天

其实前面学的根本不记得了&#xff0c;小丑.jpg&#xff0c;如果真的面试问到了估计也是一脸懵今日任务&#xff1a;JSON先认识一下JSON和JSONL文件记得之前在面试KIMI的时候&#xff0c;面试官就给我出了JSONL和EXCEL转换的手撕代码题&#xff0c;而那个时候&#xff0c;我连什…

Spring框架集成Kakfa的方式

Spring框架集成Kakfa的方式 springboot集成kafka的方式 添加maven依赖 <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.0</version> </dependency&g…

【蓝桥杯 2024 省 Python B】缴纳过路费

【蓝桥杯 2024 省 Python B】缴纳过路费 蓝桥杯专栏&#xff1a;2024 省 Python B 算法竞赛&#xff1a;图论&#xff0c;生成树&#xff0c;并查集&#xff0c;组合计数&#xff0c;kruskal 最小生成树&#xff0c;乘法原理 题目链接&#xff1a;洛谷 【蓝桥杯 2024 省 Python…

个性化导航新体验:cpolar让Dashy支持语音控制

文章目录简介1. 安装Dashy2. 安装cpolar3.配置公网访问地址4. 固定域名访问用 cpolar 让 Dashy 管理个人导航站就是这么简单&#xff01;三步轻松搞定&#xff1a;在电脑上安装 Dashy&#xff0c;拖拽添加常用网站&#xff0c;运行 cpolar 生成远程访问链接。这个方法不仅免费&…

SQL学习记录

基本的&#xff0c;增、删&#xff0c;改insert into table_name (列1, 列2,...) VALUES (值1, 值2,....)Delete from 表 where keyvalueupdate 表 set keyvalue,keyvalue where keyvalue查用的最多whereSELECT prod_name, prod_price FROM Products WHERE vend idDLLO1OR ve…